Completed
Push — master ( a7cd2a...eabd6c )
by Stephen
38:42
created

WP_oEmbed::_parse_json()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 4
rs 10
cc 3
eloc 3
nc 4
nop 1
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 );
0 ignored issues
show
Documentation Bug introduced by
It seems like apply_filters('oembed_providers', $providers) of type * is incompatible with the declared type array of property $providers.

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..

Loading history...
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 ) )
0 ignored issues
show
Bug Best Practice introduced by
The expression $provider of type false|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
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 ) ) {
0 ignored issues
show
Bug introduced by
It seems like $request defined by wp_safe_remote_get($url, $args) on line 331 can also be of type object<WP_Error>; however, wp_remote_retrieve_body() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
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 ) )
0 ignored issues
show
Bug introduced by
It seems like $response defined by wp_safe_remote_get($provider_url_with_args, $args) on line 437 can also be of type object<WP_Error>; however, wp_remote_retrieve_response_code() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
439
			return new WP_Error( 'not-implemented' );
440
		if ( ! $body = wp_remote_retrieve_body( $response ) )
0 ignored issues
show
Bug introduced by
It seems like $response defined by wp_safe_remote_get($provider_url_with_args, $args) on line 437 can also be of type object<WP_Error>; however, wp_remote_retrieve_body() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
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 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $url is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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 ) {
0 ignored issues
show
Bug introduced by
The expression $matches of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
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