Issues (2010)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

wp-includes/embed.php (19 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * oEmbed API: Top-level oEmbed functionality
4
 *
5
 * @package WordPress
6
 * @subpackage oEmbed
7
 * @since 4.4.0
8
 */
9
10
/**
11
 * Registers an embed handler.
12
 *
13
 * Should probably only be used for sites that do not support oEmbed.
14
 *
15
 * @since 2.9.0
16
 *
17
 * @global WP_Embed $wp_embed
18
 *
19
 * @param string   $id       An internal ID/name for the handler. Needs to be unique.
20
 * @param string   $regex    The regex that will be used to see if this handler should be used for a URL.
21
 * @param callable $callback The callback function that will be called if the regex is matched.
22
 * @param int      $priority Optional. Used to specify the order in which the registered handlers will
23
 *                           be tested. Default 10.
24
 */
25
function wp_embed_register_handler( $id, $regex, $callback, $priority = 10 ) {
26
	global $wp_embed;
27
	$wp_embed->register_handler( $id, $regex, $callback, $priority );
28
}
29
30
/**
31
 * Unregisters a previously-registered embed handler.
32
 *
33
 * @since 2.9.0
34
 *
35
 * @global WP_Embed $wp_embed
36
 *
37
 * @param string $id       The handler ID that should be removed.
38
 * @param int    $priority Optional. The priority of the handler to be removed. Default 10.
39
 */
40
function wp_embed_unregister_handler( $id, $priority = 10 ) {
41
	global $wp_embed;
42
	$wp_embed->unregister_handler( $id, $priority );
43
}
44
45
/**
46
 * Creates default array of embed parameters.
47
 *
48
 * The width defaults to the content width as specified by the theme. If the
49
 * theme does not specify a content width, then 500px is used.
50
 *
51
 * The default height is 1.5 times the width, or 1000px, whichever is smaller.
52
 *
53
 * The {@see 'embed_defaults'} filter can be used to adjust either of these values.
54
 *
55
 * @since 2.9.0
56
 *
57
 * @global int $content_width
58
 *
59
 * @param string $url Optional. The URL that should be embedded. Default empty.
60
 *
61
 * @return array Default embed parameters.
62
 */
63
function wp_embed_defaults( $url = '' ) {
64
	if ( ! empty( $GLOBALS['content_width'] ) )
65
		$width = (int) $GLOBALS['content_width'];
66
67
	if ( empty( $width ) )
68
		$width = 500;
69
70
	$height = min( ceil( $width * 1.5 ), 1000 );
71
72
	/**
73
	 * Filters the default array of embed dimensions.
74
	 *
75
	 * @since 2.9.0
76
	 *
77
	 * @param array  $size An array of embed width and height values
78
	 *                     in pixels (in that order).
79
	 * @param string $url  The URL that should be embedded.
80
	 */
81
	return apply_filters( 'embed_defaults', compact( 'width', 'height' ), $url );
82
}
83
84
/**
85
 * Attempts to fetch the embed HTML for a provided URL using oEmbed.
86
 *
87
 * @since 2.9.0
88
 *
89
 * @see WP_oEmbed
90
 *
91
 * @param string $url  The URL that should be embedded.
92
 * @param array  $args Optional. Additional arguments and parameters for retrieving embed HTML.
93
 *                     Default empty.
94
 * @return false|string False on failure or the embed HTML on success.
95
 */
96
function wp_oembed_get( $url, $args = '' ) {
97
	require_once( ABSPATH . WPINC . '/class-oembed.php' );
98
	$oembed = _wp_oembed_get_object();
99
	return $oembed->get_html( $url, $args );
100
}
101
102
/**
103
 * Adds a URL format and oEmbed provider URL pair.
104
 *
105
 * @since 2.9.0
106
 *
107
 * @see WP_oEmbed
108
 *
109
 * @param string  $format   The format of URL that this provider can handle. You can use asterisks
110
 *                          as wildcards.
111
 * @param string  $provider The URL to the oEmbed provider.
112
 * @param boolean $regex    Optional. Whether the `$format` parameter is in a RegEx format. Default false.
113
 */
114
function wp_oembed_add_provider( $format, $provider, $regex = false ) {
115
	require_once( ABSPATH . WPINC . '/class-oembed.php' );
116
117
	if ( did_action( 'plugins_loaded' ) ) {
118
		$oembed = _wp_oembed_get_object();
119
		$oembed->providers[$format] = array( $provider, $regex );
120
	} else {
121
		WP_oEmbed::_add_provider_early( $format, $provider, $regex );
122
	}
123
}
124
125
/**
126
 * Removes an oEmbed provider.
127
 *
128
 * @since 3.5.0
129
 *
130
 * @see WP_oEmbed
131
 *
132
 * @param string $format The URL format for the oEmbed provider to remove.
133
 * @return bool Was the provider removed successfully?
134
 */
135
function wp_oembed_remove_provider( $format ) {
136
	require_once( ABSPATH . WPINC . '/class-oembed.php' );
137
138
	if ( did_action( 'plugins_loaded' ) ) {
139
		$oembed = _wp_oembed_get_object();
140
141
		if ( isset( $oembed->providers[ $format ] ) ) {
142
			unset( $oembed->providers[ $format ] );
143
			return true;
144
		}
145
	} else {
146
		WP_oEmbed::_remove_provider_early( $format );
147
	}
148
149
	return false;
150
}
151
152
/**
153
 * Determines if default embed handlers should be loaded.
154
 *
155
 * Checks to make sure that the embeds library hasn't already been loaded. If
156
 * it hasn't, then it will load the embeds library.
157
 *
158
 * @since 2.9.0
159
 *
160
 * @see wp_embed_register_handler()
161
 */
162
function wp_maybe_load_embeds() {
163
	/**
164
	 * Filters whether to load the default embed handlers.
165
	 *
166
	 * Returning a falsey value will prevent loading the default embed handlers.
167
	 *
168
	 * @since 2.9.0
169
	 *
170
	 * @param bool $maybe_load_embeds Whether to load the embeds library. Default true.
171
	 */
172
	if ( ! apply_filters( 'load_default_embeds', true ) ) {
173
		return;
174
	}
175
176
	wp_embed_register_handler( 'youtube_embed_url', '#https?://(www.)?youtube\.com/(?:v|embed)/([^/]+)#i', 'wp_embed_handler_youtube' );
177
178
	/**
179
	 * Filters the audio embed handler callback.
180
	 *
181
	 * @since 3.6.0
182
	 *
183
	 * @param callable $handler Audio embed handler callback function.
184
	 */
185
	wp_embed_register_handler( 'audio', '#^https?://.+?\.(' . join( '|', wp_get_audio_extensions() ) . ')$#i', apply_filters( 'wp_audio_embed_handler', 'wp_embed_handler_audio' ), 9999 );
0 ignored issues
show
apply_filters('wp_audio_...p_embed_handler_audio') is of type *, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
186
187
	/**
188
	 * Filters the video embed handler callback.
189
	 *
190
	 * @since 3.6.0
191
	 *
192
	 * @param callable $handler Video embed handler callback function.
193
	 */
194
	wp_embed_register_handler( 'video', '#^https?://.+?\.(' . join( '|', wp_get_video_extensions() ) . ')$#i', apply_filters( 'wp_video_embed_handler', 'wp_embed_handler_video' ), 9999 );
0 ignored issues
show
apply_filters('wp_video_...p_embed_handler_video') is of type *, but the function expects a callable.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
195
}
196
197
/**
198
 * YouTube iframe embed handler callback.
199
 *
200
 * Catches YouTube iframe embed URLs that are not parsable by oEmbed but can be translated into a URL that is.
201
 *
202
 * @since 4.0.0
203
 *
204
 * @global WP_Embed $wp_embed
205
 *
206
 * @param array  $matches The RegEx matches from the provided regex when calling
207
 *                        wp_embed_register_handler().
208
 * @param array  $attr    Embed attributes.
209
 * @param string $url     The original URL that was matched by the regex.
210
 * @param array  $rawattr The original unmodified attributes.
211
 * @return string The embed HTML.
212
 */
213
function wp_embed_handler_youtube( $matches, $attr, $url, $rawattr ) {
214
	global $wp_embed;
215
	$embed = $wp_embed->autoembed( "https://youtube.com/watch?v={$matches[2]}" );
216
217
	/**
218
	 * Filters the YoutTube embed output.
219
	 *
220
	 * @since 4.0.0
221
	 *
222
	 * @see wp_embed_handler_youtube()
223
	 *
224
	 * @param string $embed   YouTube embed output.
225
	 * @param array  $attr    An array of embed attributes.
226
	 * @param string $url     The original URL that was matched by the regex.
227
	 * @param array  $rawattr The original unmodified attributes.
228
	 */
229
	return apply_filters( 'wp_embed_handler_youtube', $embed, $attr, $url, $rawattr );
230
}
231
232
/**
233
 * Audio embed handler callback.
234
 *
235
 * @since 3.6.0
236
 *
237
 * @param array  $matches The RegEx matches from the provided regex when calling wp_embed_register_handler().
238
 * @param array  $attr Embed attributes.
239
 * @param string $url The original URL that was matched by the regex.
240
 * @param array  $rawattr The original unmodified attributes.
241
 * @return string The embed HTML.
242
 */
243
function wp_embed_handler_audio( $matches, $attr, $url, $rawattr ) {
244
	$audio = sprintf( '[audio src="%s" /]', esc_url( $url ) );
245
246
	/**
247
	 * Filters the audio embed output.
248
	 *
249
	 * @since 3.6.0
250
	 *
251
	 * @param string $audio   Audio embed output.
252
	 * @param array  $attr    An array of embed attributes.
253
	 * @param string $url     The original URL that was matched by the regex.
254
	 * @param array  $rawattr The original unmodified attributes.
255
	 */
256
	return apply_filters( 'wp_embed_handler_audio', $audio, $attr, $url, $rawattr );
257
}
258
259
/**
260
 * Video embed handler callback.
261
 *
262
 * @since 3.6.0
263
 *
264
 * @param array  $matches The RegEx matches from the provided regex when calling wp_embed_register_handler().
265
 * @param array  $attr    Embed attributes.
266
 * @param string $url     The original URL that was matched by the regex.
267
 * @param array  $rawattr The original unmodified attributes.
268
 * @return string The embed HTML.
269
 */
270
function wp_embed_handler_video( $matches, $attr, $url, $rawattr ) {
271
	$dimensions = '';
272
	if ( ! empty( $rawattr['width'] ) && ! empty( $rawattr['height'] ) ) {
273
		$dimensions .= sprintf( 'width="%d" ', (int) $rawattr['width'] );
274
		$dimensions .= sprintf( 'height="%d" ', (int) $rawattr['height'] );
275
	}
276
	$video = sprintf( '[video %s src="%s" /]', $dimensions, esc_url( $url ) );
277
278
	/**
279
	 * Filters the video embed output.
280
	 *
281
	 * @since 3.6.0
282
	 *
283
	 * @param string $video   Video embed output.
284
	 * @param array  $attr    An array of embed attributes.
285
	 * @param string $url     The original URL that was matched by the regex.
286
	 * @param array  $rawattr The original unmodified attributes.
287
	 */
288
	return apply_filters( 'wp_embed_handler_video', $video, $attr, $url, $rawattr );
289
}
290
291
/**
292
 * Registers the oEmbed REST API route.
293
 *
294
 * @since 4.4.0
295
 */
296
function wp_oembed_register_route() {
297
	$controller = new WP_oEmbed_Controller();
298
	$controller->register_routes();
299
}
300
301
/**
302
 * Adds oEmbed discovery links in the website <head>.
303
 *
304
 * @since 4.4.0
305
 */
306
function wp_oembed_add_discovery_links() {
307
	$output = '';
308
309
	if ( is_singular() ) {
310
		$output .= '<link rel="alternate" type="application/json+oembed" href="' . esc_url( get_oembed_endpoint_url( get_permalink() ) ) . '" />' . "\n";
0 ignored issues
show
It seems like get_permalink() can also be of type false; however, get_oembed_endpoint_url() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
311
312
		if ( class_exists( 'SimpleXMLElement' ) ) {
313
			$output .= '<link rel="alternate" type="text/xml+oembed" href="' . esc_url( get_oembed_endpoint_url( get_permalink(), 'xml' ) ) . '" />' . "\n";
0 ignored issues
show
It seems like get_permalink() can also be of type false; however, get_oembed_endpoint_url() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
314
		}
315
	}
316
317
	/**
318
	 * Filters the oEmbed discovery links HTML.
319
	 *
320
	 * @since 4.4.0
321
	 *
322
	 * @param string $output HTML of the discovery links.
323
	 */
324
	echo apply_filters( 'oembed_discovery_links', $output );
325
}
326
327
/**
328
 * Adds the necessary JavaScript to communicate with the embedded iframes.
329
 *
330
 * @since 4.4.0
331
 */
332
function wp_oembed_add_host_js() {
333
	wp_enqueue_script( 'wp-embed' );
334
}
335
336
/**
337
 * Retrieves the URL to embed a specific post in an iframe.
338
 *
339
 * @since 4.4.0
340
 *
341
 * @param int|WP_Post $post Optional. Post ID or object. Defaults to the current post.
342
 * @return string|false The post embed URL on success, false if the post doesn't exist.
343
 */
344
function get_post_embed_url( $post = null ) {
345
	$post = get_post( $post );
346
347
	if ( ! $post ) {
348
		return false;
349
	}
350
351
	$embed_url     = trailingslashit( get_permalink( $post ) ) . user_trailingslashit( 'embed' );
0 ignored issues
show
$post is of type object<WP_Post>|array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
It seems like get_permalink($post) targeting get_permalink() can also be of type false; however, trailingslashit() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
352
	$path_conflict = get_page_by_path( str_replace( home_url(), '', $embed_url ), OBJECT, get_post_types( array( 'public' => true ) ) );
353
354
	if ( ! get_option( 'permalink_structure' ) || $path_conflict ) {
355
		$embed_url = add_query_arg( array( 'embed' => 'true' ), get_permalink( $post ) );
0 ignored issues
show
$post is of type object<WP_Post>|array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
356
	}
357
358
	/**
359
	 * Filters the URL to embed a specific post.
360
	 *
361
	 * @since 4.4.0
362
	 *
363
	 * @param string  $embed_url The post embed URL.
364
	 * @param WP_Post $post      The corresponding post object.
365
	 */
366
	return esc_url_raw( apply_filters( 'post_embed_url', $embed_url, $post ) );
367
}
368
369
/**
370
 * Retrieves the oEmbed endpoint URL for a given permalink.
371
 *
372
 * Pass an empty string as the first argument to get the endpoint base URL.
373
 *
374
 * @since 4.4.0
375
 *
376
 * @param string $permalink Optional. The permalink used for the `url` query arg. Default empty.
377
 * @param string $format    Optional. The requested response format. Default 'json'.
378
 * @return string The oEmbed endpoint URL.
379
 */
380
function get_oembed_endpoint_url( $permalink = '', $format = 'json' ) {
381
	$url = rest_url( 'oembed/1.0/embed' );
382
383
	if ( 'json' === $format ) {
384
		$format = false;
385
	}
386
387
	if ( '' !== $permalink ) {
388
		$url = add_query_arg( array(
389
			'url'    => urlencode( $permalink ),
390
			'format' => $format,
391
		), $url );
392
	}
393
394
	/**
395
	 * Filters the oEmbed endpoint URL.
396
	 *
397
	 * @since 4.4.0
398
	 *
399
	 * @param string $url       The URL to the oEmbed endpoint.
400
	 * @param string $permalink The permalink used for the `url` query arg.
401
	 * @param string $format    The requested response format.
402
	 */
403
	return apply_filters( 'oembed_endpoint_url', $url, $permalink, $format );
404
}
405
406
/**
407
 * Retrieves the embed code for a specific post.
408
 *
409
 * @since 4.4.0
410
 *
411
 * @param int         $width  The width for the response.
412
 * @param int         $height The height for the response.
413
 * @param int|WP_Post $post   Optional. Post ID or object. Default is global `$post`.
414
 * @return string|false Embed code on success, false if post doesn't exist.
415
 */
416
function get_post_embed_html( $width, $height, $post = null ) {
417
	$post = get_post( $post );
418
419
	if ( ! $post ) {
420
		return false;
421
	}
422
423
	$embed_url = get_post_embed_url( $post );
0 ignored issues
show
It seems like $post defined by get_post($post) on line 417 can also be of type array; however, get_post_embed_url() does only seem to accept integer|object<WP_Post>|null, 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...
424
425
	$output = '<blockquote class="wp-embedded-content"><a href="' . esc_url( get_permalink( $post ) ) . '">' . get_the_title( $post ) . "</a></blockquote>\n";
0 ignored issues
show
$post is of type object<WP_Post>|array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
It seems like get_permalink($post) targeting get_permalink() can also be of type false; however, esc_url() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
426
427
	$output .= "<script type='text/javascript'>\n";
428
	$output .= "<!--//--><![CDATA[//><!--\n";
429
	if ( SCRIPT_DEBUG ) {
430
		$output .= file_get_contents( ABSPATH . WPINC . '/js/wp-embed.js' );
431
	} else {
432
		/*
433
		 * If you're looking at a src version of this file, you'll see an "include"
434
		 * statement below. This is used by the `grunt build` process to directly
435
		 * include a minified version of wp-embed.js, instead of using the
436
		 * file_get_contents() method from above.
437
		 *
438
		 * If you're looking at a build version of this file, you'll see a string of
439
		 * minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
440
		 * and edit wp-embed.js directly.
441
		 */
442
		$output .=<<<JS
443
		include "js/wp-embed.min.js"
444
JS;
445
	}
446
	$output .= "\n//--><!]]>";
447
	$output .= "\n</script>";
448
449
	$output .= sprintf(
450
		'<iframe sandbox="allow-scripts" security="restricted" src="%1$s" width="%2$d" height="%3$d" title="%4$s" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" class="wp-embedded-content"></iframe>',
451
		esc_url( $embed_url ),
0 ignored issues
show
It seems like $embed_url defined by get_post_embed_url($post) on line 423 can also be of type false; however, esc_url() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
452
		absint( $width ),
453
		absint( $height ),
454
		esc_attr(
455
			sprintf(
456
				/* translators: 1: post title, 2: site name */
457
				__( '&#8220;%1$s&#8221; &#8212; %2$s' ),
458
				get_the_title( $post ),
0 ignored issues
show
$post is of type object<WP_Post>|array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
459
				get_bloginfo( 'name' )
460
			)
461
		)
462
	);
463
464
	/**
465
	 * Filters the embed HTML output for a given post.
466
	 *
467
	 * @since 4.4.0
468
	 *
469
	 * @param string  $output The default HTML.
470
	 * @param WP_Post $post   Current post object.
471
	 * @param int     $width  Width of the response.
472
	 * @param int     $height Height of the response.
473
	 */
474
	return apply_filters( 'embed_html', $output, $post, $width, $height );
475
}
476
477
/**
478
 * Retrieves the oEmbed response data for a given post.
479
 *
480
 * @since 4.4.0
481
 *
482
 * @param WP_Post|int $post  Post object or ID.
483
 * @param int         $width The requested width.
484
 * @return array|false Response data on success, false if post doesn't exist.
485
 */
486
function get_oembed_response_data( $post, $width ) {
487
	$post  = get_post( $post );
488
	$width = absint( $width );
489
490
	if ( ! $post ) {
491
		return false;
492
	}
493
494
	if ( 'publish' !== get_post_status( $post ) ) {
0 ignored issues
show
It seems like $post defined by get_post($post) on line 487 can also be of type array; however, get_post_status() does only seem to accept string|integer|object<WP_Post>, 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...
495
		return false;
496
	}
497
498
	/**
499
	 * Filters the allowed minimum and maximum widths for the oEmbed response.
500
	 *
501
	 * @since 4.4.0
502
	 *
503
	 * @param array $min_max_width {
504
	 *     Minimum and maximum widths for the oEmbed response.
505
	 *
506
	 *     @type int $min Minimum width. Default 200.
507
	 *     @type int $max Maximum width. Default 600.
508
	 * }
509
	 */
510
	$min_max_width = apply_filters( 'oembed_min_max_width', array(
511
		'min' => 200,
512
		'max' => 600
513
	) );
514
515
	$width  = min( max( $min_max_width['min'], $width ), $min_max_width['max'] );
516
	$height = max( ceil( $width / 16 * 9 ), 200 );
517
518
	$data = array(
519
		'version'       => '1.0',
520
		'provider_name' => get_bloginfo( 'name' ),
521
		'provider_url'  => get_home_url(),
522
		'author_name'   => get_bloginfo( 'name' ),
523
		'author_url'    => get_home_url(),
524
		'title'         => $post->post_title,
525
		'type'          => 'link',
526
	);
527
528
	$author = get_userdata( $post->post_author );
529
530
	if ( $author ) {
531
		$data['author_name'] = $author->display_name;
532
		$data['author_url']  = get_author_posts_url( $author->ID );
533
	}
534
535
	/**
536
	 * Filters the oEmbed response data.
537
	 *
538
	 * @since 4.4.0
539
	 *
540
	 * @param array   $data   The response data.
541
	 * @param WP_Post $post   The post object.
542
	 * @param int     $width  The requested width.
543
	 * @param int     $height The calculated height.
544
	 */
545
	return apply_filters( 'oembed_response_data', $data, $post, $width, $height );
546
}
547
548
/**
549
 * Filters the oEmbed response data to return an iframe embed code.
550
 *
551
 * @since 4.4.0
552
 *
553
 * @param array   $data   The response data.
554
 * @param WP_Post $post   The post object.
555
 * @param int     $width  The requested width.
556
 * @param int     $height The calculated height.
557
 * @return array The modified response data.
558
 */
559
function get_oembed_response_data_rich( $data, $post, $width, $height ) {
560
	$data['width']  = absint( $width );
561
	$data['height'] = absint( $height );
562
	$data['type']   = 'rich';
563
	$data['html']   = get_post_embed_html( $width, $height, $post );
564
565
	// Add post thumbnail to response if available.
566
	$thumbnail_id = false;
567
568
	if ( has_post_thumbnail( $post->ID ) ) {
569
		$thumbnail_id = get_post_thumbnail_id( $post->ID );
570
	}
571
572
	if ( 'attachment' === get_post_type( $post ) ) {
573
		if ( wp_attachment_is_image( $post ) ) {
574
			$thumbnail_id = $post->ID;
575
		} else if ( wp_attachment_is( 'video', $post ) ) {
576
			$thumbnail_id = get_post_thumbnail_id( $post );
577
			$data['type'] = 'video';
578
		}
579
	}
580
581
	if ( $thumbnail_id ) {
582
		list( $thumbnail_url, $thumbnail_width, $thumbnail_height ) = wp_get_attachment_image_src( $thumbnail_id, array( $width, 99999 ) );
583
		$data['thumbnail_url']    = $thumbnail_url;
584
		$data['thumbnail_width']  = $thumbnail_width;
585
		$data['thumbnail_height'] = $thumbnail_height;
586
	}
587
588
	return $data;
589
}
590
591
/**
592
 * Ensures that the specified format is either 'json' or 'xml'.
593
 *
594
 * @since 4.4.0
595
 *
596
 * @param string $format The oEmbed response format. Accepts 'json' or 'xml'.
597
 * @return string The format, either 'xml' or 'json'. Default 'json'.
598
 */
599
function wp_oembed_ensure_format( $format ) {
600
	if ( ! in_array( $format, array( 'json', 'xml' ), true ) ) {
601
		return 'json';
602
	}
603
604
	return $format;
605
}
606
607
/**
608
 * Hooks into the REST API output to print XML instead of JSON.
609
 *
610
 * This is only done for the oEmbed API endpoint,
611
 * which supports both formats.
612
 *
613
 * @access private
614
 * @since 4.4.0
615
 *
616
 * @param bool                      $served  Whether the request has already been served.
617
 * @param WP_HTTP_ResponseInterface $result  Result to send to the client. Usually a WP_REST_Response.
618
 * @param WP_REST_Request           $request Request used to generate the response.
619
 * @param WP_REST_Server            $server  Server instance.
620
 * @return true
621
 */
622
function _oembed_rest_pre_serve_request( $served, $result, $request, $server ) {
623
	$params = $request->get_params();
624
625
	if ( '/oembed/1.0/embed' !== $request->get_route() || 'GET' !== $request->get_method() ) {
626
		return $served;
627
	}
628
629
	if ( ! isset( $params['format'] ) || 'xml' !== $params['format'] ) {
630
		return $served;
631
	}
632
633
	// Embed links inside the request.
634
	$data = $server->response_to_data( $result, false );
635
636
	if ( ! class_exists( 'SimpleXMLElement' ) ) {
637
		status_header( 501 );
638
		die( get_status_header_desc( 501 ) );
639
	}
640
641
	$result = _oembed_create_xml( $data );
642
643
	// Bail if there's no XML.
644
	if ( ! $result ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $result 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...
645
		status_header( 501 );
646
		return get_status_header_desc( 501 );
647
	}
648
649
	if ( ! headers_sent() ) {
650
		$server->send_header( 'Content-Type', 'text/xml; charset=' . get_option( 'blog_charset' ) );
651
	}
652
653
	echo $result;
654
655
	return true;
656
}
657
658
/**
659
 * Creates an XML string from a given array.
660
 *
661
 * @since 4.4.0
662
 * @access private
663
 *
664
 * @param array            $data The original oEmbed response data.
665
 * @param SimpleXMLElement $node Optional. XML node to append the result to recursively.
666
 * @return string|false XML string on success, false on error.
667
 */
668
function _oembed_create_xml( $data, $node = null ) {
669
	if ( ! is_array( $data ) || empty( $data ) ) {
670
		return false;
671
	}
672
673
	if ( null === $node ) {
674
		$node = new SimpleXMLElement( '<oembed></oembed>' );
675
	}
676
677
	foreach ( $data as $key => $value ) {
678
		if ( is_numeric( $key ) ) {
679
			$key = 'oembed';
680
		}
681
682
		if ( is_array( $value ) ) {
683
			$item = $node->addChild( $key );
684
			_oembed_create_xml( $value, $item );
685
		} else {
686
			$node->addChild( $key, esc_html( $value ) );
687
		}
688
	}
689
690
	return $node->asXML();
691
}
692
693
/**
694
 * Filters the given oEmbed HTML.
695
 *
696
 * If the `$url` isn't on the trusted providers list,
697
 * we need to filter the HTML heavily for security.
698
 *
699
 * Only filters 'rich' and 'html' response types.
700
 *
701
 * @since 4.4.0
702
 *
703
 * @param string $result The oEmbed HTML result.
704
 * @param object $data   A data object result from an oEmbed provider.
705
 * @param string $url    The URL of the content to be embedded.
706
 * @return string The filtered and sanitized oEmbed result.
707
 */
708
function wp_filter_oembed_result( $result, $data, $url ) {
709
	if ( false === $result || ! in_array( $data->type, array( 'rich', 'video' ) ) ) {
710
		return $result;
711
	}
712
713
	require_once( ABSPATH . WPINC . '/class-oembed.php' );
714
	$wp_oembed = _wp_oembed_get_object();
715
716
	// Don't modify the HTML for trusted providers.
717
	if ( false !== $wp_oembed->get_provider( $url, array( 'discover' => false ) ) ) {
718
		return $result;
719
	}
720
721
	$allowed_html = array(
722
		'a'          => array(
723
			'href'         => true,
724
		),
725
		'blockquote' => array(),
726
		'iframe'     => array(
727
			'src'          => true,
728
			'width'        => true,
729
			'height'       => true,
730
			'frameborder'  => true,
731
			'marginwidth'  => true,
732
			'marginheight' => true,
733
			'scrolling'    => true,
734
			'title'        => true,
735
		),
736
	);
737
738
	$html = wp_kses( $result, $allowed_html );
739
740
	preg_match( '|(<blockquote>.*?</blockquote>)?.*(<iframe.*?></iframe>)|ms', $html, $content );
741
	// We require at least the iframe to exist.
742
	if ( empty( $content[2] ) ) {
743
		return false;
744
	}
745
	$html = $content[1] . $content[2];
746
747
	if ( ! empty( $content[1] ) ) {
748
		// We have a blockquote to fall back on. Hide the iframe by default.
749
		$html = str_replace( '<iframe', '<iframe style="position: absolute; clip: rect(1px, 1px, 1px, 1px);"', $html );
750
		$html = str_replace( '<blockquote', '<blockquote class="wp-embedded-content"', $html );
751
	}
752
753
	$html = str_replace( '<iframe', '<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted"', $html );
754
755
	preg_match( '/ src=[\'"]([^\'"]*)[\'"]/', $html, $results );
756
757
	if ( ! empty( $results ) ) {
758
		$secret = wp_generate_password( 10, false );
759
760
		$url = esc_url( "{$results[1]}#?secret=$secret" );
761
762
		$html = str_replace( $results[0], " src=\"$url\" data-secret=\"$secret\"", $html );
763
		$html = str_replace( '<blockquote', "<blockquote data-secret=\"$secret\"", $html );
764
	}
765
766
	return $html;
767
}
768
769
/**
770
 * Filters the string in the 'more' link displayed after a trimmed excerpt.
771
 *
772
 * Replaces '[...]' (appended to automatically generated excerpts) with an
773
 * ellipsis and a "Continue reading" link in the embed template.
774
 *
775
 * @since 4.4.0
776
 *
777
 * @param string $more_string Default 'more' string.
778
 * @return string 'Continue reading' link prepended with an ellipsis.
779
 */
780
function wp_embed_excerpt_more( $more_string ) {
781
	if ( ! is_embed() ) {
782
		return $more_string;
783
	}
784
785
	$link = sprintf( '<a href="%1$s" class="wp-embed-more" target="_top">%2$s</a>',
786
		esc_url( get_permalink() ),
0 ignored issues
show
It seems like get_permalink() can also be of type false; however, esc_url() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
787
		/* translators: %s: Name of current post */
788
		sprintf( __( 'Continue reading %s' ), '<span class="screen-reader-text">' . get_the_title() . '</span>' )
789
	);
790
	return ' &hellip; ' . $link;
791
}
792
793
/**
794
 * Displays the post excerpt for the embed template.
795
 *
796
 * Intended to be used in 'The Loop'.
797
 *
798
 * @since 4.4.0
799
 */
800
function the_excerpt_embed() {
801
	$output = get_the_excerpt();
802
803
	/**
804
	 * Filters the post excerpt for the embed template.
805
	 *
806
	 * @since 4.4.0
807
	 *
808
	 * @param string $output The current post excerpt.
809
	 */
810
	echo apply_filters( 'the_excerpt_embed', $output );
811
}
812
813
/**
814
 * Filters the post excerpt for the embed template.
815
 *
816
 * Shows players for video and audio attachments.
817
 *
818
 * @since 4.4.0
819
 *
820
 * @param string $content The current post excerpt.
821
 * @return string The modified post excerpt.
822
 */
823
function wp_embed_excerpt_attachment( $content ) {
824
	if ( is_attachment() ) {
825
		return prepend_attachment( '' );
826
	}
827
828
	return $content;
829
}
830
831
/**
832
 * Enqueue embed iframe default CSS and JS & fire do_action('enqueue_embed_scripts')
833
 *
834
 * Enqueue PNG fallback CSS for embed iframe for legacy versions of IE.
835
 *
836
 * Allows plugins to queue scripts for the embed iframe end using wp_enqueue_script().
837
 * Runs first in oembed_head().
838
 *
839
 * @since 4.4.0
840
 */
841
function enqueue_embed_scripts() {
842
	wp_enqueue_style( 'wp-embed-template-ie' );
843
844
	/**
845
	 * Fires when scripts and styles are enqueued for the embed iframe.
846
	 *
847
	 * @since 4.4.0
848
	 */
849
	do_action( 'enqueue_embed_scripts' );
850
}
851
852
/**
853
 * Prints the CSS in the embed iframe header.
854
 *
855
 * @since 4.4.0
856
 */
857 View Code Duplication
function print_embed_styles() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
858
	?>
859
	<style type="text/css">
860
	<?php
861
		if ( SCRIPT_DEBUG ) {
862
			readfile( ABSPATH . WPINC . "/css/wp-embed-template.css" );
863
		} else {
864
			/*
865
			 * If you're looking at a src version of this file, you'll see an "include"
866
			 * statement below. This is used by the `grunt build` process to directly
867
			 * include a minified version of wp-oembed-embed.css, instead of using the
868
			 * readfile() method from above.
869
			 *
870
			 * If you're looking at a build version of this file, you'll see a string of
871
			 * minified CSS. If you need to debug it, please turn on SCRIPT_DEBUG
872
			 * and edit wp-embed-template.css directly.
873
			 */
874
			?>
875
			include "css/wp-embed-template.min.css"
876
			<?php
877
		}
878
	?>
879
	</style>
880
	<?php
881
}
882
883
/**
884
 * Prints the JavaScript in the embed iframe header.
885
 *
886
 * @since 4.4.0
887
 */
