1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* API for fetching the HTML to embed remote content based on a provided URL. |
4
|
|
|
* Used internally by the {@link WP_Embed} class, but is designed to be generic. |
5
|
|
|
* |
6
|
|
|
* @link https://codex.wordpress.org/oEmbed oEmbed Codex Article |
7
|
|
|
* @link http://oembed.com/ oEmbed Homepage |
8
|
|
|
* |
9
|
|
|
* @package WordPress |
10
|
|
|
* @subpackage oEmbed |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* oEmbed class. |
15
|
|
|
* |
16
|
|
|
* @package WordPress |
17
|
|
|
* @subpackage oEmbed |
18
|
|
|
* @since 2.9.0 |
19
|
|
|
*/ |
20
|
|
|
class WP_oEmbed { |
21
|
|
|
public $providers = array(); |
22
|
|
|
/** |
23
|
|
|
* @static |
24
|
|
|
* @var array |
25
|
|
|
*/ |
26
|
|
|
public static $early_providers = array(); |
27
|
|
|
|
28
|
|
|
private $compat_methods = array( '_fetch_with_format', '_parse_json', '_parse_xml', '_parse_body' ); |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* Constructor |
32
|
|
|
* |
33
|
|
|
* @since 2.9.0 |
34
|
|
|
*/ |
35
|
|
|
public function __construct() { |
36
|
|
|
$host = urlencode( home_url() ); |
37
|
|
|
$providers = array( |
38
|
|
|
'#http://((m|www)\.)?youtube\.com/watch.*#i' => array( 'http://www.youtube.com/oembed', true ), |
39
|
|
|
'#https://((m|www)\.)?youtube\.com/watch.*#i' => array( 'http://www.youtube.com/oembed?scheme=https', true ), |
40
|
|
|
'#http://((m|www)\.)?youtube\.com/playlist.*#i' => array( 'http://www.youtube.com/oembed', true ), |
41
|
|
|
'#https://((m|www)\.)?youtube\.com/playlist.*#i' => array( 'http://www.youtube.com/oembed?scheme=https', true ), |
42
|
|
|
'#http://youtu\.be/.*#i' => array( 'http://www.youtube.com/oembed', true ), |
43
|
|
|
'#https://youtu\.be/.*#i' => array( 'http://www.youtube.com/oembed?scheme=https', true ), |
44
|
|
|
'#https?://(.+\.)?vimeo\.com/.*#i' => array( 'http://vimeo.com/api/oembed.{format}', true ), |
45
|
|
|
'#https?://(www\.)?dailymotion\.com/.*#i' => array( 'https://www.dailymotion.com/services/oembed', true ), |
46
|
|
|
'#https?://dai.ly/.*#i' => array( 'https://www.dailymotion.com/services/oembed', true ), |
47
|
|
|
'#https?://(www\.)?flickr\.com/.*#i' => array( 'https://www.flickr.com/services/oembed/', true ), |
48
|
|
|
'#https?://flic\.kr/.*#i' => array( 'https://www.flickr.com/services/oembed/', true ), |
49
|
|
|
'#https?://(.+\.)?smugmug\.com/.*#i' => array( 'http://api.smugmug.com/services/oembed/', true ), |
50
|
|
|
'#https?://(www\.)?hulu\.com/watch/.*#i' => array( 'http://www.hulu.com/api/oembed.{format}', true ), |
51
|
|
|
'http://i*.photobucket.com/albums/*' => array( 'http://api.photobucket.com/oembed', false ), |
52
|
|
|
'http://gi*.photobucket.com/groups/*' => array( 'http://api.photobucket.com/oembed', false ), |
53
|
|
|
'#https?://(www\.)?scribd\.com/doc/.*#i' => array( 'http://www.scribd.com/services/oembed', true ), |
54
|
|
|
'#https?://wordpress.tv/.*#i' => array( 'http://wordpress.tv/oembed/', true ), |
55
|
|
|
'#https?://(.+\.)?polldaddy\.com/.*#i' => array( 'https://polldaddy.com/oembed/', true ), |
56
|
|
|
'#https?://poll\.fm/.*#i' => array( 'https://polldaddy.com/oembed/', true ), |
57
|
|
|
'#https?://(www\.)?funnyordie\.com/videos/.*#i' => array( 'http://www.funnyordie.com/oembed', true ), |
58
|
|
|
'#https?://(www\.)?twitter\.com/.+?/status(es)?/.*#i' => array( 'https://publish.twitter.com/oembed', true ), |
59
|
|
|
'#https?://(www\.)?twitter\.com/.+?/timelines/.*#i' => array( 'https://publish.twitter.com/oembed', true ), |
60
|
|
|
'#https?://(www\.)?twitter\.com/i/moments/.*#i' => array( 'https://publish.twitter.com/oembed', true ), |
61
|
|
|
'#https?://vine.co/v/.*#i' => array( 'https://vine.co/oembed.{format}', true ), |
62
|
|
|
'#https?://(www\.)?soundcloud\.com/.*#i' => array( 'http://soundcloud.com/oembed', true ), |
63
|
|
|
'#https?://(.+?\.)?slideshare\.net/.*#i' => array( 'https://www.slideshare.net/api/oembed/2', true ), |
64
|
|
|
'#https?://(www\.)?instagr(\.am|am\.com)/p/.*#i' => array( 'https://api.instagram.com/oembed', true ), |
65
|
|
|
'#https?://(open|play)\.spotify\.com/.*#i' => array( 'https://embed.spotify.com/oembed/', true ), |
66
|
|
|
'#https?://(.+\.)?imgur\.com/.*#i' => array( 'http://api.imgur.com/oembed', true ), |
67
|
|
|
'#https?://(www\.)?meetu(\.ps|p\.com)/.*#i' => array( 'http://api.meetup.com/oembed', true ), |
68
|
|
|
'#https?://(www\.)?issuu\.com/.+/docs/.+#i' => array( 'http://issuu.com/oembed_wp', true ), |
69
|
|
|
'#https?://(www\.)?collegehumor\.com/video/.*#i' => array( 'http://www.collegehumor.com/oembed.{format}', true ), |
70
|
|
|
'#https?://(www\.)?mixcloud\.com/.*#i' => array( 'http://www.mixcloud.com/oembed', true ), |
71
|
|
|
'#https?://(www\.|embed\.)?ted\.com/talks/.*#i' => array( 'http://www.ted.com/talks/oembed.{format}', true ), |
72
|
|
|
'#https?://(www\.)?(animoto|video214)\.com/play/.*#i' => array( 'https://animoto.com/oembeds/create', true ), |
73
|
|
|
'#https?://(.+)\.tumblr\.com/post/.*#i' => array( 'https://www.tumblr.com/oembed/1.0', true ), |
74
|
|
|
'#https?://(www\.)?kickstarter\.com/projects/.*#i' => array( 'https://www.kickstarter.com/services/oembed', true ), |
75
|
|
|
'#https?://kck\.st/.*#i' => array( 'https://www.kickstarter.com/services/oembed', true ), |
76
|
|
|
'#https?://cloudup\.com/.*#i' => array( 'https://cloudup.com/oembed', true ), |
77
|
|
|
'#https?://(www\.)?reverbnation\.com/.*#i' => array( 'https://www.reverbnation.com/oembed', true ), |
78
|
|
|
'#https?://videopress.com/v/.*#' => array( 'https://public-api.wordpress.com/oembed/1.0/?for=' . $host, true ), |
79
|
|
|
'#https?://(www\.)?reddit\.com/r/[^/]+/comments/.*#i' => array( 'https://www.reddit.com/oembed', true ), |
80
|
|
|
'#https?://(www\.)?speakerdeck\.com/.*#i' => array( 'https://speakerdeck.com/oembed.{format}', true ), |
81
|
|
|
); |
82
|
|
|
|
83
|
|
View Code Duplication |
if ( ! empty( self::$early_providers['add'] ) ) { |
84
|
|
|
foreach ( self::$early_providers['add'] as $format => $data ) { |
85
|
|
|
$providers[ $format ] = $data; |
86
|
|
|
} |
87
|
|
|
} |
88
|
|
|
|
89
|
|
View Code Duplication |
if ( ! empty( self::$early_providers['remove'] ) ) { |
90
|
|
|
foreach ( self::$early_providers['remove'] as $format ) { |
91
|
|
|
unset( $providers[ $format ] ); |
92
|
|
|
} |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
self::$early_providers = array(); |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Filter the list of whitelisted oEmbed providers. |
99
|
|
|
* |
100
|
|
|
* Since WordPress 4.4, oEmbed discovery is enabled for all users and allows embedding of sanitized |
101
|
|
|
* iframes. The providers in this list are whitelisted, meaning they are trusted and allowed to |
102
|
|
|
* embed any content, such as iframes, videos, JavaScript, and arbitrary HTML. |
103
|
|
|
* |
104
|
|
|
* Supported providers: |
105
|
|
|
* |
106
|
|
|
* | Provider | Flavor | HTTPS | Since | |
107
|
|
|
* | ------------ | -------------------- | :---: | --------- | |
108
|
|
|
* | Dailymotion | dailymotion.com | Yes | 2.9.0 | |
109
|
|
|
* | Flickr | flickr.com | Yes | 2.9.0 | |
110
|
|
|
* | Hulu | hulu.com | Yes | 2.9.0 | |
111
|
|
|
* | Photobucket | photobucket.com | No | 2.9.0 | |
112
|
|
|
* | Scribd | scribd.com | Yes | 2.9.0 | |
113
|
|
|
* | Vimeo | vimeo.com | Yes | 2.9.0 | |
114
|
|
|
* | WordPress.tv | wordpress.tv | Yes | 2.9.0 | |
115
|
|
|
* | YouTube | youtube.com/watch | Yes | 2.9.0 | |
116
|
|
|
* | Funny or Die | funnyordie.com | Yes | 3.0.0 | |
117
|
|
|
* | Polldaddy | polldaddy.com | Yes | 3.0.0 | |
118
|
|
|
* | SmugMug | smugmug.com | Yes | 3.0.0 | |
119
|
|
|
* | YouTube | youtu.be | Yes | 3.0.0 | |
120
|
|
|
* | Twitter | twitter.com | Yes | 3.4.0 | |
121
|
|
|
* | Instagram | instagram.com | Yes | 3.5.0 | |
122
|
|
|
* | Instagram | instagr.am | Yes | 3.5.0 | |
123
|
|
|
* | Slideshare | slideshare.net | Yes | 3.5.0 | |
124
|
|
|
* | SoundCloud | soundcloud.com | Yes | 3.5.0 | |
125
|
|
|
* | Dailymotion | dai.ly | Yes | 3.6.0 | |
126
|
|
|
* | Flickr | flic.kr | Yes | 3.6.0 | |
127
|
|
|
* | Spotify | spotify.com | Yes | 3.6.0 | |
128
|
|
|
* | Imgur | imgur.com | Yes | 3.9.0 | |
129
|
|
|
* | Meetup.com | meetup.com | Yes | 3.9.0 | |
130
|
|
|
* | Meetup.com | meetu.ps | Yes | 3.9.0 | |
131
|
|
|
* | Animoto | animoto.com | Yes | 4.0.0 | |
132
|
|
|
* | Animoto | video214.com | Yes | 4.0.0 | |
133
|
|
|
* | CollegeHumor | collegehumor.com | Yes | 4.0.0 | |
134
|
|
|
* | Issuu | issuu.com | Yes | 4.0.0 | |
135
|
|
|
* | Mixcloud | mixcloud.com | Yes | 4.0.0 | |
136
|
|
|
* | Polldaddy | poll.fm | Yes | 4.0.0 | |
137
|
|
|
* | TED | ted.com | Yes | 4.0.0 | |
138
|
|
|
* | YouTube | youtube.com/playlist | Yes | 4.0.0 | |
139
|
|
|
* | Vine | vine.co | Yes | 4.1.0 | |
140
|
|
|
* | Tumblr | tumblr.com | Yes | 4.2.0 | |
141
|
|
|
* | Kickstarter | kickstarter.com | Yes | 4.2.0 | |
142
|
|
|
* | Kickstarter | kck.st | Yes | 4.2.0 | |
143
|
|
|
* | Cloudup | cloudup.com | Yes | 4.4.0 | |
144
|
|
|
* | ReverbNation | reverbnation.com | Yes | 4.4.0 | |
145
|
|
|
* | VideoPress | videopress.com | Yes | 4.4.0 | |
146
|
|
|
* | Reddit | reddit.com | Yes | 4.4.0 | |
147
|
|
|
* | Speaker Deck | speakerdeck.com | Yes | 4.4.0 | |
148
|
|
|
* |
149
|
|
|
* No longer supported providers: |
150
|
|
|
* |
151
|
|
|
* | Provider | Flavor | HTTPS | Since | Removed | |
152
|
|
|
* | ------------ | -------------------- | :---: | --------- | --------- | |
153
|
|
|
* | Qik | qik.com | Yes | 2.9.0 | 3.9.0 | |
154
|
|
|
* | Viddler | viddler.com | Yes | 2.9.0 | 4.0.0 | |
155
|
|
|
* | Revision3 | revision3.com | No | 2.9.0 | 4.2.0 | |
156
|
|
|
* | Blip | blip.tv | No | 2.9.0 | 4.4.0 | |
157
|
|
|
* | Rdio | rdio.com | Yes | 3.6.0 | 4.4.1 | |
158
|
|
|
* | Rdio | rd.io | Yes | 3.6.0 | 4.4.1 | |
159
|
|
|
* |
160
|
|
|
* @see wp_oembed_add_provider() |
161
|
|
|
* |
162
|
|
|
* @since 2.9.0 |
163
|
|
|
* |
164
|
|
|
* @param array $providers An array of popular oEmbed providers. |
165
|
|
|
*/ |
166
|
|
|
$this->providers = apply_filters( 'oembed_providers', $providers ); |
|
|
|
|
167
|
|
|
|
168
|
|
|
// Fix any embeds that contain new lines in the middle of the HTML which breaks wpautop(). |
169
|
|
|
add_filter( 'oembed_dataparse', array($this, '_strip_newlines'), 10, 3 ); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Make private/protected methods readable for backwards compatibility. |
174
|
|
|
* |
175
|
|
|
* @since 4.0.0 |
176
|
|
|
* @access public |
177
|
|
|
* |
178
|
|
|
* @param callable $name Method to call. |
179
|
|
|
* @param array $arguments Arguments to pass when calling. |
180
|
|
|
* @return mixed|bool Return value of the callback, false otherwise. |
181
|
|
|
*/ |
182
|
|
|
public function __call( $name, $arguments ) { |
183
|
|
|
if ( in_array( $name, $this->compat_methods ) ) { |
184
|
|
|
return call_user_func_array( array( $this, $name ), $arguments ); |
185
|
|
|
} |
186
|
|
|
return false; |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Takes a URL and returns the corresponding oEmbed provider's URL, if there is one. |
191
|
|
|
* |
192
|
|
|
* @since 4.0.0 |
193
|
|
|
* @access public |
194
|
|
|
* |
195
|
|
|
* @see WP_oEmbed::discover() |
196
|
|
|
* |
197
|
|
|
* @param string $url The URL to the content. |
198
|
|
|
* @param string|array $args Optional provider arguments. |
199
|
|
|
* @return false|string False on failure, otherwise the oEmbed provider URL. |
200
|
|
|
*/ |
201
|
|
|
public function get_provider( $url, $args = '' ) { |
202
|
|
|
|
203
|
|
|
$provider = false; |
204
|
|
|
|
205
|
|
|
if ( !isset($args['discover']) ) |
206
|
|
|
$args['discover'] = true; |
207
|
|
|
|
208
|
|
|
foreach ( $this->providers as $matchmask => $data ) { |
209
|
|
|
list( $providerurl, $regex ) = $data; |
210
|
|
|
|
211
|
|
|
// Turn the asterisk-type provider URLs into regex |
212
|
|
|
if ( !$regex ) { |
213
|
|
|
$matchmask = '#' . str_replace( '___wildcard___', '(.+)', preg_quote( str_replace( '*', '___wildcard___', $matchmask ), '#' ) ) . '#i'; |
214
|
|
|
$matchmask = preg_replace( '|^#http\\\://|', '#https?\://', $matchmask ); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
if ( preg_match( $matchmask, $url ) ) { |
218
|
|
|
$provider = str_replace( '{format}', 'json', $providerurl ); // JSON is easier to deal with than XML |
219
|
|
|
break; |
220
|
|
|
} |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
if ( !$provider && $args['discover'] ) |
224
|
|
|
$provider = $this->discover( $url ); |
225
|
|
|
|
226
|
|
|
return $provider; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* Add an oEmbed provider just-in-time when wp_oembed_add_provider() is called |
231
|
|
|
* before the 'plugins_loaded' hook. |
232
|
|
|
* |
233
|
|
|
* The just-in-time addition is for the benefit of the 'oembed_providers' filter. |
234
|
|
|
* |
235
|
|
|
* @since 4.0.0 |
236
|
|
|
* @access public |
237
|
|
|
* @static |
238
|
|
|
* |
239
|
|
|
* @see wp_oembed_add_provider() |
240
|
|
|
* |
241
|
|
|
* @param string $format Format of URL that this provider can handle. You can use |
242
|
|
|
* asterisks as wildcards. |
243
|
|
|
* @param string $provider The URL to the oEmbed provider.. |
244
|
|
|
* @param bool $regex Optional. Whether the $format parameter is in a regex format. |
245
|
|
|
* Default false. |
246
|
|
|
*/ |
247
|
|
|
public static function _add_provider_early( $format, $provider, $regex = false ) { |
248
|
|
|
if ( empty( self::$early_providers['add'] ) ) { |
249
|
|
|
self::$early_providers['add'] = array(); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
self::$early_providers['add'][ $format ] = array( $provider, $regex ); |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Remove an oEmbed provider just-in-time when wp_oembed_remove_provider() is called |
257
|
|
|
* before the 'plugins_loaded' hook. |
258
|
|
|
* |
259
|
|
|
* The just-in-time removal is for the benefit of the 'oembed_providers' filter. |
260
|
|
|
* |
261
|
|
|
* @since 4.0.0 |
262
|
|
|
* @access public |
263
|
|
|
* @static |
264
|
|
|
* |
265
|
|
|
* @see wp_oembed_remove_provider() |
266
|
|
|
* |
267
|
|
|
* @param string $format The format of URL that this provider can handle. You can use |
268
|
|
|
* asterisks as wildcards. |
269
|
|
|
*/ |
270
|
|
|
public static function _remove_provider_early( $format ) { |
271
|
|
|
if ( empty( self::$early_providers['remove'] ) ) { |
272
|
|
|
self::$early_providers['remove'] = array(); |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
self::$early_providers['remove'][] = $format; |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* The do-it-all function that takes a URL and attempts to return the HTML. |
280
|
|
|
* |
281
|
|
|
* @see WP_oEmbed::fetch() |
282
|
|
|
* @see WP_oEmbed::data2html() |
283
|
|
|
* |
284
|
|
|
* @param string $url The URL to the content that should be attempted to be embedded. |
285
|
|
|
* @param array $args Optional arguments. Usually passed from a shortcode. |
286
|
|
|
* @return false|string False on failure, otherwise the UNSANITIZED (and potentially unsafe) HTML that should be used to embed. |
287
|
|
|
*/ |
288
|
|
|
public function get_html( $url, $args = '' ) { |
289
|
|
|
$provider = $this->get_provider( $url, $args ); |
290
|
|
|
|
291
|
|
|
if ( !$provider || false === $data = $this->fetch( $provider, $url, $args ) ) |
|
|
|
|
292
|
|
|
return false; |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* Filter the HTML returned by the oEmbed provider. |
296
|
|
|
* |
297
|
|
|
* @since 2.9.0 |
298
|
|
|
* |
299
|
|
|
* @param string $data The returned oEmbed HTML. |
300
|
|
|
* @param string $url URL of the content to be embedded. |
301
|
|
|
* @param array $args Optional arguments, usually passed from a shortcode. |
302
|
|
|
*/ |
303
|
|
|
return apply_filters( 'oembed_result', $this->data2html( $data, $url ), $url, $args ); |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* Attempts to discover link tags at the given URL for an oEmbed provider. |
308
|
|
|
* |
309
|
|
|
* @param string $url The URL that should be inspected for discovery `<link>` tags. |
310
|
|
|
* @return false|string False on failure, otherwise the oEmbed provider URL. |
311
|
|
|
*/ |
312
|
|
|
public function discover( $url ) { |
313
|
|
|
$providers = array(); |
314
|
|
|
$args = array( |
315
|
|
|
'limit_response_size' => 153600, // 150 KB |
316
|
|
|
); |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* Filter oEmbed remote get arguments. |
320
|
|
|
* |
321
|
|
|
* @since 4.0.0 |
322
|
|
|
* |
323
|
|
|
* @see WP_Http::request() |
324
|
|
|
* |
325
|
|
|
* @param array $args oEmbed remote get arguments. |
326
|
|
|
* @param string $url URL to be inspected. |
327
|
|
|
*/ |
328
|
|
|
$args = apply_filters( 'oembed_remote_get_args', $args, $url ); |
329
|
|
|
|
330
|
|
|
// Fetch URL content |
331
|
|
|
$request = wp_safe_remote_get( $url, $args ); |
332
|
|
|
if ( $html = wp_remote_retrieve_body( $request ) ) { |
|
|
|
|
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* Filter the link types that contain oEmbed provider URLs. |
336
|
|
|
* |
337
|
|
|
* @since 2.9.0 |
338
|
|
|
* |
339
|
|
|
* @param array $format Array of oEmbed link types. Accepts 'application/json+oembed', |
340
|
|
|
* 'text/xml+oembed', and 'application/xml+oembed' (incorrect, |
341
|
|
|
* used by at least Vimeo). |
342
|
|
|
*/ |
343
|
|
|
$linktypes = apply_filters( 'oembed_linktypes', array( |
344
|
|
|
'application/json+oembed' => 'json', |
345
|
|
|
'text/xml+oembed' => 'xml', |
346
|
|
|
'application/xml+oembed' => 'xml', |
347
|
|
|
) ); |
348
|
|
|
|
349
|
|
|
// Strip <body> |
350
|
|
|
if ( $html_head_end = stripos( $html, '</head>' ) ) { |
351
|
|
|
$html = substr( $html, 0, $html_head_end ); |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
// Do a quick check |
355
|
|
|
$tagfound = false; |
356
|
|
|
foreach ( $linktypes as $linktype => $format ) { |
357
|
|
|
if ( stripos($html, $linktype) ) { |
358
|
|
|
$tagfound = true; |
359
|
|
|
break; |
360
|
|
|
} |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
if ( $tagfound && preg_match_all( '#<link([^<>]+)/?>#iU', $html, $links ) ) { |
364
|
|
|
foreach ( $links[1] as $link ) { |
365
|
|
|
$atts = shortcode_parse_atts( $link ); |
366
|
|
|
|
367
|
|
|
if ( !empty($atts['type']) && !empty($linktypes[$atts['type']]) && !empty($atts['href']) ) { |
368
|
|
|
$providers[$linktypes[$atts['type']]] = htmlspecialchars_decode( $atts['href'] ); |
369
|
|
|
|
370
|
|
|
// Stop here if it's JSON (that's all we need) |
371
|
|
|
if ( 'json' == $linktypes[$atts['type']] ) |
372
|
|
|
break; |
373
|
|
|
} |
374
|
|
|
} |
375
|
|
|
} |
376
|
|
|
} |
377
|
|
|
|
378
|
|
|
// JSON is preferred to XML |
379
|
|
|
if ( !empty($providers['json']) ) |
380
|
|
|
return $providers['json']; |
381
|
|
|
elseif ( !empty($providers['xml']) ) |
382
|
|
|
return $providers['xml']; |
383
|
|
|
else |
384
|
|
|
return false; |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* Connects to a oEmbed provider and returns the result. |
389
|
|
|
* |
390
|
|
|
* @param string $provider The URL to the oEmbed provider. |
391
|
|
|
* @param string $url The URL to the content that is desired to be embedded. |
392
|
|
|
* @param array $args Optional arguments. Usually passed from a shortcode. |
393
|
|
|
* @return false|object False on failure, otherwise the result in the form of an object. |
394
|
|
|
*/ |
395
|
|
|
public function fetch( $provider, $url, $args = '' ) { |
396
|
|
|
$args = wp_parse_args( $args, wp_embed_defaults( $url ) ); |
397
|
|
|
|
398
|
|
|
$provider = add_query_arg( 'maxwidth', (int) $args['width'], $provider ); |
399
|
|
|
$provider = add_query_arg( 'maxheight', (int) $args['height'], $provider ); |
400
|
|
|
$provider = add_query_arg( 'url', urlencode($url), $provider ); |
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* Filter the oEmbed URL to be fetched. |
404
|
|
|
* |
405
|
|
|
* @since 2.9.0 |
406
|
|
|
* |
407
|
|
|
* @param string $provider URL of the oEmbed provider. |
408
|
|
|
* @param string $url URL of the content to be embedded. |
409
|
|
|
* @param array $args Optional arguments, usually passed from a shortcode. |
410
|
|
|
*/ |
411
|
|
|
$provider = apply_filters( 'oembed_fetch_url', $provider, $url, $args ); |
412
|
|
|
|
413
|
|
|
foreach ( array( 'json', 'xml' ) as $format ) { |
414
|
|
|
$result = $this->_fetch_with_format( $provider, $format ); |
415
|
|
|
if ( is_wp_error( $result ) && 'not-implemented' == $result->get_error_code() ) |
416
|
|
|
continue; |
417
|
|
|
return ( $result && ! is_wp_error( $result ) ) ? $result : false; |
418
|
|
|
} |
419
|
|
|
return false; |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
/** |
423
|
|
|
* Fetches result from an oEmbed provider for a specific format and complete provider URL |
424
|
|
|
* |
425
|
|
|
* @since 3.0.0 |
426
|
|
|
* @access private |
427
|
|
|
* @param string $provider_url_with_args URL to the provider with full arguments list (url, maxheight, etc.) |
428
|
|
|
* @param string $format Format to use |
429
|
|
|
* @return false|object|WP_Error False on failure, otherwise the result in the form of an object. |
430
|
|
|
*/ |
431
|
|
|
private function _fetch_with_format( $provider_url_with_args, $format ) { |
432
|
|
|
$provider_url_with_args = add_query_arg( 'format', $format, $provider_url_with_args ); |
433
|
|
|
|
434
|
|
|
/** This filter is documented in wp-includes/class-oembed.php */ |
435
|
|
|
$args = apply_filters( 'oembed_remote_get_args', array(), $provider_url_with_args ); |
436
|
|
|
|
437
|
|
|
$response = wp_safe_remote_get( $provider_url_with_args, $args ); |
438
|
|
|
if ( 501 == wp_remote_retrieve_response_code( $response ) ) |
|
|
|
|
439
|
|
|
return new WP_Error( 'not-implemented' ); |
440
|
|
|
if ( ! $body = wp_remote_retrieve_body( $response ) ) |
|
|
|
|
441
|
|
|
return false; |
442
|
|
|
$parse_method = "_parse_$format"; |
443
|
|
|
return $this->$parse_method( $body ); |
444
|
|
|
} |
445
|
|
|
|
446
|
|
|
/** |
447
|
|
|
* Parses a json response body. |
448
|
|
|
* |
449
|
|
|
* @since 3.0.0 |
450
|
|
|
* @access private |
451
|
|
|
* |
452
|
|
|
* @param string $response_body |
453
|
|
|
* @return object|false |
454
|
|
|
*/ |
455
|
|
|
private function _parse_json( $response_body ) { |
456
|
|
|
$data = json_decode( trim( $response_body ) ); |
457
|
|
|
return ( $data && is_object( $data ) ) ? $data : false; |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
/** |
461
|
|
|
* Parses an XML response body. |
462
|
|
|
* |
463
|
|
|
* @since 3.0.0 |
464
|
|
|
* @access private |
465
|
|
|
* |
466
|
|
|
* @param string $response_body |
467
|
|
|
* @return object|false |
468
|
|
|
*/ |
469
|
|
|
private function _parse_xml( $response_body ) { |
470
|
|
|
if ( ! function_exists( 'libxml_disable_entity_loader' ) ) |
471
|
|
|
return false; |
472
|
|
|
|
473
|
|
|
$loader = libxml_disable_entity_loader( true ); |
474
|
|
|
$errors = libxml_use_internal_errors( true ); |
475
|
|
|
|
476
|
|
|
$return = $this->_parse_xml_body( $response_body ); |
477
|
|
|
|
478
|
|
|
libxml_use_internal_errors( $errors ); |
479
|
|
|
libxml_disable_entity_loader( $loader ); |
480
|
|
|
|
481
|
|
|
return $return; |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
/** |
485
|
|
|
* Helper function for parsing an XML response body. |
486
|
|
|
* |
487
|
|
|
* @since 3.6.0 |
488
|
|
|
* @access private |
489
|
|
|
* |
490
|
|
|
* @param string $response_body |
491
|
|
|
* @return object|false |
492
|
|
|
*/ |
493
|
|
|
private function _parse_xml_body( $response_body ) { |
494
|
|
|
if ( ! function_exists( 'simplexml_import_dom' ) || ! class_exists( 'DOMDocument', false ) ) |
495
|
|
|
return false; |
496
|
|
|
|
497
|
|
|
$dom = new DOMDocument; |
498
|
|
|
$success = $dom->loadXML( $response_body ); |
499
|
|
|
if ( ! $success ) |
500
|
|
|
return false; |
501
|
|
|
|
502
|
|
|
if ( isset( $dom->doctype ) ) |
503
|
|
|
return false; |
504
|
|
|
|
505
|
|
|
foreach ( $dom->childNodes as $child ) { |
506
|
|
|
if ( XML_DOCUMENT_TYPE_NODE === $child->nodeType ) |
507
|
|
|
return false; |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
$xml = simplexml_import_dom( $dom ); |
511
|
|
|
if ( ! $xml ) |
512
|
|
|
return false; |
513
|
|
|
|
514
|
|
|
$return = new stdClass; |
515
|
|
|
foreach ( $xml as $key => $value ) { |
516
|
|
|
$return->$key = (string) $value; |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
return $return; |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
/** |
523
|
|
|
* Converts a data object from {@link WP_oEmbed::fetch()} and returns the HTML. |
524
|
|
|
* |
525
|
|
|
* @param object $data A data object result from an oEmbed provider. |
526
|
|
|
* @param string $url The URL to the content that is desired to be embedded. |
527
|
|
|
* @return false|string False on error, otherwise the HTML needed to embed. |
528
|
|
|
*/ |
529
|
|
|
public function data2html( $data, $url ) { |
530
|
|
|
if ( ! is_object( $data ) || empty( $data->type ) ) |
531
|
|
|
return false; |
532
|
|
|
|
533
|
|
|
$return = false; |
534
|
|
|
|
535
|
|
|
switch ( $data->type ) { |
536
|
|
|
case 'photo': |
537
|
|
|
if ( empty( $data->url ) || empty( $data->width ) || empty( $data->height ) ) |
538
|
|
|
break; |
539
|
|
|
if ( ! is_string( $data->url ) || ! is_numeric( $data->width ) || ! is_numeric( $data->height ) ) |
540
|
|
|
break; |
541
|
|
|
|
542
|
|
|
$title = ! empty( $data->title ) && is_string( $data->title ) ? $data->title : ''; |
543
|
|
|
$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>'; |
544
|
|
|
break; |
545
|
|
|
|
546
|
|
|
case 'video': |
547
|
|
|
case 'rich': |
548
|
|
|
if ( ! empty( $data->html ) && is_string( $data->html ) ) |
549
|
|
|
$return = $data->html; |
550
|
|
|
break; |
551
|
|
|
|
552
|
|
|
case 'link': |
553
|
|
|
if ( ! empty( $data->title ) && is_string( $data->title ) ) |
554
|
|
|
$return = '<a href="' . esc_url( $url ) . '">' . esc_html( $data->title ) . '</a>'; |
555
|
|
|
break; |
556
|
|
|
|
557
|
|
|
default: |
558
|
|
|
$return = false; |
559
|
|
|
} |
560
|
|
|
|
561
|
|
|
/** |
562
|
|
|
* Filter the returned oEmbed HTML. |
563
|
|
|
* |
564
|
|
|
* Use this filter to add support for custom data types, or to filter the result. |
565
|
|
|
* |
566
|
|
|
* @since 2.9.0 |
567
|
|
|
* |
568
|
|
|
* @param string $return The returned oEmbed HTML. |
569
|
|
|
* @param object $data A data object result from an oEmbed provider. |
570
|
|
|
* @param string $url The URL of the content to be embedded. |
571
|
|
|
*/ |
572
|
|
|
return apply_filters( 'oembed_dataparse', $return, $data, $url ); |
573
|
|
|
} |
574
|
|
|
|
575
|
|
|
/** |
576
|
|
|
* Strip any new lines from the HTML. |
577
|
|
|
* |
578
|
|
|
* @access public |
579
|
|
|
* @param string $html Existing HTML. |
580
|
|
|
* @param object $data Data object from WP_oEmbed::data2html() |
581
|
|
|
* @param string $url The original URL passed to oEmbed. |
582
|
|
|
* @return string Possibly modified $html |
583
|
|
|
*/ |
584
|
|
|
public function _strip_newlines( $html, $data, $url ) { |
|
|
|
|
585
|
|
|
if ( false === strpos( $html, "\n" ) ) { |
586
|
|
|
return $html; |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
$count = 1; |
590
|
|
|
$found = array(); |
591
|
|
|
$token = '__PRE__'; |
592
|
|
|
$search = array( "\t", "\n", "\r", ' ' ); |
593
|
|
|
$replace = array( '__TAB__', '__NL__', '__CR__', '__SPACE__' ); |
594
|
|
|
$tokenized = str_replace( $search, $replace, $html ); |
595
|
|
|
|
596
|
|
|
preg_match_all( '#(<pre[^>]*>.+?</pre>)#i', $tokenized, $matches, PREG_SET_ORDER ); |
597
|
|
|
foreach ( $matches as $i => $match ) { |
|
|
|
|
598
|
|
|
$tag_html = str_replace( $replace, $search, $match[0] ); |
599
|
|
|
$tag_token = $token . $i; |
600
|
|
|
|
601
|
|
|
$found[ $tag_token ] = $tag_html; |
602
|
|
|
$html = str_replace( $tag_html, $tag_token, $html, $count ); |
603
|
|
|
} |
604
|
|
|
|
605
|
|
|
$replaced = str_replace( $replace, $search, $html ); |
606
|
|
|
$stripped = str_replace( array( "\r\n", "\n" ), '', $replaced ); |
607
|
|
|
$pre = array_values( $found ); |
608
|
|
|
$tokens = array_keys( $found ); |
609
|
|
|
|
610
|
|
|
return str_replace( $tokens, $pre, $stripped ); |
611
|
|
|
} |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
/** |
615
|
|
|
* Returns the initialized {@link WP_oEmbed} object |
616
|
|
|
* |
617
|
|
|
* @since 2.9.0 |
618
|
|
|
* @access private |
619
|
|
|
* |
620
|
|
|
* @staticvar WP_oEmbed $wp_oembed |
621
|
|
|
* |
622
|
|
|
* @return WP_oEmbed object. |
623
|
|
|
*/ |
624
|
|
|
function _wp_oembed_get_object() { |
625
|
|
|
static $wp_oembed = null; |
626
|
|
|
|
627
|
|
|
if ( is_null( $wp_oembed ) ) { |
628
|
|
|
$wp_oembed = new WP_oEmbed(); |
629
|
|
|
} |
630
|
|
|
return $wp_oembed; |
631
|
|
|
} |
632
|
|
|
|
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..