Diff: STRATO-apps/wordpress_03/app/wp-includes/class-wp-oembed.php
Keine Baseline-Datei – Diff nur gegen leer.
1
-
1
+
<?php
2
+
/**
3
+
* API for fetching the HTML to embed remote content based on a provided URL
4
+
*
5
+
* Used internally by the WP_Embed class, but is designed to be generic.
6
+
*
7
+
* @link https://developer.wordpress.org/advanced-administration/wordpress/oembed/
8
+
* @link http://oembed.com/
9
+
*
10
+
* @package WordPress
11
+
* @subpackage oEmbed
12
+
*/
13
+
14
+
/**
15
+
* Core class used to implement oEmbed functionality.
16
+
*
17
+
* @since 2.9.0
18
+
*/
19
+
#[AllowDynamicProperties]
20
+
class WP_oEmbed {
21
+
22
+
/**
23
+
* A list of oEmbed providers.
24
+
*
25
+
* @since 2.9.0
26
+
* @var array
27
+
*/
28
+
public $providers = array();
29
+
30
+
/**
31
+
* A list of an early oEmbed providers.
32
+
*
33
+
* @since 4.0.0
34
+
* @var array
35
+
*/
36
+
public static $early_providers = array();
37
+
38
+
/**
39
+
* A list of private/protected methods, used for backward compatibility.
40
+
*
41
+
* @since 4.2.0
42
+
* @var array
43
+
*/
44
+
private $compat_methods = array( '_fetch_with_format', '_parse_json', '_parse_xml', '_parse_xml_body' );
45
+
46
+
/**
47
+
* Constructor.
48
+
*
49
+
* @since 2.9.0
50
+
*/
51
+
public function __construct() {
52
+
$host = urlencode( home_url() );
53
+
$providers = array(
54
+
'#https?://((m|www)\.)?youtube\.com/watch.*#i' => array( 'https://www.youtube.com/oembed', true ),
55
+
'#https?://((m|www)\.)?youtube\.com/playlist.*#i' => array( 'https://www.youtube.com/oembed', true ),
56
+
'#https?://((m|www)\.)?youtube\.com/shorts/*#i' => array( 'https://www.youtube.com/oembed', true ),
57
+
'#https?://((m|www)\.)?youtube\.com/live/*#i' => array( 'https://www.youtube.com/oembed', true ),
58
+
'#https?://youtu\.be/.*#i' => array( 'https://www.youtube.com/oembed', true ),
59
+
'#https?://(.+\.)?vimeo\.com/.*#i' => array( 'https://vimeo.com/api/oembed.{format}', true ),
60
+
'#https?://(www\.)?dailymotion\.com/.*#i' => array( 'https://www.dailymotion.com/services/oembed', true ),
61
+
'#https?://dai\.ly/.*#i' => array( 'https://www.dailymotion.com/services/oembed', true ),
62
+
'#https?://(www\.)?flickr\.com/.*#i' => array( 'https://www.flickr.com/services/oembed/', true ),
63
+
'#https?://flic\.kr/.*#i' => array( 'https://www.flickr.com/services/oembed/', true ),
64
+
'#https?://(.+\.)?smugmug\.com/.*#i' => array( 'https://api.smugmug.com/services/oembed/', true ),
65
+
'#https?://(www\.)?scribd\.com/(doc|document)/.*#i' => array( 'https://www.scribd.com/services/oembed', true ),
66
+
'#https?://wordpress\.tv/.*#i' => array( 'https://wordpress.tv/oembed/', true ),
67
+
'#https?://(.+\.)?crowdsignal\.net/.*#i' => array( 'https://api.crowdsignal.com/oembed', true ),
68
+
'#https?://(.+\.)?polldaddy\.com/.*#i' => array( 'https://api.crowdsignal.com/oembed', true ),
69
+
'#https?://poll\.fm/.*#i' => array( 'https://api.crowdsignal.com/oembed', true ),
70
+
'#https?://(.+\.)?survey\.fm/.*#i' => array( 'https://api.crowdsignal.com/oembed', true ),
71
+
'#https?://(www\.)?twitter\.com/\w{1,15}/status(es)?/.*#i' => array( 'https://publish.twitter.com/oembed', true ),
72
+
'#https?://(www\.)?twitter\.com/\w{1,15}$#i' => array( 'https://publish.twitter.com/oembed', true ),
73
+
'#https?://(www\.)?twitter\.com/\w{1,15}/likes$#i' => array( 'https://publish.twitter.com/oembed', true ),
74
+
'#https?://(www\.)?twitter\.com/\w{1,15}/lists/.*#i' => array( 'https://publish.twitter.com/oembed', true ),
75
+
'#https?://(www\.)?twitter\.com/\w{1,15}/timelines/.*#i' => array( 'https://publish.twitter.com/oembed', true ),
76
+
'#https?://(www\.)?twitter\.com/i/moments/.*#i' => array( 'https://publish.twitter.com/oembed', true ),
77
+
'#https?://(www\.)?soundcloud\.com/.*#i' => array( 'https://soundcloud.com/oembed', true ),
78
+
'#https?://(open|play)\.spotify\.com/.*#i' => array( 'https://embed.spotify.com/oembed/', true ),
79
+
'#https?://(.+\.)?imgur\.com/.*#i' => array( 'https://api.imgur.com/oembed', true ),
80
+
'#https?://(www\.)?issuu\.com/.+/docs/.+#i' => array( 'https://issuu.com/oembed_wp', true ),
81
+
'#https?://(www\.)?mixcloud\.com/.*#i' => array( 'https://app.mixcloud.com/oembed/', true ),
82
+
'#https?://(www\.|embed\.)?ted\.com/talks/.*#i' => array( 'https://www.ted.com/services/v1/oembed.{format}', true ),
83
+
'#https?://(www\.)?(animoto|video214)\.com/play/.*#i' => array( 'https://animoto.com/oembeds/create', true ),
84
+
'#https?://(.+)\.tumblr\.com/.*#i' => array( 'https://www.tumblr.com/oembed/1.0', true ),
85
+
'#https?://(www\.)?kickstarter\.com/projects/.*#i' => array( 'https://www.kickstarter.com/services/oembed', true ),
86
+
'#https?://kck\.st/.*#i' => array( 'https://www.kickstarter.com/services/oembed', true ),
87
+
'#https?://cloudup\.com/.*#i' => array( 'https://cloudup.com/oembed', true ),
88
+
'#https?://(www\.)?reverbnation\.com/.*#i' => array( 'https://www.reverbnation.com/oembed', true ),
89
+
'#https?://videopress\.com/v/.*#' => array( 'https://public-api.wordpress.com/oembed/?for=' . $host, true ),
90
+
'#https?://(www\.)?reddit\.com/r/[^/]+/comments/.*#i' => array( 'https://www.reddit.com/oembed', true ),
91
+
'#https?://(www\.)?speakerdeck\.com/.*#i' => array( 'https://speakerdeck.com/oembed.{format}', true ),
92
+
'#https?://([a-z0-9-]+\.)?amazon\.(com|com\.mx|com\.br|ca)/.*#i' => array( 'https://read.amazon.com/kp/api/oembed', true ),
93
+
'#https?://([a-z0-9-]+\.)?amazon\.(co\.uk|de|fr|it|es|in|nl|ru)/.*#i' => array( 'https://read.amazon.co.uk/kp/api/oembed', true ),
94
+
'#https?://([a-z0-9-]+\.)?amazon\.(co\.jp|com\.au)/.*#i' => array( 'https://read.amazon.com.au/kp/api/oembed', true ),
95
+
'#https?://([a-z0-9-]+\.)?amazon\.cn/.*#i' => array( 'https://read.amazon.cn/kp/api/oembed', true ),
96
+
'#https?://(www\.)?a\.co/.*#i' => array( 'https://read.amazon.com/kp/api/oembed', true ),
97
+
'#https?://(www\.)?amzn\.to/.*#i' => array( 'https://read.amazon.com/kp/api/oembed', true ),
98
+
'#https?://(www\.)?amzn\.eu/.*#i' => array( 'https://read.amazon.co.uk/kp/api/oembed', true ),
99
+
'#https?://(www\.)?amzn\.in/.*#i' => array( 'https://read.amazon.in/kp/api/oembed', true ),
100
+
'#https?://(www\.)?amzn\.asia/.*#i' => array( 'https://read.amazon.com.au/kp/api/oembed', true ),
101
+
'#https?://(www\.)?z\.cn/.*#i' => array( 'https://read.amazon.cn/kp/api/oembed', true ),
102
+
'#https?://www\.someecards\.com/.+-cards/.+#i' => array( 'https://www.someecards.com/v2/oembed/', true ),
103
+
'#https?://www\.someecards\.com/usercards/viewcard/.+#i' => array( 'https://www.someecards.com/v2/oembed/', true ),
104
+
'#https?://some\.ly\/.+#i' => array( 'https://www.someecards.com/v2/oembed/', true ),
105
+
'#https?://(www\.)?tiktok\.com/.*/video/.*#i' => array( 'https://www.tiktok.com/oembed', true ),
106
+
'#https?://(www\.)?tiktok\.com/@.*#i' => array( 'https://www.tiktok.com/oembed', true ),
107
+
'#https?://([a-z]{2}|www)\.pinterest\.com(\.(au|mx))?/.*#i' => array( 'https://www.pinterest.com/oembed.json', true ),
108
+
'#https?://(www\.)?wolframcloud\.com/obj/.+#i' => array( 'https://www.wolframcloud.com/oembed', true ),
109
+
'#https?://pca\.st/.+#i' => array( 'https://pca.st/oembed.json', true ),
110
+
'#https?://((play|www)\.)?anghami\.com/.*#i' => array( 'https://api.anghami.com/rest/v1/oembed.view', true ),
111
+
'#https?://bsky.app/profile/.*/post/.*#i' => array( 'https://embed.bsky.app/oembed', true ),
112
+
'#https?://(www\.)?canva\.com/design/.*/view.*#i' => array( 'https://canva.com/_oembed', true ),
113
+
);
114
+
115
+
if ( ! empty( self::$early_providers['add'] ) ) {
116
+
foreach ( self::$early_providers['add'] as $format => $data ) {
117
+
$providers[ $format ] = $data;
118
+
}
119
+
}
120
+
121
+
if ( ! empty( self::$early_providers['remove'] ) ) {
122
+
foreach ( self::$early_providers['remove'] as $format ) {
123
+
unset( $providers[ $format ] );
124
+
}
125
+
}
126
+
127
+
self::$early_providers = array();
128
+
129
+
/**
130
+
* Filters the list of sanctioned oEmbed providers.
131
+
*
132
+
* Since WordPress 4.4, oEmbed discovery is enabled for all users and allows embedding of sanitized
133
+
* iframes. The providers in this list are sanctioned, meaning they are trusted and allowed to
134
+
* embed any content, such as iframes, videos, JavaScript, and arbitrary HTML.
135
+
*
136
+
* Supported providers:
137
+
*
138
+
* | Provider | Flavor | Since |
139
+
* | ------------ | ----------------------------------------- | ------- |
140
+
* | Dailymotion | dailymotion.com | 2.9.0 |
141
+
* | Flickr | flickr.com | 2.9.0 |
142
+
* | Scribd | scribd.com | 2.9.0 |
143
+
* | Vimeo | vimeo.com | 2.9.0 |
144
+
* | WordPress.tv | wordpress.tv | 2.9.0 |
145
+
* | YouTube | youtube.com/watch | 2.9.0 |
146
+
* | Crowdsignal | polldaddy.com | 3.0.0 |
147
+
* | SmugMug | smugmug.com | 3.0.0 |
148
+
* | YouTube | youtu.be | 3.0.0 |
149
+
* | Twitter | twitter.com | 3.4.0 |
150
+
* | SoundCloud | soundcloud.com | 3.5.0 |
151
+
* | Dailymotion | dai.ly | 3.6.0 |
152
+
* | Flickr | flic.kr | 3.6.0 |
153
+
* | Spotify | spotify.com | 3.6.0 |
154
+
* | Imgur | imgur.com | 3.9.0 |
155
+
* | Animoto | animoto.com | 4.0.0 |
156
+
* | Animoto | video214.com | 4.0.0 |
157
+
* | Issuu | issuu.com | 4.0.0 |
158
+
* | Mixcloud | mixcloud.com | 4.0.0 |
159
+
* | Crowdsignal | poll.fm | 4.0.0 |
160
+
* | TED | ted.com | 4.0.0 |
161
+
* | YouTube | youtube.com/playlist | 4.0.0 |
162
+
* | Tumblr | tumblr.com | 4.2.0 |
163
+
* | Kickstarter | kickstarter.com | 4.2.0 |
164
+
* | Kickstarter | kck.st | 4.2.0 |
165
+
* | Cloudup | cloudup.com | 4.3.0 |
166
+
* | ReverbNation | reverbnation.com | 4.4.0 |
167
+
* | VideoPress | videopress.com | 4.4.0 |
168
+
* | Reddit | reddit.com | 4.4.0 |
169
+
* | Speaker Deck | speakerdeck.com | 4.4.0 |
170
+
* | Twitter | twitter.com/timelines | 4.5.0 |
171
+
* | Twitter | twitter.com/moments | 4.5.0 |
172
+
* | Twitter | twitter.com/user | 4.7.0 |
173
+
* | Twitter | twitter.com/likes | 4.7.0 |
174
+
* | Twitter | twitter.com/lists | 4.7.0 |
175
+
* | Screencast | screencast.com | 4.8.0 |
176
+
* | Amazon | amazon.com (com.mx, com.br, ca) | 4.9.0 |
177
+
* | Amazon | amazon.de (fr, it, es, in, nl, ru, co.uk) | 4.9.0 |
178
+
* | Amazon | amazon.co.jp (com.au) | 4.9.0 |
179
+
* | Amazon | amazon.cn | 4.9.0 |
180
+
* | Amazon | a.co | 4.9.0 |
181
+
* | Amazon | amzn.to (eu, in, asia) | 4.9.0 |
182
+
* | Amazon | z.cn | 4.9.0 |
183
+
* | Someecards | someecards.com | 4.9.0 |
184
+
* | Someecards | some.ly | 4.9.0 |
185
+
* | Crowdsignal | survey.fm | 5.1.0 |
186
+
* | TikTok | tiktok.com | 5.4.0 |
187
+
* | Pinterest | pinterest.com | 5.9.0 |
188
+
* | WolframCloud | wolframcloud.com | 5.9.0 |
189
+
* | Pocket Casts | pocketcasts.com | 6.1.0 |
190
+
* | Crowdsignal | crowdsignal.net | 6.2.0 |
191
+
* | Anghami | anghami.com | 6.3.0 |
192
+
* | Bluesky | bsky.app | 6.6.0 |
193
+
* | Canva | canva.com | 6.8.0 |
194
+
*
195
+
* No longer supported providers:
196
+
*
197
+
* | Provider | Flavor | Since | Removed |
198
+
* | ------------ | -------------------- | --------- | --------- |
199
+
* | Qik | qik.com | 2.9.0 | 3.9.0 |
200
+
* | Viddler | viddler.com | 2.9.0 | 4.0.0 |
201
+
* | Revision3 | revision3.com | 2.9.0 | 4.2.0 |
202
+
* | Blip | blip.tv | 2.9.0 | 4.4.0 |
203
+
* | Rdio | rdio.com | 3.6.0 | 4.4.1 |
204
+
* | Rdio | rd.io | 3.6.0 | 4.4.1 |
205
+
* | Vine | vine.co | 4.1.0 | 4.9.0 |
206
+
* | Photobucket | photobucket.com | 2.9.0 | 5.1.0 |
207
+
* | Funny or Die | funnyordie.com | 3.0.0 | 5.1.0 |
208
+
* | CollegeHumor | collegehumor.com | 4.0.0 | 5.3.1 |
209
+
* | Hulu | hulu.com | 2.9.0 | 5.5.0 |
210
+
* | Instagram | instagram.com | 3.5.0 | 5.5.2 |
211
+
* | Instagram | instagr.am | 3.5.0 | 5.5.2 |
212
+
* | Instagram TV | instagram.com | 5.1.0 | 5.5.2 |
213
+
* | Instagram TV | instagr.am | 5.1.0 | 5.5.2 |
214
+
* | Facebook | facebook.com | 4.7.0 | 5.5.2 |
215
+
* | Meetup.com | meetup.com | 3.9.0 | 6.0.1 |
216
+
* | Meetup.com | meetu.ps | 3.9.0 | 6.0.1 |
217
+
* | SlideShare | slideshare.net | 3.5.0 | 6.6.0 |
218
+
* | Screencast | screencast.com | 4.8.0 | 6.8.2 |
219
+
*
220
+
* @see wp_oembed_add_provider()
221
+
*
222
+
* @since 2.9.0
223
+
*
224
+
* @param array[] $providers An array of arrays containing data about popular oEmbed providers.
225
+
*/
226
+
$this->providers = apply_filters( 'oembed_providers', $providers );
227
+
228
+
// Fix any embeds that contain new lines in the middle of the HTML which breaks wpautop().
229
+
add_filter( 'oembed_dataparse', array( $this, '_strip_newlines' ), 10, 3 );
230
+
}
231
+
232
+
/**
233
+
* Exposes private/protected methods for backward compatibility.
234
+
*
235
+
* @since 4.0.0
236
+
*
237
+
* @param string $name Method to call.
238
+
* @param array $arguments Arguments to pass when calling.
239
+
* @return mixed|false Return value of the callback, false otherwise.
240
+
*/
241
+
public function __call( $name, $arguments ) {
242
+
if ( in_array( $name, $this->compat_methods, true ) ) {
243
+
return $this->$name( ...$arguments );
244
+
}
245
+
246
+
return false;
247
+
}
248
+
249
+
/**
250
+
* Takes a URL and returns the corresponding oEmbed provider's URL, if there is one.
251
+
*
252
+
* @since 4.0.0
253
+
*
254
+
* @see WP_oEmbed::discover()
255
+
*
256
+
* @param string $url The URL to the content.
257
+
* @param string|array $args {
258
+
* Optional. Additional provider arguments. Default empty.
259
+
*
260
+
* @type bool $discover Optional. Determines whether to attempt to discover link tags
261
+
* at the given URL for an oEmbed provider when the provider URL
262
+
* is not found in the built-in providers list. Default true.
263
+
* }
264
+
* @return string|false The oEmbed provider URL on success, false on failure.
265
+
*/
266
+
public function get_provider( $url, $args = '' ) {
267
+
$args = wp_parse_args( $args );
268
+
269
+
$provider = false;
270
+
271
+
if ( ! isset( $args['discover'] ) ) {
272
+
$args['discover'] = true;
273
+
}
274
+
275
+
foreach ( $this->providers as $matchmask => $data ) {
276
+
list( $providerurl, $regex ) = $data;
277
+
278
+
// Turn the asterisk-type provider URLs into regex.
279
+
if ( ! $regex ) {
280
+
$matchmask = '#' . str_replace( '___wildcard___', '(.+)', preg_quote( str_replace( '*', '___wildcard___', $matchmask ), '#' ) ) . '#i';
281
+
$matchmask = preg_replace( '|^#http\\\://|', '#https?\://', $matchmask );
282
+
}
283
+
284
+
if ( preg_match( $matchmask, $url ) ) {
285
+
$provider = str_replace( '{format}', 'json', $providerurl ); // JSON is easier to deal with than XML.
286
+
break;
287
+
}
288
+
}
289
+
290
+
if ( ! $provider && $args['discover'] ) {
291
+
$provider = $this->discover( $url );
292
+
}
293
+
294
+
return $provider;
295
+
}
296
+
297
+
/**
298
+
* Adds an oEmbed provider.
299
+
*
300
+
* The provider is added just-in-time when wp_oembed_add_provider() is called before
301
+
* the {@see 'plugins_loaded'} hook.
302
+
*
303
+
* The just-in-time addition is for the benefit of the {@see 'oembed_providers'} filter.
304
+
*
305
+
* @since 4.0.0
306
+
*
307
+
* @see wp_oembed_add_provider()
308
+
*
309
+
* @param string $format Format of URL that this provider can handle. You can use
310
+
* asterisks as wildcards.
311
+
* @param string $provider The URL to the oEmbed provider..
312
+
* @param bool $regex Optional. Whether the $format parameter is in a regex format.
313
+
* Default false.
314
+
*/
315
+
public static function _add_provider_early( $format, $provider, $regex = false ) {
316
+
if ( empty( self::$early_providers['add'] ) ) {
317
+
self::$early_providers['add'] = array();
318
+
}
319
+
320
+
self::$early_providers['add'][ $format ] = array( $provider, $regex );
321
+
}
322
+
323
+
/**
324
+
* Removes an oEmbed provider.
325
+
*
326
+
* The provider is removed just-in-time when wp_oembed_remove_provider() is called before
327
+
* the {@see 'plugins_loaded'} hook.
328
+
*
329
+
* The just-in-time removal is for the benefit of the {@see 'oembed_providers'} filter.
330
+
*
331
+
* @since 4.0.0
332
+
*
333
+
* @see wp_oembed_remove_provider()
334
+
*
335
+
* @param string $format The format of URL that this provider can handle. You can use
336
+
* asterisks as wildcards.
337
+
*/
338
+
public static function _remove_provider_early( $format ) {
339
+
if ( empty( self::$early_providers['remove'] ) ) {
340
+
self::$early_providers['remove'] = array();
341
+
}
342
+
343
+
self::$early_providers['remove'][] = $format;
344
+
}
345
+
346
+
/**
347
+
* Takes a URL and attempts to return the oEmbed data.
348
+
*
349
+
* @see WP_oEmbed::fetch()
350
+
*
351
+
* @since 4.8.0
352
+
*
353
+
* @param string $url The URL to the content that should be attempted to be embedded.
354
+
* @param string|array $args Optional. Additional arguments for retrieving embed HTML.
355
+
* See wp_oembed_get() for accepted arguments. Default empty.
356
+
* @return object|false The result in the form of an object on success, false on failure.
357
+
*/
358
+
public function get_data( $url, $args = '' ) {
359
+
$args = wp_parse_args( $args );
360
+
361
+
$provider = $this->get_provider( $url, $args );
362
+
363
+
if ( ! $provider ) {
364
+
return false;
365
+
}
366
+
367
+
$data = $this->fetch( $provider, $url, $args );
368
+
369
+
if ( false === $data ) {
370
+
return false;
371
+
}
372
+
373
+
return $data;
374
+
}
375
+
376
+
/**
377
+
* The do-it-all function that takes a URL and attempts to return the HTML.
378
+
*
379
+
* @see WP_oEmbed::fetch()
380
+
* @see WP_oEmbed::data2html()
381
+
*
382
+
* @since 2.9.0
383
+
*
384
+
* @param string $url The URL to the content that should be attempted to be embedded.
385
+
* @param string|array $args Optional. Additional arguments for retrieving embed HTML.
386
+
* See wp_oembed_get() for accepted arguments. Default empty.
387
+
* @return string|false The UNSANITIZED (and potentially unsafe) HTML that should be used to embed
388
+
* on success, false on failure.
389
+
*/
390
+
public function get_html( $url, $args = '' ) {
391
+
/**
392
+
* Filters the oEmbed result before any HTTP requests are made.
393
+
*
394
+
* This allows one to short-circuit the default logic, perhaps by
395
+
* replacing it with a routine that is more optimal for your setup.
396
+
*
397
+
* Returning a non-null value from the filter will effectively short-circuit retrieval
398
+
* and return the passed value instead.
399
+
*
400
+
* @since 4.5.3
401
+
*
402
+
* @param null|string $result The UNSANITIZED (and potentially unsafe) HTML that should be used to embed.
403
+
* Default null to continue retrieving the result.
404
+
* @param string $url The URL to the content that should be attempted to be embedded.
405
+
* @param string|array $args Optional. Additional arguments for retrieving embed HTML.
406
+
* See wp_oembed_get() for accepted arguments. Default empty.
407
+
*/
408
+
$pre = apply_filters( 'pre_oembed_result', null, $url, $args );
409
+
410
+
if ( null !== $pre ) {
411
+
return $pre;
412
+
}
413
+
414
+
$data = $this->get_data( $url, $args );
415
+
416
+
if ( false === $data ) {
417
+
return false;
418
+
}
419
+
420
+
/**
421
+
* Filters the HTML returned by the oEmbed provider.
422
+
*
423
+
* @since 2.9.0
424
+
*
425
+
* @param string|false $data The returned oEmbed HTML (false if unsafe).
426
+
* @param string $url URL of the content to be embedded.
427
+
* @param string|array $args Optional. Additional arguments for retrieving embed HTML.
428
+
* See wp_oembed_get() for accepted arguments. Default empty.
429
+
*/
430
+
return apply_filters( 'oembed_result', $this->data2html( $data, $url ), $url, $args );
431
+
}
432
+
433
+
/**
434
+
* Attempts to discover link tags at the given URL for an oEmbed provider.
435
+
*
436
+
* @since 2.9.0
437
+
*
438
+
* @param string $url The URL that should be inspected for discovery `<link>` tags.
439
+
* @return string|false The oEmbed provider URL on success, false on failure.
440
+
*/
441
+
public function discover( $url ) {
442
+
$providers = array();
443
+
$args = array(
444
+
'limit_response_size' => 153600, // 150 KB
445
+
);
446
+
447
+
/**
448
+
* Filters oEmbed remote get arguments.
449
+
*
450
+
* @since 4.0.0
451
+
*
452
+
* @see WP_Http::request()
453
+
*
454
+
* @param array $args oEmbed remote get arguments.
455
+
* @param string $url URL to be inspected.
456
+
*/
457
+
$args = apply_filters( 'oembed_remote_get_args', $args, $url );
458
+
459
+
// Fetch URL content.
460
+
$request = wp_safe_remote_get( $url, $args );
461
+
$html = wp_remote_retrieve_body( $request );
462
+
if ( $html ) {
463
+
464
+
/**
465
+
* Filters the link types that contain oEmbed provider URLs.
466
+
*
467
+
* @since 2.9.0
468
+
*
469
+
* @param string[] $format Array of oEmbed link types. Accepts 'application/json+oembed',
470
+
* 'text/xml+oembed', and 'application/xml+oembed' (incorrect,
471
+
* used by at least Vimeo).
472
+
*/
473
+
$linktypes = apply_filters(
474
+
'oembed_linktypes',
475
+
array(
476
+
'application/json+oembed' => 'json',
477
+
'text/xml+oembed' => 'xml',
478
+
'application/xml+oembed' => 'xml',
479
+
)
480
+
);
481
+
482
+
// Strip <body>.
483
+
$html_head_end = stripos( $html, '</head>' );
484
+
if ( $html_head_end ) {
485
+
$html = substr( $html, 0, $html_head_end );
486
+
}
487
+
488
+
// Do a quick check.
489
+
$tagfound = false;
490
+
foreach ( $linktypes as $linktype => $format ) {
491
+
if ( stripos( $html, $linktype ) ) {
492
+
$tagfound = true;
493
+
break;
494
+
}
495
+
}
496
+
497
+
if ( $tagfound && preg_match_all( '#<link([^<>]+)/?>#iU', $html, $links ) ) {
498
+
foreach ( $links[1] as $link ) {
499
+
$atts = shortcode_parse_atts( $link );
500
+
501
+
if ( ! empty( $atts['type'] ) && ! empty( $linktypes[ $atts['type'] ] ) && ! empty( $atts['href'] ) ) {
502
+
$providers[ $linktypes[ $atts['type'] ] ] = htmlspecialchars_decode( $atts['href'] );
503
+
504
+
// Stop here if it's JSON (that's all we need).
505
+
if ( 'json' === $linktypes[ $atts['type'] ] ) {
506
+
break;
507
+
}
508
+
}
509
+
}
510
+
}
511
+
}
512
+
513
+
// JSON is preferred to XML.
514
+
if ( ! empty( $providers['json'] ) ) {
515
+
return $providers['json'];
516
+
} elseif ( ! empty( $providers['xml'] ) ) {
517
+
return $providers['xml'];
518
+
} else {
519
+
return false;
520
+
}
521
+
}
522
+
523
+
/**
524
+
* Connects to an oEmbed provider and returns the result.
525
+
*
526
+
* @since 2.9.0
527
+
*
528
+
* @param string $provider The URL to the oEmbed provider.
529
+
* @param string $url The URL to the content that is desired to be embedded.
530
+
* @param string|array $args Optional. Additional arguments for retrieving embed HTML.
531
+
* See wp_oembed_get() for accepted arguments. Default empty.
532
+
* @return object|false The result in the form of an object on success, false on failure.
533
+
*/
534
+
public function fetch( $provider, $url, $args = '' ) {
535
+
$args = wp_parse_args( $args, wp_embed_defaults( $url ) );
536
+
537
+
$provider = add_query_arg( 'maxwidth', (int) $args['width'], $provider );
538
+
$provider = add_query_arg( 'maxheight', (int) $args['height'], $provider );
539
+
$provider = add_query_arg( 'url', urlencode( $url ), $provider );
540
+
$provider = add_query_arg( 'dnt', 1, $provider );
541
+
542
+
/**
543
+
* Filters the oEmbed URL to be fetched.
544
+
*
545
+
* @since 2.9.0
546
+
* @since 4.9.0 The `dnt` (Do Not Track) query parameter was added to all oEmbed provider URLs.
547
+
*
548
+
* @param string $provider URL of the oEmbed provider.
549
+
* @param string $url URL of the content to be embedded.
550
+
* @param array $args Optional. Additional arguments for retrieving embed HTML.
551
+
* See wp_oembed_get() for accepted arguments. Default empty.
552
+
*/
553
+
$provider = apply_filters( 'oembed_fetch_url', $provider, $url, $args );
554
+
555
+
foreach ( array( 'json', 'xml' ) as $format ) {
556
+
$result = $this->_fetch_with_format( $provider, $format );
557
+
if ( is_wp_error( $result ) && 'not-implemented' === $result->get_error_code() ) {
558
+
continue;
559
+
}
560
+
561
+
return ( $result && ! is_wp_error( $result ) ) ? $result : false;
562
+
}
563
+
564
+
return false;
565
+
}
566
+
567
+
/**
568
+
* Fetches result from an oEmbed provider for a specific format and complete provider URL
569
+
*
570
+
* @since 3.0.0
571
+
*
572
+
* @param string $provider_url_with_args URL to the provider with full arguments list (url, maxheight, etc.)
573
+
* @param string $format Format to use.
574
+
* @return object|false|WP_Error The result in the form of an object on success, false on failure.
575
+
*/
576
+
private function _fetch_with_format( $provider_url_with_args, $format ) {
577
+
$provider_url_with_args = add_query_arg( 'format', $format, $provider_url_with_args );
578
+
579
+
/** This filter is documented in wp-includes/class-wp-oembed.php */
580
+
$args = apply_filters( 'oembed_remote_get_args', array(), $provider_url_with_args );
581
+
582
+
$response = wp_safe_remote_get( $provider_url_with_args, $args );
583
+
584
+
if ( 501 === wp_remote_retrieve_response_code( $response ) ) {
585
+
return new WP_Error( 'not-implemented' );
586
+
}
587
+
588
+
$body = wp_remote_retrieve_body( $response );
589
+
if ( ! $body ) {
590
+
return false;
591
+
}
592
+
593
+
$parse_method = "_parse_$format";
594
+
595
+
return $this->$parse_method( $body );
596
+
}
597
+
598
+
/**
599
+
* Parses a json response body.
600
+
*
601
+
* @since 3.0.0
602
+
*
603
+
* @param string $response_body
604
+
* @return object|false
605
+
*/
606
+
private function _parse_json( $response_body ) {
607
+
$data = json_decode( trim( $response_body ) );
608
+
609
+
return ( $data && is_object( $data ) ) ? $data : false;
610
+
}
611
+
612
+
/**
613
+
* Parses an XML response body.
614
+
*
615
+
* @since 3.0.0
616
+
*
617
+
* @param string $response_body
618
+
* @return object|false
619
+
*/
620
+
private function _parse_xml( $response_body ) {
621
+
if ( ! function_exists( 'libxml_disable_entity_loader' ) ) {
622
+
return false;
623
+
}
624
+
625
+
if ( PHP_VERSION_ID < 80000 ) {
626
+
/*
627
+
* This function has been deprecated in PHP 8.0 because in libxml 2.9.0, external entity loading
628
+
* is disabled by default, so this function is no longer needed to protect against XXE attacks.
629
+
*/
630
+
$loader = libxml_disable_entity_loader( true );
631
+
}
632
+
633
+
$errors = libxml_use_internal_errors( true );
634
+
635
+
$return = $this->_parse_xml_body( $response_body );
636
+
637
+
libxml_use_internal_errors( $errors );
638
+
639
+
if ( PHP_VERSION_ID < 80000 && isset( $loader ) ) {
640
+
// phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.libxml_disable_entity_loaderDeprecated
641
+
libxml_disable_entity_loader( $loader );
642
+
}
643
+
644
+
return $return;
645
+
}
646
+
647
+
/**
648
+
* Serves as a helper function for parsing an XML response body.
649
+
*
650
+
* @since 3.6.0
651
+
*
652
+
* @param string $response_body
653
+
* @return stdClass|false
654
+
*/
655
+
private function _parse_xml_body( $response_body ) {
656
+
if ( ! function_exists( 'simplexml_import_dom' ) || ! class_exists( 'DOMDocument', false ) ) {
657
+
return false;
658
+
}
659
+
660
+
$dom = new DOMDocument();
661
+
$success = $dom->loadXML( $response_body );
662
+
if ( ! $success ) {
663
+
return false;
664
+
}
665
+
666
+
if ( isset( $dom->doctype ) ) {
667
+
return false;
668
+
}
669
+
670
+
foreach ( $dom->childNodes as $child ) {
671
+
if ( XML_DOCUMENT_TYPE_NODE === $child->nodeType ) {
672
+
return false;
673
+
}
674
+
}
675
+
676
+
$xml = simplexml_import_dom( $dom );
677
+
if ( ! $xml ) {
678
+
return false;
679
+
}
680
+
681
+
$return = new stdClass();
682
+
foreach ( $xml as $key => $value ) {
683
+
$return->$key = (string) $value;
684
+
}
685
+
686
+
return $return;
687
+
}
688
+
689
+
/**
690
+
* Converts a data object from WP_oEmbed::fetch() and returns the HTML.
691
+
*
692
+
* @since 2.9.0
693
+
*
694
+
* @param object $data A data object result from an oEmbed provider.
695
+
* @param string $url The URL to the content that is desired to be embedded.
696
+
* @return string|false The HTML needed to embed on success, false on failure.
697
+
*/
698
+
public function data2html( $data, $url ) {
699
+
if ( ! is_object( $data ) || empty( $data->type ) ) {
700
+
return false;
701
+
}
702
+
703
+
$return = false;
704
+
705
+
switch ( $data->type ) {
706
+
case 'photo':
707
+
if ( empty( $data->url ) || empty( $data->width ) || empty( $data->height ) ) {
708
+
break;
709
+
}
710
+
if ( ! is_string( $data->url ) || ! is_numeric( $data->width ) || ! is_numeric( $data->height ) ) {
711
+
break;
712
+
}
713
+
714
+
$title = ! empty( $data->title ) && is_string( $data->title ) ? $data->title : '';
715
+
$return = '<a href="' . esc_url( $url ) . '"><img src="' . esc_url( $data->url ) . '" alt="' . esc_attr( $title ) . '" width="' . esc_attr( $data->width ) . '" height="' . esc_attr( $data->height ) . '" /></a>';
716
+
break;
717
+
718
+
case 'video':
719
+
case 'rich':
720
+
if ( ! empty( $data->html ) && is_string( $data->html ) ) {
721
+
$return = $data->html;
722
+
}
723
+
break;
724
+
725
+
case 'link':
726
+
if ( ! empty( $data->title ) && is_string( $data->title ) ) {
727
+
$return = '<a href="' . esc_url( $url ) . '">' . esc_html( $data->title ) . '</a>';
728
+
}
729
+
break;
730
+
731
+
default:
732
+
$return = false;
733
+
}
734
+
735
+
/**
736
+
* Filters the returned oEmbed HTML.
737
+
*
738
+
* Use this filter to add support for custom data types, or to filter the result.
739
+
*
740
+
* @since 2.9.0
741
+
*
742
+
* @param string|false $return The returned oEmbed HTML, or false on failure.
743
+
* @param object $data A data object result from an oEmbed provider.
744
+
* @param string $url The URL of the content to be embedded.
745
+
*/
746
+
return apply_filters( 'oembed_dataparse', $return, $data, $url );
747
+
}
748
+
749
+
/**
750
+
* Strips any new lines from the HTML.
751
+
*
752
+
* @since 2.9.0 as strip_scribd_newlines()
753
+
* @since 3.0.0
754
+
*
755
+
* @param string|false $html Existing HTML.
756
+
* @param object $data Data object from WP_oEmbed::data2html()
757
+
* @param string $url The original URL passed to oEmbed.
758
+
* @return string|false Possibly modified $html.
759
+
*/
760
+
public function _strip_newlines( $html, $data, $url ) {
761
+
if ( ! str_contains( $html, "\n" ) ) {
762
+
return $html;
763
+
}
764
+
765
+
$count = 1;
766
+
$found = array();
767
+
$token = '__PRE__';
768
+
$search = array( "\t", "\n", "\r", ' ' );
769
+
$replace = array( '__TAB__', '__NL__', '__CR__', '__SPACE__' );
770
+
$tokenized = str_replace( $search, $replace, $html );
771
+
772
+
preg_match_all( '#(<pre[^>]*>.+?</pre>)#i', $tokenized, $matches, PREG_SET_ORDER );
773
+
foreach ( $matches as $i => $match ) {
774
+
$tag_html = str_replace( $replace, $search, $match[0] );
775
+
$tag_token = $token . $i;
776
+
777
+
$found[ $tag_token ] = $tag_html;
778
+
$html = str_replace( $tag_html, $tag_token, $html, $count );
779
+
}
780
+
781
+
$replaced = str_replace( $replace, $search, $html );
782
+
$stripped = str_replace( array( "\r\n", "\n" ), '', $replaced );
783
+
$pre = array_values( $found );
784
+
$tokens = array_keys( $found );
785
+
786
+
return str_replace( $tokens, $pre, $stripped );
787
+
}
788
+
}
789
+