888 View Code Duplication
function print_embed_scripts() {
0 ignored issues
show
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
889
	?>
890
	<script type="text/javascript">
891
	<?php
892
		if ( SCRIPT_DEBUG ) {
893
			readfile( ABSPATH . WPINC . "/js/wp-embed-template.js" );
894
		} else {
895
			/*
896
			 * If you're looking at a src version of this file, you'll see an "include"
897
			 * statement below. This is used by the `grunt build` process to directly
898
			 * include a minified version of wp-embed-template.js, instead of using the
899
			 * readfile() method from above.
900
			 *
901
			 * If you're looking at a build version of this file, you'll see a string of
902
			 * minified JavaScript. If you need to debug it, please turn on SCRIPT_DEBUG
903
			 * and edit wp-embed-template.js directly.
904
			 */
905
			?>
906
			include "js/wp-embed-template.min.js"
907
			<?php
908
		}
909
	?>
910
	</script>
911
	<?php
912
}
913
914
/**
915
 * Prepare the oembed HTML to be displayed in an RSS feed.
916
 *
917
 * @since 4.4.0
918
 * @access private
919
 *
920
 * @param string $content The content to filter.
921
 * @return string The filtered content.
922
 */
923
function _oembed_filter_feed_content( $content ) {
924
	return str_replace( '<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" style="position: absolute; clip: rect(1px, 1px, 1px, 1px);"', '<iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted"', $content );
925
}
926
927
/**
928
 * Prints the necessary markup for the embed comments button.
929
 *
930
 * @since 4.4.0
931
 */
932
function print_embed_comments_button() {
933
	if ( is_404() || ! ( get_comments_number() || comments_open() ) ) {
934
		return;
935
	}
936
	?>
937
	<div class="wp-embed-comments">
938
		<a href="<?php comments_link(); ?>" target="_top">
939
			<span class="dashicons dashicons-admin-comments"></span>
940
			<?php
941
			printf(
942
				_n(
943
					'%s <span class="screen-reader-text">Comment</span>',
944
					'%s <span class="screen-reader-text">Comments</span>',
945
					get_comments_number()
946
				),
947
				number_format_i18n( get_comments_number() )
948
			);
949
			?>
950
		</a>
951
	</div>
952
	<?php
953
}
954
955
/**
956
 * Prints the necessary markup for the embed sharing button.
957
 *
958
 * @since 4.4.0
959
 */
960
function print_embed_sharing_button() {
961
	if ( is_404() ) {
962
		return;
963
	}
964
	?>
965
	<div class="wp-embed-share">
966
		<button type="button" class="wp-embed-share-dialog-open" aria-label="<?php esc_attr_e( 'Open sharing dialog' ); ?>">
967
			<span class="dashicons dashicons-share"></span>
968
		</button>
969
	</div>
970
	<?php
971
}
972
973
/**
974
 * Prints the necessary markup for the embed sharing dialog.
975
 *
976
 * @since 4.4.0
977
 */
978
function print_embed_sharing_dialog() {
979
	if ( is_404() ) {
980
		return;
981
	}
982
	?>
983
	<div class="wp-embed-share-dialog hidden" role="dialog" aria-label="<?php esc_attr_e( 'Sharing options' ); ?>">
984
		<div class="wp-embed-share-dialog-content">
985
			<div class="wp-embed-share-dialog-text">
986
				<ul class="wp-embed-share-tabs" role="tablist">
987
					<li class="wp-embed-share-tab-button wp-embed-share-tab-button-wordpress" role="presentation">
988
						<button type="button" role="tab" aria-controls="wp-embed-share-tab-wordpress" aria-selected="true" tabindex="0"><?php esc_html_e( 'WordPress Embed' ); ?></button>
989
					</li>
990
					<li class="wp-embed-share-tab-button wp-embed-share-tab-button-html" role="presentation">
991
						<button type="button" role="tab" aria-controls="wp-embed-share-tab-html" aria-selected="false" tabindex="-1"><?php esc_html_e( 'HTML Embed' ); ?></button>
992
					</li>
993
				</ul>
994
				<div id="wp-embed-share-tab-wordpress" class="wp-embed-share-tab" role="tabpanel" aria-hidden="false">
995
					<input type="text" value="<?php the_permalink(); ?>" class="wp-embed-share-input" aria-describedby="wp-embed-share-description-wordpress" tabindex="0" readonly/>
996
997
					<p class="wp-embed-share-description" id="wp-embed-share-description-wordpress">
998
						<?php _e( 'Copy and paste this URL into your WordPress site to embed' ); ?>
999
					</p>
1000
				</div>
1001
				<div id="wp-embed-share-tab-html" class="wp-embed-share-tab" role="tabpanel" aria-hidden="true">
1002
					<textarea class="wp-embed-share-input" aria-describedby="wp-embed-share-description-html" tabindex="0" readonly><?php echo esc_textarea( get_post_embed_html( 600, 400 ) ); ?></textarea>
0 ignored issues
show
It seems like get_post_embed_html(600, 400) targeting get_post_embed_html() can also be of type false; however, esc_textarea() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
1003
1004
					<p class="wp-embed-share-description" id="wp-embed-share-description-html">
1005
						<?php _e( 'Copy and paste this code into your site to embed' ); ?>
1006
					</p>
1007
				</div>
1008
			</div>
1009
1010
			<button type="button" class="wp-embed-share-dialog-close" aria-label="<?php esc_attr_e( 'Close sharing dialog' ); ?>">
1011
				<span class="dashicons dashicons-no"></span>
1012
			</button>
1013
		</div>
1014
	</div>
1015
	<?php
1016
}
1017
1018
/**
1019
 * Prints the necessary markup for the site title in an embed template.
1020
 *
1021
 * @since 4.5.0
1022
 */
1023
function the_embed_site_title() {
1024
	$site_title = sprintf(
1025
		'<a href="%s" target="_top"><img src="%s" srcset="%s 2x" width="32" height="32" alt="" class="wp-embed-site-icon"/><span>%s</span></a>',
1026
		esc_url( home_url() ),
1027
		esc_url( get_site_icon_url( 32, admin_url( 'images/w-logo-blue.png' ) ) ),
1028
		esc_url( get_site_icon_url( 64, admin_url( 'images/w-logo-blue.png' ) ) ),
1029
		esc_html( get_bloginfo( 'name' ) )
1030
	);
1031
1032
	$site_title = '<div class="wp-embed-site-title">' . $site_title . '</div>';
1033
1034
	/**
1035
	 * Filters the site title HTML in the embed footer.
1036
	 *
1037
	 * @since 4.4.0
1038
	 *
1039
	 * @param string $site_title The site title HTML.
1040
	 */
1041
	echo apply_filters( 'embed_site_title_html', $site_title );
1042
}
1043
1044
/**
1045
 * Filters the oEmbed result before any HTTP requests are made.
1046
 *
1047
 * If the URL belongs to the current site, the result is fetched directly instead of
1048
 * going through the oEmbed discovery process.
1049
 *
1050
 * @since 4.5.3
1051
 *
1052
 * @param null|string $result The UNSANITIZED (and potentially unsafe) HTML that should be used to embed. Default null.
1053
 * @param string      $url    The URL that should be inspected for discovery `<link>` tags.
1054
 * @param array       $args   oEmbed remote get arguments.
1055
 * @return null|string The UNSANITIZED (and potentially unsafe) HTML that should be used to embed.
1056
 *                     Null if the URL does not belong to the current site.
1057
 */
1058
function wp_filter_pre_oembed_result( $result, $url, $args ) {
1059
	$post_id = url_to_postid( $url );
1060
1061
	/** This filter is documented in wp-includes/class-wp-oembed-controller.php */
1062
	$post_id = apply_filters( 'oembed_request_post_id', $post_id, $url );
1063
1064
	if ( ! $post_id ) {
1065
		return $result;
1066
	}
1067
1068
	$width = isset( $args['width'] ) ? $args['width'] : 0;
1069
1070
	$data = get_oembed_response_data( $post_id, $width );
1071
	$data = _wp_oembed_get_object()->data2html( (object) $data, $url );
1072
1073
	if ( ! $data ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data 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...
1074
		return $result;
1075
	}
1076
1077
	return $data;
1078
}
1079