Issues (4967)

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.

src/wp-includes/media.php (46 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
 * WordPress API for media display.
4
 *
5
 * @package WordPress
6
 * @subpackage Media
7
 */
8
9
/**
10
 * Retrieve additional image sizes.
11
 *
12
 * @since 4.7.0
13
 *
14
 * @global array $_wp_additional_image_sizes
15
 *
16
 * @return array Additional images size data.
17
 */
18
function wp_get_additional_image_sizes() {
19
	global $_wp_additional_image_sizes;
20
	if ( ! $_wp_additional_image_sizes ) {
21
		$_wp_additional_image_sizes = array();
22
	}
23
	return $_wp_additional_image_sizes;
24
}
25
26
/**
27
 * Scale down the default size of an image.
28
 *
29
 * This is so that the image is a better fit for the editor and theme.
30
 *
31
 * The `$size` parameter accepts either an array or a string. The supported string
32
 * values are 'thumb' or 'thumbnail' for the given thumbnail size or defaults at
33
 * 128 width and 96 height in pixels. Also supported for the string value is
34
 * 'medium', 'medium_large' and 'full'. The 'full' isn't actually supported, but any value other
35
 * than the supported will result in the content_width size or 500 if that is
36
 * not set.
37
 *
38
 * Finally, there is a filter named {@see 'editor_max_image_size'}, that will be
39
 * called on the calculated array for width and height, respectively. The second
40
 * parameter will be the value that was in the $size parameter. The returned
41
 * type for the hook is an array with the width as the first element and the
42
 * height as the second element.
43
 *
44
 * @since 2.5.0
45
 *
46
 * @global int   $content_width
47
 *
48
 * @param int          $width   Width of the image in pixels.
49
 * @param int          $height  Height of the image in pixels.
50
 * @param string|array $size    Optional. Image size. Accepts any valid image size, or an array
51
 *                              of width and height values in pixels (in that order).
52
 *                              Default 'medium'.
53
 * @param string       $context Optional. Could be 'display' (like in a theme) or 'edit'
54
 *                              (like inserting into an editor). Default null.
55
 * @return array Width and height of what the result image should resize to.
56
 */
57
function image_constrain_size_for_editor( $width, $height, $size = 'medium', $context = null ) {
58
	global $content_width;
59
60
	$_wp_additional_image_sizes = wp_get_additional_image_sizes();
61
62
	if ( ! $context )
0 ignored issues
show
Bug Best Practice introduced by
The expression $context of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null 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...
63
		$context = is_admin() ? 'edit' : 'display';
64
65
	if ( is_array($size) ) {
66
		$max_width = $size[0];
67
		$max_height = $size[1];
68
	}
69
	elseif ( $size == 'thumb' || $size == 'thumbnail' ) {
70
		$max_width = intval(get_option('thumbnail_size_w'));
71
		$max_height = intval(get_option('thumbnail_size_h'));
72
		// last chance thumbnail size defaults
73
		if ( !$max_width && !$max_height ) {
74
			$max_width = 128;
75
			$max_height = 96;
76
		}
77
	}
78
	elseif ( $size == 'medium' ) {
79
		$max_width = intval(get_option('medium_size_w'));
80
		$max_height = intval(get_option('medium_size_h'));
81
82
	}
83 View Code Duplication
	elseif ( $size == 'medium_large' ) {
84
		$max_width = intval( get_option( 'medium_large_size_w' ) );
85
		$max_height = intval( get_option( 'medium_large_size_h' ) );
86
87
		if ( intval( $content_width ) > 0 ) {
88
			$max_width = min( intval( $content_width ), $max_width );
89
		}
90
	}
91 View Code Duplication
	elseif ( $size == 'large' ) {
92
		/*
93
		 * We're inserting a large size image into the editor. If it's a really
94
		 * big image we'll scale it down to fit reasonably within the editor
95
		 * itself, and within the theme's content width if it's known. The user
96
		 * can resize it in the editor if they wish.
97
		 */
98
		$max_width = intval(get_option('large_size_w'));
99
		$max_height = intval(get_option('large_size_h'));
100
		if ( intval($content_width) > 0 ) {
101
			$max_width = min( intval($content_width), $max_width );
102
		}
103
	} elseif ( ! empty( $_wp_additional_image_sizes ) && in_array( $size, array_keys( $_wp_additional_image_sizes ) ) ) {
104
		$max_width = intval( $_wp_additional_image_sizes[$size]['width'] );
105
		$max_height = intval( $_wp_additional_image_sizes[$size]['height'] );
106
		// Only in admin. Assume that theme authors know what they're doing.
107
		if ( intval( $content_width ) > 0 && 'edit' === $context ) {
108
			$max_width = min( intval( $content_width ), $max_width );
109
		}
110
	}
111
	// $size == 'full' has no constraint
112
	else {
113
		$max_width = $width;
114
		$max_height = $height;
115
	}
116
117
	/**
118
	 * Filters the maximum image size dimensions for the editor.
119
	 *
120
	 * @since 2.5.0
121
	 *
122
	 * @param array        $max_image_size An array with the width as the first element,
123
	 *                                     and the height as the second element.
124
	 * @param string|array $size           Size of what the result image should be.
125
	 * @param string       $context        The context the image is being resized for.
126
	 *                                     Possible values are 'display' (like in a theme)
127
	 *                                     or 'edit' (like inserting into an editor).
128
	 */
129
	list( $max_width, $max_height ) = apply_filters( 'editor_max_image_size', array( $max_width, $max_height ), $size, $context );
130
131
	return wp_constrain_dimensions( $width, $height, $max_width, $max_height );
132
}
133
134
/**
135
 * Retrieve width and height attributes using given width and height values.
136
 *
137
 * Both attributes are required in the sense that both parameters must have a
138
 * value, but are optional in that if you set them to false or null, then they
139
 * will not be added to the returned string.
140
 *
141
 * You can set the value using a string, but it will only take numeric values.
142
 * If you wish to put 'px' after the numbers, then it will be stripped out of
143
 * the return.
144
 *
145
 * @since 2.5.0
146
 *
147
 * @param int|string $width  Image width in pixels.
148
 * @param int|string $height Image height in pixels.
149
 * @return string HTML attributes for width and, or height.
150
 */
151
function image_hwstring( $width, $height ) {
152
	$out = '';
153
	if ($width)
154
		$out .= 'width="'.intval($width).'" ';
155
	if ($height)
156
		$out .= 'height="'.intval($height).'" ';
157
	return $out;
158
}
159
160
/**
161
 * Scale an image to fit a particular size (such as 'thumb' or 'medium').
162
 *
163
 * Array with image url, width, height, and whether is intermediate size, in
164
 * that order is returned on success is returned. $is_intermediate is true if
165
 * $url is a resized image, false if it is the original.
166
 *
167
 * The URL might be the original image, or it might be a resized version. This
168
 * function won't create a new resized copy, it will just return an already
169
 * resized one if it exists.
170
 *
171
 * A plugin may use the {@see 'image_downsize'} filter to hook into and offer image
172
 * resizing services for images. The hook must return an array with the same
173
 * elements that are returned in the function. The first element being the URL
174
 * to the new image that was resized.
175
 *
176
 * @since 2.5.0
177
 *
178
 * @param int          $id   Attachment ID for image.
179
 * @param array|string $size Optional. Image size to scale to. Accepts any valid image size,
180
 *                           or an array of width and height values in pixels (in that order).
181
 *                           Default 'medium'.
182
 * @return false|array Array containing the image URL, width, height, and boolean for whether
183
 *                     the image is an intermediate size. False on failure.
184
 */
185
function image_downsize( $id, $size = 'medium' ) {
186
	$is_image = wp_attachment_is_image( $id );
187
188
	/**
189
	 * Filters whether to preempt the output of image_downsize().
190
	 *
191
	 * Passing a truthy value to the filter will effectively short-circuit
192
	 * down-sizing the image, returning that value as output instead.
193
	 *
194
	 * @since 2.5.0
195
	 *
196
	 * @param bool         $downsize Whether to short-circuit the image downsize. Default false.
197
	 * @param int          $id       Attachment ID for image.
198
	 * @param array|string $size     Size of image. Image size or array of width and height values (in that order).
199
	 *                               Default 'medium'.
200
	 */
201
	if ( $out = apply_filters( 'image_downsize', false, $id, $size ) ) {
202
		return $out;
203
	}
204
205
	$img_url = wp_get_attachment_url($id);
206
	$meta = wp_get_attachment_metadata($id);
207
	$width = $height = 0;
208
	$is_intermediate = false;
209
	$img_url_basename = wp_basename($img_url);
0 ignored issues
show
It seems like $img_url defined by wp_get_attachment_url($id) on line 205 can also be of type false; however, wp_basename() 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...
210
211
	// If the file isn't an image, attempt to replace its URL with a rendered image from its meta.
212
	// Otherwise, a non-image type could be returned.
213
	if ( ! $is_image ) {
214
		if ( ! empty( $meta['sizes'] ) ) {
215
			$img_url = str_replace( $img_url_basename, $meta['sizes']['full']['file'], $img_url );
216
			$img_url_basename = $meta['sizes']['full']['file'];
217
			$width = $meta['sizes']['full']['width'];
218
			$height = $meta['sizes']['full']['height'];
219
		} else {
220
			return false;
221
		}
222
	}
223
224
	// try for a new style intermediate size
225
	if ( $intermediate = image_get_intermediate_size($id, $size) ) {
226
		$img_url = str_replace($img_url_basename, $intermediate['file'], $img_url);
227
		$width = $intermediate['width'];
228
		$height = $intermediate['height'];
229
		$is_intermediate = true;
230
	}
231
	elseif ( $size == 'thumbnail' ) {
232
		// fall back to the old thumbnail
233
		if ( ($thumb_file = wp_get_attachment_thumb_file($id)) && $info = getimagesize($thumb_file) ) {
234
			$img_url = str_replace($img_url_basename, wp_basename($thumb_file), $img_url);
235
			$width = $info[0];
236
			$height = $info[1];
237
			$is_intermediate = true;
238
		}
239
	}
240
	if ( !$width && !$height && isset( $meta['width'], $meta['height'] ) ) {
241
		// any other type: use the real image
242
		$width = $meta['width'];
243
		$height = $meta['height'];
244
	}
245
246
	if ( $img_url) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $img_url of type string|false is loosely compared to true; 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...
247
		// we have the actual image size, but might need to further constrain it if content_width is narrower
248
		list( $width, $height ) = image_constrain_size_for_editor( $width, $height, $size );
249
250
		return array( $img_url, $width, $height, $is_intermediate );
251
	}
252
	return false;
253
254
}
255
256
/**
257
 * Register a new image size.
258
 *
259
 * Cropping behavior for the image size is dependent on the value of $crop:
260
 * 1. If false (default), images will be scaled, not cropped.
261
 * 2. If an array in the form of array( x_crop_position, y_crop_position ):
262
 *    - x_crop_position accepts 'left' 'center', or 'right'.
263
 *    - y_crop_position accepts 'top', 'center', or 'bottom'.
264
 *    Images will be cropped to the specified dimensions within the defined crop area.
265
 * 3. If true, images will be cropped to the specified dimensions using center positions.
266
 *
267
 * @since 2.9.0
268
 *
269
 * @global array $_wp_additional_image_sizes Associative array of additional image sizes.
270
 *
271
 * @param string     $name   Image size identifier.
272
 * @param int        $width  Image width in pixels.
273
 * @param int        $height Image height in pixels.
274
 * @param bool|array $crop   Optional. Whether to crop images to specified width and height or resize.
275
 *                           An array can specify positioning of the crop area. Default false.
276
 */
277
function add_image_size( $name, $width = 0, $height = 0, $crop = false ) {
278
	global $_wp_additional_image_sizes;
279
280
	$_wp_additional_image_sizes[ $name ] = array(
281
		'width'  => absint( $width ),
282
		'height' => absint( $height ),
283
		'crop'   => $crop,
284
	);
285
}
286
287
/**
288
 * Check if an image size exists.
289
 *
290
 * @since 3.9.0
291
 *
292
 * @param string $name The image size to check.
293
 * @return bool True if the image size exists, false if not.
294
 */
295
function has_image_size( $name ) {
296
	$sizes = wp_get_additional_image_sizes();
297
	return isset( $sizes[ $name ] );
298
}
299
300
/**
301
 * Remove a new image size.
302
 *
303
 * @since 3.9.0
304
 *
305
 * @global array $_wp_additional_image_sizes
306
 *
307
 * @param string $name The image size to remove.
308
 * @return bool True if the image size was successfully removed, false on failure.
309
 */
310
function remove_image_size( $name ) {
311
	global $_wp_additional_image_sizes;
312
313
	if ( isset( $_wp_additional_image_sizes[ $name ] ) ) {
314
		unset( $_wp_additional_image_sizes[ $name ] );
315
		return true;
316
	}
317
318
	return false;
319
}
320
321
/**
322
 * Registers an image size for the post thumbnail.
323
 *
324
 * @since 2.9.0
325
 *
326
 * @see add_image_size() for details on cropping behavior.
327
 *
328
 * @param int        $width  Image width in pixels.
329
 * @param int        $height Image height in pixels.
330
 * @param bool|array $crop   Optional. Whether to crop images to specified width and height or resize.
331
 *                           An array can specify positioning of the crop area. Default false.
332
 */
333
function set_post_thumbnail_size( $width = 0, $height = 0, $crop = false ) {
334
	add_image_size( 'post-thumbnail', $width, $height, $crop );
335
}
336
337
/**
338
 * Gets an img tag for an image attachment, scaling it down if requested.
339
 *
340
 * The {@see 'get_image_tag_class'} filter allows for changing the class name for the
341
 * image without having to use regular expressions on the HTML content. The
342
 * parameters are: what WordPress will use for the class, the Attachment ID,
343
 * image align value, and the size the image should be.
344
 *
345
 * The second filter, {@see 'get_image_tag'}, has the HTML content, which can then be
346
 * further manipulated by a plugin to change all attribute values and even HTML
347
 * content.
348
 *
349
 * @since 2.5.0
350
 *
351
 * @param int          $id    Attachment ID.
352
 * @param string       $alt   Image Description for the alt attribute.
353
 * @param string       $title Image Description for the title attribute.
354
 * @param string       $align Part of the class name for aligning the image.
355
 * @param string|array $size  Optional. Registered image size to retrieve a tag for. Accepts any
356
 *                            valid image size, or an array of width and height values in pixels
357
 *                            (in that order). Default 'medium'.
358
 * @return string HTML IMG element for given image attachment
359
 */
360
function get_image_tag( $id, $alt, $title, $align, $size = 'medium' ) {
361
362
	list( $img_src, $width, $height ) = image_downsize($id, $size);
363
	$hwstring = image_hwstring($width, $height);
364
365
	$title = $title ? 'title="' . esc_attr( $title ) . '" ' : '';
366
367
	$class = 'align' . esc_attr($align) .' size-' . esc_attr($size) . ' wp-image-' . $id;
0 ignored issues
show
It seems like $size defined by parameter $size on line 360 can also be of type array; however, esc_attr() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
368
369
	/**
370
	 * Filters the value of the attachment's image tag class attribute.
371
	 *
372
	 * @since 2.6.0
373
	 *
374
	 * @param string       $class CSS class name or space-separated list of classes.
375
	 * @param int          $id    Attachment ID.
376
	 * @param string       $align Part of the class name for aligning the image.
377
	 * @param string|array $size  Size of image. Image size or array of width and height values (in that order).
378
	 *                            Default 'medium'.
379
	 */
380
	$class = apply_filters( 'get_image_tag_class', $class, $id, $align, $size );
381
382
	$html = '<img src="' . esc_attr($img_src) . '" alt="' . esc_attr($alt) . '" ' . $title . $hwstring . 'class="' . $class . '" />';
383
384
	/**
385
	 * Filters the HTML content for the image tag.
386
	 *
387
	 * @since 2.6.0
388
	 *
389
	 * @param string       $html  HTML content for the image.
390
	 * @param int          $id    Attachment ID.
391
	 * @param string       $alt   Alternate text.
392
	 * @param string       $title Attachment title.
393
	 * @param string       $align Part of the class name for aligning the image.
394
	 * @param string|array $size  Size of image. Image size or array of width and height values (in that order).
395
	 *                            Default 'medium'.
396
	 */
397
	return apply_filters( 'get_image_tag', $html, $id, $alt, $title, $align, $size );
398
}
399
400
/**
401
 * Calculates the new dimensions for a down-sampled image.
402
 *
403
 * If either width or height are empty, no constraint is applied on
404
 * that dimension.
405
 *
406
 * @since 2.5.0
407
 *
408
 * @param int $current_width  Current width of the image.
409
 * @param int $current_height Current height of the image.
410
 * @param int $max_width      Optional. Max width in pixels to constrain to. Default 0.
411
 * @param int $max_height     Optional. Max height in pixels to constrain to. Default 0.
412
 * @return array First item is the width, the second item is the height.
413
 */
414
function wp_constrain_dimensions( $current_width, $current_height, $max_width = 0, $max_height = 0 ) {
415
	if ( !$max_width && !$max_height )
416
		return array( $current_width, $current_height );
417
418
	$width_ratio = $height_ratio = 1.0;
419
	$did_width = $did_height = false;
420
421
	if ( $max_width > 0 && $current_width > 0 && $current_width > $max_width ) {
422
		$width_ratio = $max_width / $current_width;
423
		$did_width = true;
424
	}
425
426
	if ( $max_height > 0 && $current_height > 0 && $current_height > $max_height ) {
427
		$height_ratio = $max_height / $current_height;
428
		$did_height = true;
429
	}
430
431
	// Calculate the larger/smaller ratios
432
	$smaller_ratio = min( $width_ratio, $height_ratio );
433
	$larger_ratio  = max( $width_ratio, $height_ratio );
434
435
	if ( (int) round( $current_width * $larger_ratio ) > $max_width || (int) round( $current_height * $larger_ratio ) > $max_height ) {
436
 		// The larger ratio is too big. It would result in an overflow.
437
		$ratio = $smaller_ratio;
438
	} else {
439
		// The larger ratio fits, and is likely to be a more "snug" fit.
440
		$ratio = $larger_ratio;
441
	}
442
443
	// Very small dimensions may result in 0, 1 should be the minimum.
444
	$w = max ( 1, (int) round( $current_width  * $ratio ) );
445
	$h = max ( 1, (int) round( $current_height * $ratio ) );
446
447
	// Sometimes, due to rounding, we'll end up with a result like this: 465x700 in a 177x177 box is 117x176... a pixel short
448
	// We also have issues with recursive calls resulting in an ever-changing result. Constraining to the result of a constraint should yield the original result.
449
	// Thus we look for dimensions that are one pixel shy of the max value and bump them up
450
451
	// Note: $did_width means it is possible $smaller_ratio == $width_ratio.
452
	if ( $did_width && $w == $max_width - 1 ) {
453
		$w = $max_width; // Round it up
454
	}
455
456
	// Note: $did_height means it is possible $smaller_ratio == $height_ratio.
457
	if ( $did_height && $h == $max_height - 1 ) {
458
		$h = $max_height; // Round it up
459
	}
460
461
	/**
462
	 * Filters dimensions to constrain down-sampled images to.
463
	 *
464
	 * @since 4.1.0
465
	 *
466
	 * @param array $dimensions     The image width and height.
467
	 * @param int 	$current_width  The current width of the image.
468
	 * @param int 	$current_height The current height of the image.
469
	 * @param int 	$max_width      The maximum width permitted.
470
	 * @param int 	$max_height     The maximum height permitted.
471
	 */
472
	return apply_filters( 'wp_constrain_dimensions', array( $w, $h ), $current_width, $current_height, $max_width, $max_height );
473
}
474
475
/**
476
 * Retrieves calculated resize dimensions for use in WP_Image_Editor.
477
 *
478
 * Calculates dimensions and coordinates for a resized image that fits
479
 * within a specified width and height.
480
 *
481
 * Cropping behavior is dependent on the value of $crop:
482
 * 1. If false (default), images will not be cropped.
483
 * 2. If an array in the form of array( x_crop_position, y_crop_position ):
484
 *    - x_crop_position accepts 'left' 'center', or 'right'.
485
 *    - y_crop_position accepts 'top', 'center', or 'bottom'.
486
 *    Images will be cropped to the specified dimensions within the defined crop area.
487
 * 3. If true, images will be cropped to the specified dimensions using center positions.
488
 *
489
 * @since 2.5.0
490
 *
491
 * @param int        $orig_w Original width in pixels.
492
 * @param int        $orig_h Original height in pixels.
493
 * @param int        $dest_w New width in pixels.
494
 * @param int        $dest_h New height in pixels.
495
 * @param bool|array $crop   Optional. Whether to crop image to specified width and height or resize.
496
 *                           An array can specify positioning of the crop area. Default false.
497
 * @return false|array False on failure. Returned array matches parameters for `imagecopyresampled()`.
498
 */
499
function image_resize_dimensions( $orig_w, $orig_h, $dest_w, $dest_h, $crop = false ) {
500
501
	if ($orig_w <= 0 || $orig_h <= 0)
502
		return false;
503
	// at least one of dest_w or dest_h must be specific
504
	if ($dest_w <= 0 && $dest_h <= 0)
505
		return false;
506
507
	/**
508
	 * Filters whether to preempt calculating the image resize dimensions.
509
	 *
510
	 * Passing a non-null value to the filter will effectively short-circuit
511
	 * image_resize_dimensions(), returning that value instead.
512
	 *
513
	 * @since 3.4.0
514
	 *
515
	 * @param null|mixed $null   Whether to preempt output of the resize dimensions.
516
	 * @param int        $orig_w Original width in pixels.
517
	 * @param int        $orig_h Original height in pixels.
518
	 * @param int        $dest_w New width in pixels.
519
	 * @param int        $dest_h New height in pixels.
520
	 * @param bool|array $crop   Whether to crop image to specified width and height or resize.
521
	 *                           An array can specify positioning of the crop area. Default false.
522
	 */
523
	$output = apply_filters( 'image_resize_dimensions', null, $orig_w, $orig_h, $dest_w, $dest_h, $crop );
524
	if ( null !== $output )
525
		return $output;
526
527
	if ( $crop ) {
528
		// crop the largest possible portion of the original image that we can size to $dest_w x $dest_h
529
		$aspect_ratio = $orig_w / $orig_h;
530
		$new_w = min($dest_w, $orig_w);
531
		$new_h = min($dest_h, $orig_h);
532
533
		if ( ! $new_w ) {
534
			$new_w = (int) round( $new_h * $aspect_ratio );
535
		}
536
537
		if ( ! $new_h ) {
538
			$new_h = (int) round( $new_w / $aspect_ratio );
539
		}
540
541
		$size_ratio = max($new_w / $orig_w, $new_h / $orig_h);
542
543
		$crop_w = round($new_w / $size_ratio);
544
		$crop_h = round($new_h / $size_ratio);
545
546
		if ( ! is_array( $crop ) || count( $crop ) !== 2 ) {
547
			$crop = array( 'center', 'center' );
548
		}
549
550
		list( $x, $y ) = $crop;
551
552
		if ( 'left' === $x ) {
553
			$s_x = 0;
554
		} elseif ( 'right' === $x ) {
555
			$s_x = $orig_w - $crop_w;
556
		} else {
557
			$s_x = floor( ( $orig_w - $crop_w ) / 2 );
558
		}
559
560
		if ( 'top' === $y ) {
561
			$s_y = 0;
562
		} elseif ( 'bottom' === $y ) {
563
			$s_y = $orig_h - $crop_h;
564
		} else {
565
			$s_y = floor( ( $orig_h - $crop_h ) / 2 );
566
		}
567
	} else {
568
		// don't crop, just resize using $dest_w x $dest_h as a maximum bounding box
569
		$crop_w = $orig_w;
570
		$crop_h = $orig_h;
571
572
		$s_x = 0;
573
		$s_y = 0;
574
575
		list( $new_w, $new_h ) = wp_constrain_dimensions( $orig_w, $orig_h, $dest_w, $dest_h );
576
	}
577
578
	// if the resulting image would be the same size or larger we don't want to resize it
579
	if ( $new_w >= $orig_w && $new_h >= $orig_h && $dest_w != $orig_w && $dest_h != $orig_h ) {
580
		return false;
581
	}
582
583
	// the return array matches the parameters to imagecopyresampled()
584
	// int dst_x, int dst_y, int src_x, int src_y, int dst_w, int dst_h, int src_w, int src_h
585
	return array( 0, 0, (int) $s_x, (int) $s_y, (int) $new_w, (int) $new_h, (int) $crop_w, (int) $crop_h );
586
587
}
588
589
/**
590
 * Resizes an image to make a thumbnail or intermediate size.
591
 *
592
 * The returned array has the file size, the image width, and image height. The
593
 * {@see 'image_make_intermediate_size'} filter can be used to hook in and change the
594
 * values of the returned array. The only parameter is the resized file path.
595
 *
596
 * @since 2.5.0
597
 *
598
 * @param string $file   File path.
599
 * @param int    $width  Image width.
600
 * @param int    $height Image height.
601
 * @param bool   $crop   Optional. Whether to crop image to specified width and height or resize.
602
 *                       Default false.
603
 * @return false|array False, if no image was created. Metadata array on success.
604
 */
605
function image_make_intermediate_size( $file, $width, $height, $crop = false ) {
606
	if ( $width || $height ) {
607
		$editor = wp_get_image_editor( $file );
608
609
		if ( is_wp_error( $editor ) || is_wp_error( $editor->resize( $width, $height, $crop ) ) )
0 ignored issues
show
The method resize does only exist in WP_Image_Editor, but not in WP_Error.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
610
			return false;
611
612
		$resized_file = $editor->save();
0 ignored issues
show
The method save does only exist in WP_Image_Editor, but not in WP_Error.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
613
614
		if ( ! is_wp_error( $resized_file ) && $resized_file ) {
615
			unset( $resized_file['path'] );
616
			return $resized_file;
617
		}
618
	}
619
	return false;
620
}
621
622
/**
623
 * Helper function to test if aspect ratios for two images match.
624
 *
625
 * @since 4.6.0
626
 *
627
 * @param int $source_width  Width of the first image in pixels.
628
 * @param int $source_height Height of the first image in pixels.
629
 * @param int $target_width  Width of the second image in pixels.
630
 * @param int $target_height Height of the second image in pixels.
631
 * @return bool True if aspect ratios match within 1px. False if not.
632
 */
633
function wp_image_matches_ratio( $source_width, $source_height, $target_width, $target_height ) {
634
	/*
635
	 * To test for varying crops, we constrain the dimensions of the larger image
636
	 * to the dimensions of the smaller image and see if they match.
637
	 */
638
	if ( $source_width > $target_width ) {
639
		$constrained_size = wp_constrain_dimensions( $source_width, $source_height, $target_width );
640
		$expected_size = array( $target_width, $target_height );
641
	} else {
642
		$constrained_size = wp_constrain_dimensions( $target_width, $target_height, $source_width );
643
		$expected_size = array( $source_width, $source_height );
644
	}
645
646
	// If the image dimensions are within 1px of the expected size, we consider it a match.
647
	$matched = ( abs( $constrained_size[0] - $expected_size[0] ) <= 1 && abs( $constrained_size[1] - $expected_size[1] ) <= 1 );
648
649
	return $matched;
650
}
651
652
/**
653
 * Retrieves the image's intermediate size (resized) path, width, and height.
654
 *
655
 * The $size parameter can be an array with the width and height respectively.
656
 * If the size matches the 'sizes' metadata array for width and height, then it
657
 * will be used. If there is no direct match, then the nearest image size larger
658
 * than the specified size will be used. If nothing is found, then the function
659
 * will break out and return false.
660
 *
661
 * The metadata 'sizes' is used for compatible sizes that can be used for the
662
 * parameter $size value.
663
 *
664
 * The url path will be given, when the $size parameter is a string.
665
 *
666
 * If you are passing an array for the $size, you should consider using
667
 * add_image_size() so that a cropped version is generated. It's much more
668
 * efficient than having to find the closest-sized image and then having the
669
 * browser scale down the image.
670
 *
671
 * @since 2.5.0
672
 *
673
 * @param int          $post_id Attachment ID.
674
 * @param array|string $size    Optional. Image size. Accepts any valid image size, or an array
675
 *                              of width and height values in pixels (in that order).
676
 *                              Default 'thumbnail'.
677
 * @return false|array $data {
678
 *     Array of file relative path, width, and height on success. Additionally includes absolute
679
 *     path and URL if registered size is passed to $size parameter. False on failure.
680
 *
681
 *     @type string $file   Image's path relative to uploads directory
682
 *     @type int    $width  Width of image
683
 *     @type int    $height Height of image
684
 *     @type string $path   Image's absolute filesystem path.
685
 *     @type string $url    Image's URL.
686
 * }
687
 */
688
function image_get_intermediate_size( $post_id, $size = 'thumbnail' ) {
689
	if ( ! $size || ! is_array( $imagedata = wp_get_attachment_metadata( $post_id ) ) || empty( $imagedata['sizes'] )  ) {
690
		return false;
691
	}
692
693
	$data = array();
694
695
	// Find the best match when '$size' is an array.
696
	if ( is_array( $size ) ) {
697
		$candidates = array();
698
699 View Code Duplication
		if ( ! isset( $imagedata['file'] ) && isset( $imagedata['sizes']['full'] ) ) {
700
			$imagedata['height'] = $imagedata['sizes']['full']['height'];
701
			$imagedata['width']  = $imagedata['sizes']['full']['width'];
702
		}
703
704
		foreach ( $imagedata['sizes'] as $_size => $data ) {
705
			// If there's an exact match to an existing image size, short circuit.
706
			if ( $data['width'] == $size[0] && $data['height'] == $size[1] ) {
707
				$candidates[ $data['width'] * $data['height'] ] = $data;
708
				break;
709
			}
710
711
			// If it's not an exact match, consider larger sizes with the same aspect ratio.
712
			if ( $data['width'] >= $size[0] && $data['height'] >= $size[1] ) {
713
				// If '0' is passed to either size, we test ratios against the original file.
714
				if ( 0 === $size[0] || 0 === $size[1] ) {
715
					$same_ratio = wp_image_matches_ratio( $data['width'], $data['height'], $imagedata['width'], $imagedata['height'] );
716
				} else {
717
					$same_ratio = wp_image_matches_ratio( $data['width'], $data['height'], $size[0], $size[1] );
718
				}
719
720
				if ( $same_ratio ) {
721
					$candidates[ $data['width'] * $data['height'] ] = $data;
722
				}
723
			}
724
		}
725
726
		if ( ! empty( $candidates ) ) {
727
			// Sort the array by size if we have more than one candidate.
728
			if ( 1 < count( $candidates ) ) {
729
				ksort( $candidates );
730
			}
731
732
			$data = array_shift( $candidates );
733
		/*
734
		 * When the size requested is smaller than the thumbnail dimensions, we
735
		 * fall back to the thumbnail size to maintain backwards compatibility with
736
		 * pre 4.6 versions of WordPress.
737
		 */
738
		} elseif ( ! empty( $imagedata['sizes']['thumbnail'] ) && $imagedata['sizes']['thumbnail']['width'] >= $size[0] && $imagedata['sizes']['thumbnail']['width'] >= $size[1] ) {
739
			$data = $imagedata['sizes']['thumbnail'];
740
		} else {
741
			return false;
742
		}
743
744
		// Constrain the width and height attributes to the requested values.
745
		list( $data['width'], $data['height'] ) = image_constrain_size_for_editor( $data['width'], $data['height'], $size );
746
747
	} elseif ( ! empty( $imagedata['sizes'][ $size ] ) ) {
748
		$data = $imagedata['sizes'][ $size ];
749
	}
750
751
	// If we still don't have a match at this point, return false.
752
	if ( empty( $data ) ) {
753
		return false;
754
	}
755
756
	// include the full filesystem path of the intermediate file
757
	if ( empty( $data['path'] ) && ! empty( $data['file'] ) && ! empty( $imagedata['file'] ) ) {
758
		$file_url = wp_get_attachment_url($post_id);
759
		$data['path'] = path_join( dirname($imagedata['file']), $data['file'] );
760
		$data['url'] = path_join( dirname($file_url), $data['file'] );
761
	}
762
763
	/**
764
	 * Filters the output of image_get_intermediate_size()
765
	 *
766
	 * @since 4.4.0
767
	 *
768
	 * @see image_get_intermediate_size()
769
	 *
770
	 * @param array        $data    Array of file relative path, width, and height on success. May also include
771
	 *                              file absolute path and URL.
772
	 * @param int          $post_id The post_id of the image attachment
773
	 * @param string|array $size    Registered image size or flat array of initially-requested height and width
774
	 *                              dimensions (in that order).
775
	 */
776
	return apply_filters( 'image_get_intermediate_size', $data, $post_id, $size );
777
}
778
779
/**
780
 * Gets the available intermediate image sizes.
781
 *
782
 * @since 3.0.0
783
 *
784
 * @return array Returns a filtered array of image size strings.
785
 */
786
function get_intermediate_image_sizes() {
787
	$_wp_additional_image_sizes = wp_get_additional_image_sizes();
788
	$image_sizes = array('thumbnail', 'medium', 'medium_large', 'large'); // Standard sizes
789
	if ( ! empty( $_wp_additional_image_sizes ) ) {
790
		$image_sizes = array_merge( $image_sizes, array_keys( $_wp_additional_image_sizes ) );
791
	}
792
793
	/**
794
	 * Filters the list of intermediate image sizes.
795
	 *
796
	 * @since 2.5.0
797
	 *
798
	 * @param array $image_sizes An array of intermediate image sizes. Defaults
799
	 *                           are 'thumbnail', 'medium', 'medium_large', 'large'.
800
	 */
801
	return apply_filters( 'intermediate_image_sizes', $image_sizes );
802
}
803
804
/**
805
 * Retrieve an image to represent an attachment.
806
 *
807
 * A mime icon for files, thumbnail or intermediate size for images.
808
 *
809
 * The returned array contains four values: the URL of the attachment image src,
810
 * the width of the image file, the height of the image file, and a boolean
811
 * representing whether the returned array describes an intermediate (generated)
812
 * image size or the original, full-sized upload.
813
 *
814
 * @since 2.5.0
815
 *
816
 * @param int          $attachment_id Image attachment ID.
817
 * @param string|array $size          Optional. Image size. Accepts any valid image size, or an array of width
818
 *                                    and height values in pixels (in that order). Default 'thumbnail'.
819
 * @param bool         $icon          Optional. Whether the image should be treated as an icon. Default false.
820
 * @return false|array Returns an array (url, width, height, is_intermediate), or false, if no image is available.
821
 */
822
function wp_get_attachment_image_src( $attachment_id, $size = 'thumbnail', $icon = false ) {
823
	// get a thumbnail or intermediate image if there is one
824
	$image = image_downsize( $attachment_id, $size );
825
	if ( ! $image ) {
826
		$src = false;
827
828
		if ( $icon && $src = wp_mime_type_icon( $attachment_id ) ) {
829
			/** This filter is documented in wp-includes/post.php */
830
			$icon_dir = apply_filters( 'icon_dir', ABSPATH . WPINC . '/images/media' );
831
832
			$src_file = $icon_dir . '/' . wp_basename( $src );
833
			@list( $width, $height ) = getimagesize( $src_file );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
834
		}
835
836
		if ( $src && $width && $height ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $src of type string|false is loosely compared to true; 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...
837
			$image = array( $src, $width, $height );
0 ignored issues
show
The variable $width does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
The variable $height does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
838
		}
839
	}
840
	/**
841
	 * Filters the image src result.
842
	 *
843
	 * @since 4.3.0
844
	 *
845
	 * @param array|false  $image         Either array with src, width & height, icon src, or false.
846
	 * @param int          $attachment_id Image attachment ID.
847
	 * @param string|array $size          Size of image. Image size or array of width and height values
848
	 *                                    (in that order). Default 'thumbnail'.
849
	 * @param bool         $icon          Whether the image should be treated as an icon. Default false.
850
	 */
851
	return apply_filters( 'wp_get_attachment_image_src', $image, $attachment_id, $size, $icon );
852
}
853
854
/**
855
 * Get an HTML img element representing an image attachment
856
 *
857
 * While `$size` will accept an array, it is better to register a size with
858
 * add_image_size() so that a cropped version is generated. It's much more
859
 * efficient than having to find the closest-sized image and then having the
860
 * browser scale down the image.
861
 *
862
 * @since 2.5.0
863
 *
864
 * @param int          $attachment_id Image attachment ID.
865
 * @param string|array $size          Optional. Image size. Accepts any valid image size, or an array of width
866
 *                                    and height values in pixels (in that order). Default 'thumbnail'.
867
 * @param bool         $icon          Optional. Whether the image should be treated as an icon. Default false.
868
 * @param string|array $attr          Optional. Attributes for the image markup. Default empty.
869
 * @return string HTML img element or empty string on failure.
870
 */
871
function wp_get_attachment_image($attachment_id, $size = 'thumbnail', $icon = false, $attr = '') {
872
	$html = '';
873
	$image = wp_get_attachment_image_src($attachment_id, $size, $icon);
874
	if ( $image ) {
875
		list($src, $width, $height) = $image;
876
		$hwstring = image_hwstring($width, $height);
877
		$size_class = $size;
878
		if ( is_array( $size_class ) ) {
879
			$size_class = join( 'x', $size_class );
880
		}
881
		$attachment = get_post($attachment_id);
882
		$default_attr = array(
883
			'src'	=> $src,
884
			'class'	=> "attachment-$size_class size-$size_class",
885
			'alt'	=> trim( strip_tags( get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ) ) ),
886
		);
887
888
		$attr = wp_parse_args( $attr, $default_attr );
889
890
		// Generate 'srcset' and 'sizes' if not already present.
891
		if ( empty( $attr['srcset'] ) ) {
892
			$image_meta = wp_get_attachment_metadata( $attachment_id );
893
894
			if ( is_array( $image_meta ) ) {
895
				$size_array = array( absint( $width ), absint( $height ) );
896
				$srcset = wp_calculate_image_srcset( $size_array, $src, $image_meta, $attachment_id );
897
				$sizes = wp_calculate_image_sizes( $size_array, $src, $image_meta, $attachment_id );
898
899
				if ( $srcset && ( $sizes || ! empty( $attr['sizes'] ) ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $srcset of type false|string is loosely compared to true; 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...
900
					$attr['srcset'] = $srcset;
901
902
					if ( empty( $attr['sizes'] ) ) {
903
						$attr['sizes'] = $sizes;
904
					}
905
				}
906
			}
907
		}
908
909
		/**
910
		 * Filters the list of attachment image attributes.
911
		 *
912
		 * @since 2.8.0
913
		 *
914
		 * @param array        $attr       Attributes for the image markup.
915
		 * @param WP_Post      $attachment Image attachment post.
916
		 * @param string|array $size       Requested size. Image size or array of width and height values
917
		 *                                 (in that order). Default 'thumbnail'.
918
		 */
919
		$attr = apply_filters( 'wp_get_attachment_image_attributes', $attr, $attachment, $size );
920
		$attr = array_map( 'esc_attr', $attr );
921
		$html = rtrim("<img $hwstring");
922
		foreach ( $attr as $name => $value ) {
923
			$html .= " $name=" . '"' . $value . '"';
924
		}
925
		$html .= ' />';
926
	}
927
928
	return $html;
929
}
930
931
/**
932
 * Get the URL of an image attachment.
933
 *
934
 * @since 4.4.0
935
 *
936
 * @param int          $attachment_id Image attachment ID.
937
 * @param string|array $size          Optional. Image size to retrieve. Accepts any valid image size, or an array
938
 *                                    of width and height values in pixels (in that order). Default 'thumbnail'.
939
 * @param bool         $icon          Optional. Whether the image should be treated as an icon. Default false.
940
 * @return string|false Attachment URL or false if no image is available.
941
 */
942
function wp_get_attachment_image_url( $attachment_id, $size = 'thumbnail', $icon = false ) {
943
	$image = wp_get_attachment_image_src( $attachment_id, $size, $icon );
944
	return isset( $image['0'] ) ? $image['0'] : false;
945
}
946
947
/**
948
 * Get the attachment path relative to the upload directory.
949
 *
950
 * @since 4.4.1
951
 * @access private
952
 *
953
 * @param string $file Attachment file name.
954
 * @return string Attachment path relative to the upload directory.
955
 */
956
function _wp_get_attachment_relative_path( $file ) {
957
	$dirname = dirname( $file );
958
959
	if ( '.' === $dirname ) {
960
		return '';
961
	}
962
963
	if ( false !== strpos( $dirname, 'wp-content/uploads' ) ) {
964
		// Get the directory name relative to the upload directory (back compat for pre-2.7 uploads)
965
		$dirname = substr( $dirname, strpos( $dirname, 'wp-content/uploads' ) + 18 );
966
		$dirname = ltrim( $dirname, '/' );
967
	}
968
969
	return $dirname;
970
}
971
972
/**
973
 * Get the image size as array from its meta data.
974
 *
975
 * Used for responsive images.
976
 *
977
 * @since 4.4.0
978
 * @access private
979
 *
980
 * @param string $size_name  Image size. Accepts any valid image size name ('thumbnail', 'medium', etc.).
981
 * @param array  $image_meta The image meta data.
982
 * @return array|bool Array of width and height values in pixels (in that order)
0 ignored issues
show
Consider making the return type a bit more specific; maybe use array<integer|double>|false.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
983
 *                    or false if the size doesn't exist.
984
 */
985
function _wp_get_image_size_from_meta( $size_name, $image_meta ) {
986
	if ( $size_name === 'full' ) {
987
		return array(
988
			absint( $image_meta['width'] ),
989
			absint( $image_meta['height'] ),
990
		);
991
	} elseif ( ! empty( $image_meta['sizes'][$size_name] ) ) {
992
		return array(
993
			absint( $image_meta['sizes'][$size_name]['width'] ),
994
			absint( $image_meta['sizes'][$size_name]['height'] ),
995
		);
996
	}
997
998
	return false;
999
}
1000
1001
/**
1002
 * Retrieves the value for an image attachment's 'srcset' attribute.
1003
 *
1004
 * @since 4.4.0
1005
 *
1006
 * @see wp_calculate_image_srcset()
1007
 *
1008
 * @param int          $attachment_id Image attachment ID.
1009
 * @param array|string $size          Optional. Image size. Accepts any valid image size, or an array of
1010
 *                                    width and height values in pixels (in that order). Default 'medium'.
1011
 * @param array        $image_meta    Optional. The image meta data as returned by 'wp_get_attachment_metadata()'.
0 ignored issues
show
Should the type for parameter $image_meta not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1012
 *                                    Default null.
1013
 * @return string|bool A 'srcset' value string or false.
0 ignored issues
show
Consider making the return type a bit more specific; maybe use false|string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
1014
 */
1015 View Code Duplication
function wp_get_attachment_image_srcset( $attachment_id, $size = 'medium', $image_meta = null ) {
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...
1016
	if ( ! $image = wp_get_attachment_image_src( $attachment_id, $size ) ) {
1017
		return false;
1018
	}
1019
1020
	if ( ! is_array( $image_meta ) ) {
1021
		$image_meta = wp_get_attachment_metadata( $attachment_id );
1022
	}
1023
1024
	$image_src = $image[0];
1025
	$size_array = array(
1026
		absint( $image[1] ),
1027
		absint( $image[2] )
1028
	);
1029
1030
	return wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attachment_id );
1031
}
1032
1033
/**
1034
 * A helper function to calculate the image sources to include in a 'srcset' attribute.
1035
 *
1036
 * @since 4.4.0
1037
 *
1038
 * @param array  $size_array    Array of width and height values in pixels (in that order).
1039
 * @param string $image_src     The 'src' of the image.
1040
 * @param array  $image_meta    The image meta data as returned by 'wp_get_attachment_metadata()'.
1041
 * @param int    $attachment_id Optional. The image attachment ID to pass to the filter. Default 0.
1042
 * @return string|bool          The 'srcset' attribute value. False on error or when only one source exists.
0 ignored issues
show
Consider making the return type a bit more specific; maybe use false|string.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
1043
 */
1044
function wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attachment_id = 0 ) {
1045
	/**
1046
	 * Let plugins pre-filter the image meta to be able to fix inconsistencies in the stored data.
1047
	 *
1048
	 * @since 4.5.0
1049
	 *
1050
	 * @param array  $image_meta    The image meta data as returned by 'wp_get_attachment_metadata()'.
1051
	 * @param array  $size_array    Array of width and height values in pixels (in that order).
1052
	 * @param string $image_src     The 'src' of the image.
1053
	 * @param int    $attachment_id The image attachment ID or 0 if not supplied.
1054
	 */
1055
	$image_meta = apply_filters( 'wp_calculate_image_srcset_meta', $image_meta, $size_array, $image_src, $attachment_id );
1056
1057
	if ( empty( $image_meta['sizes'] ) || ! isset( $image_meta['file'] ) || strlen( $image_meta['file'] ) < 4 ) {
1058
		return false;
1059
	}
1060
1061
	$image_sizes = $image_meta['sizes'];
1062
1063
	// Get the width and height of the image.
1064
	$image_width = (int) $size_array[0];
1065
	$image_height = (int) $size_array[1];
1066
1067
	// Bail early if error/no width.
1068
	if ( $image_width < 1 ) {
1069
		return false;
1070
	}
1071
1072
	$image_basename = wp_basename( $image_meta['file'] );
1073
1074
	/*
1075
	 * WordPress flattens animated GIFs into one frame when generating intermediate sizes.
1076
	 * To avoid hiding animation in user content, if src is a full size GIF, a srcset attribute is not generated.
1077
	 * If src is an intermediate size GIF, the full size is excluded from srcset to keep a flattened GIF from becoming animated.
1078
	 */
1079
	if ( ! isset( $image_sizes['thumbnail']['mime-type'] ) || 'image/gif' !== $image_sizes['thumbnail']['mime-type'] ) {
1080
		$image_sizes[] = array(
1081
			'width'  => $image_meta['width'],
1082
			'height' => $image_meta['height'],
1083
			'file'   => $image_basename,
1084
		);
1085
	} elseif ( strpos( $image_src, $image_meta['file'] ) ) {
1086
		return false;
1087
	}
1088
1089
	// Retrieve the uploads sub-directory from the full size image.
1090
	$dirname = _wp_get_attachment_relative_path( $image_meta['file'] );
1091
1092
	if ( $dirname ) {
1093
		$dirname = trailingslashit( $dirname );
1094
	}
1095
1096
	$upload_dir = wp_get_upload_dir();
1097
	$image_baseurl = trailingslashit( $upload_dir['baseurl'] ) . $dirname;
1098
1099
	/*
1100
	 * If currently on HTTPS, prefer HTTPS URLs when we know they're supported by the domain
1101
	 * (which is to say, when they share the domain name of the current request).
1102
	 */
1103
	if ( is_ssl() && 'https' !== substr( $image_baseurl, 0, 5 ) && parse_url( $image_baseurl, PHP_URL_HOST ) === $_SERVER['HTTP_HOST'] ) {
1104
		$image_baseurl = set_url_scheme( $image_baseurl, 'https' );
1105
	}
1106
1107
	/*
1108
	 * Images that have been edited in WordPress after being uploaded will
1109
	 * contain a unique hash. Look for that hash and use it later to filter
1110
	 * out images that are leftovers from previous versions.
1111
	 */
1112
	$image_edited = preg_match( '/-e[0-9]{13}/', wp_basename( $image_src ), $image_edit_hash );
1113
1114
	/**
1115
	 * Filters the maximum image width to be included in a 'srcset' attribute.
1116
	 *
1117
	 * @since 4.4.0
1118
	 *
1119
	 * @param int   $max_width  The maximum image width to be included in the 'srcset'. Default '1600'.
1120
	 * @param array $size_array Array of width and height values in pixels (in that order).
1121
	 */
1122
	$max_srcset_image_width = apply_filters( 'max_srcset_image_width', 1600, $size_array );
1123
1124
	// Array to hold URL candidates.
1125
	$sources = array();
1126
1127
	/**
1128
	 * To make sure the ID matches our image src, we will check to see if any sizes in our attachment
1129
	 * meta match our $image_src. If no matches are found we don't return a srcset to avoid serving
1130
	 * an incorrect image. See #35045.
1131
	 */
1132
	$src_matched = false;
1133
1134
	/*
1135
	 * Loop through available images. Only use images that are resized
1136
	 * versions of the same edit.
1137
	 */
1138
	foreach ( $image_sizes as $image ) {
1139
		$is_src = false;
1140
1141
		// Check if image meta isn't corrupted.
1142
		if ( ! is_array( $image ) ) {
1143
			continue;
1144
		}
1145
1146
		// If the file name is part of the `src`, we've confirmed a match.
1147
		if ( ! $src_matched && false !== strpos( $image_src, $dirname . $image['file'] ) ) {
1148
			$src_matched = $is_src = true;
1149
		}
1150
1151
		// Filter out images that are from previous edits.
1152
		if ( $image_edited && ! strpos( $image['file'], $image_edit_hash[0] ) ) {
1153
			continue;
1154
		}
1155
1156
		/*
1157
		 * Filters out images that are wider than '$max_srcset_image_width' unless
1158
		 * that file is in the 'src' attribute.
1159
		 */
1160
		if ( $max_srcset_image_width && $image['width'] > $max_srcset_image_width && ! $is_src ) {
1161
			continue;
1162
		}
1163
1164
		// If the image dimensions are within 1px of the expected size, use it.
1165
		if ( wp_image_matches_ratio( $image_width, $image_height, $image['width'], $image['height'] ) ) {
1166
			// Add the URL, descriptor, and value to the sources array to be returned.
1167
			$source = array(
1168
				'url'        => $image_baseurl . $image['file'],
1169
				'descriptor' => 'w',
1170
				'value'      => $image['width'],
1171
			);
1172
1173
			// The 'src' image has to be the first in the 'srcset', because of a bug in iOS8. See #35030.
1174
			if ( $is_src ) {
1175
				$sources = array( $image['width'] => $source ) + $sources;
1176
			} else {
1177
				$sources[ $image['width'] ] = $source;
1178
			}
1179
		}
1180
	}
1181
1182
	/**
1183
	 * Filters an image's 'srcset' sources.
1184
	 *
1185
	 * @since 4.4.0
1186
	 *
1187
	 * @param array  $sources {
1188
	 *     One or more arrays of source data to include in the 'srcset'.
1189
	 *
1190
	 *     @type array $width {
1191
	 *         @type string $url        The URL of an image source.
1192
	 *         @type string $descriptor The descriptor type used in the image candidate string,
1193
	 *                                  either 'w' or 'x'.
1194
	 *         @type int    $value      The source width if paired with a 'w' descriptor, or a
1195
	 *                                  pixel density value if paired with an 'x' descriptor.
1196
	 *     }
1197
	 * }
1198
	 * @param array  $size_array    Array of width and height values in pixels (in that order).
1199
	 * @param string $image_src     The 'src' of the image.
1200
	 * @param array  $image_meta    The image meta data as returned by 'wp_get_attachment_metadata()'.
1201
	 * @param int    $attachment_id Image attachment ID or 0.
1202
	 */
1203
	$sources = apply_filters( 'wp_calculate_image_srcset', $sources, $size_array, $image_src, $image_meta, $attachment_id );
1204
1205
	// Only return a 'srcset' value if there is more than one source.
1206
	if ( ! $src_matched || count( $sources ) < 2 ) {
1207
		return false;
1208
	}
1209
1210
	$srcset = '';
1211
1212
	foreach ( $sources as $source ) {
1213
		$srcset .= str_replace( ' ', '%20', $source['url'] ) . ' ' . $source['value'] . $source['descriptor'] . ', ';
1214
	}
1215
1216
	return rtrim( $srcset, ', ' );
1217
}
1218
1219
/**
1220
 * Retrieves the value for an image attachment's 'sizes' attribute.
1221
 *
1222
 * @since 4.4.0
1223
 *
1224
 * @see wp_calculate_image_sizes()
1225
 *
1226
 * @param int          $attachment_id Image attachment ID.
1227
 * @param array|string $size          Optional. Image size. Accepts any valid image size, or an array of width
1228
 *                                    and height values in pixels (in that order). Default 'medium'.
1229
 * @param array        $image_meta    Optional. The image meta data as returned by 'wp_get_attachment_metadata()'.
0 ignored issues
show
Should the type for parameter $image_meta not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1230
 *                                    Default null.
1231
 * @return string|bool A valid source size value for use in a 'sizes' attribute or false.
1232
 */
1233 View Code Duplication
function wp_get_attachment_image_sizes( $attachment_id, $size = 'medium', $image_meta = null ) {
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...
1234
	if ( ! $image = wp_get_attachment_image_src( $attachment_id, $size ) ) {
1235
		return false;
1236
	}
1237
1238
	if ( ! is_array( $image_meta ) ) {
1239
		$image_meta = wp_get_attachment_metadata( $attachment_id );
1240
	}
1241
1242
	$image_src = $image[0];
1243
	$size_array = array(
1244
		absint( $image[1] ),
1245
		absint( $image[2] )
1246
	);
1247
1248
	return wp_calculate_image_sizes( $size_array, $image_src, $image_meta, $attachment_id );
1249
}
1250
1251
/**
1252
 * Creates a 'sizes' attribute value for an image.
1253
 *
1254
 * @since 4.4.0
1255
 *
1256
 * @param array|string $size          Image size to retrieve. Accepts any valid image size, or an array
1257
 *                                    of width and height values in pixels (in that order). Default 'medium'.
1258
 * @param string       $image_src     Optional. The URL to the image file. Default null.
1259
 * @param array        $image_meta    Optional. The image meta data as returned by 'wp_get_attachment_metadata()'.
0 ignored issues
show
Should the type for parameter $image_meta not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
1260
 *                                    Default null.
1261
 * @param int          $attachment_id Optional. Image attachment ID. Either `$image_meta` or `$attachment_id`
1262
 *                                    is needed when using the image size name as argument for `$size`. Default 0.
1263
 * @return string|bool A valid source size value for use in a 'sizes' attribute or false.
1264
 */
1265
function wp_calculate_image_sizes( $size, $image_src = null, $image_meta = null, $attachment_id = 0 ) {
1266
	$width = 0;
1267
1268
	if ( is_array( $size ) ) {
1269
		$width = absint( $size[0] );
1270
	} elseif ( is_string( $size ) ) {
1271
		if ( ! $image_meta && $attachment_id ) {
1272
			$image_meta = wp_get_attachment_metadata( $attachment_id );
1273
		}
1274
1275
		if ( is_array( $image_meta ) ) {
1276
			$size_array = _wp_get_image_size_from_meta( $size, $image_meta );
1277
			if ( $size_array ) {
1278
				$width = absint( $size_array[0] );
1279
			}
1280
		}
1281
	}
1282
1283
	if ( ! $width ) {
1284
		return false;
1285
	}
1286
1287
	// Setup the default 'sizes' attribute.
1288
	$sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $width );
1289
1290
	/**
1291
	 * Filters the output of 'wp_calculate_image_sizes()'.
1292
	 *
1293
	 * @since 4.4.0
1294
	 *
1295
	 * @param string       $sizes         A source size value for use in a 'sizes' attribute.
1296
	 * @param array|string $size          Requested size. Image size or array of width and height values
1297
	 *                                    in pixels (in that order).
1298
	 * @param string|null  $image_src     The URL to the image file or null.
1299
	 * @param array|null   $image_meta    The image meta data as returned by wp_get_attachment_metadata() or null.
1300
	 * @param int          $attachment_id Image attachment ID of the original image or 0.
1301
	 */
1302
	return apply_filters( 'wp_calculate_image_sizes', $sizes, $size, $image_src, $image_meta, $attachment_id );
1303
}
1304
1305
/**
1306
 * Filters 'img' elements in post content to add 'srcset' and 'sizes' attributes.
1307
 *
1308
 * @since 4.4.0
1309
 *
1310
 * @see wp_image_add_srcset_and_sizes()
1311
 *
1312
 * @param string $content The raw post content to be filtered.
1313
 * @return string Converted content with 'srcset' and 'sizes' attributes added to images.
1314
 */
1315
function wp_make_content_images_responsive( $content ) {
1316
	if ( ! preg_match_all( '/<img [^>]+>/', $content, $matches ) ) {
1317
		return $content;
1318
	}
1319
1320
	$selected_images = $attachment_ids = array();
1321
1322
	foreach( $matches[0] as $image ) {
1323
		if ( false === strpos( $image, ' srcset=' ) && preg_match( '/wp-image-([0-9]+)/i', $image, $class_id ) &&
1324
			( $attachment_id = absint( $class_id[1] ) ) ) {
1325
1326
			/*
1327
			 * If exactly the same image tag is used more than once, overwrite it.
1328
			 * All identical tags will be replaced later with 'str_replace()'.
1329
			 */
1330
			$selected_images[ $image ] = $attachment_id;
1331
			// Overwrite the ID when the same image is included more than once.
1332
			$attachment_ids[ $attachment_id ] = true;
1333
		}
1334
	}
1335
1336
	if ( count( $attachment_ids ) > 1 ) {
1337
		/*
1338
		 * Warm object cache for use with 'get_post_meta()'.
1339
		 *
1340
		 * To avoid making a database call for each image, a single query
1341
		 * warms the object cache with the meta information for all images.
1342
		 */
1343
		update_meta_cache( 'post', array_keys( $attachment_ids ) );
1344
	}
1345
1346
	foreach ( $selected_images as $image => $attachment_id ) {
1347
		$image_meta = wp_get_attachment_metadata( $attachment_id );
1348
		$content = str_replace( $image, wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ), $content );
1349
	}
1350
1351
	return $content;
1352
}
1353
1354
/**
1355
 * Adds 'srcset' and 'sizes' attributes to an existing 'img' element.
1356
 *
1357
 * @since 4.4.0
1358
 *
1359
 * @see wp_calculate_image_srcset()
1360
 * @see wp_calculate_image_sizes()
1361
 *
1362
 * @param string $image         An HTML 'img' element to be filtered.
1363
 * @param array  $image_meta    The image meta data as returned by 'wp_get_attachment_metadata()'.
1364
 * @param int    $attachment_id Image attachment ID.
1365
 * @return string Converted 'img' element with 'srcset' and 'sizes' attributes added.
1366
 */
1367
function wp_image_add_srcset_and_sizes( $image, $image_meta, $attachment_id ) {
1368
	// Ensure the image meta exists.
1369
	if ( empty( $image_meta['sizes'] ) ) {
1370
		return $image;
1371
	}
1372
1373
	$image_src = preg_match( '/src="([^"]+)"/', $image, $match_src ) ? $match_src[1] : '';
1374
	list( $image_src ) = explode( '?', $image_src );
1375
1376
	// Return early if we couldn't get the image source.
1377
	if ( ! $image_src ) {
1378
		return $image;
1379
	}
1380
1381
	// Bail early if an image has been inserted and later edited.
1382
	if ( preg_match( '/-e[0-9]{13}/', $image_meta['file'], $img_edit_hash ) &&
1383
		strpos( wp_basename( $image_src ), $img_edit_hash[0] ) === false ) {
1384
1385
		return $image;
1386
	}
1387
1388
	$width  = preg_match( '/ width="([0-9]+)"/',  $image, $match_width  ) ? (int) $match_width[1]  : 0;
1389
	$height = preg_match( '/ height="([0-9]+)"/', $image, $match_height ) ? (int) $match_height[1] : 0;
1390
1391
	if ( ! $width || ! $height ) {
1392
		/*
1393
		 * If attempts to parse the size value failed, attempt to use the image meta data to match
1394
		 * the image file name from 'src' against the available sizes for an attachment.
1395
		 */
1396
		$image_filename = wp_basename( $image_src );
1397
1398
		if ( $image_filename === wp_basename( $image_meta['file'] ) ) {
1399
			$width = (int) $image_meta['width'];
1400
			$height = (int) $image_meta['height'];
1401
		} else {
1402
			foreach( $image_meta['sizes'] as $image_size_data ) {
1403
				if ( $image_filename === $image_size_data['file'] ) {
1404
					$width = (int) $image_size_data['width'];
1405
					$height = (int) $image_size_data['height'];
1406
					break;
1407
				}
1408
			}
1409
		}
1410
	}
1411
1412
	if ( ! $width || ! $height ) {
1413
		return $image;
1414
	}
1415
1416
	$size_array = array( $width, $height );
1417
	$srcset = wp_calculate_image_srcset( $size_array, $image_src, $image_meta, $attachment_id );
1418
1419
	if ( $srcset ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $srcset of type false|string is loosely compared to true; 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...
1420
		// Check if there is already a 'sizes' attribute.
1421
		$sizes = strpos( $image, ' sizes=' );
1422
1423
		if ( ! $sizes ) {
1424
			$sizes = wp_calculate_image_sizes( $size_array, $image_src, $image_meta, $attachment_id );
1425
		}
1426
	}
1427
1428
	if ( $srcset && $sizes ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $srcset of type false|string is loosely compared to true; 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...
1429
		// Format the 'srcset' and 'sizes' string and escape attributes.
1430
		$attr = sprintf( ' srcset="%s"', esc_attr( $srcset ) );
1431
1432
		if ( is_string( $sizes ) ) {
1433
			$attr .= sprintf( ' sizes="%s"', esc_attr( $sizes ) );
0 ignored issues
show
The variable $sizes does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1434
		}
1435
1436
		// Add 'srcset' and 'sizes' attributes to the image markup.
1437
		$image = preg_replace( '/<img ([^>]+?)[\/ ]*>/', '<img $1' . $attr . ' />', $image );
1438
	}
1439
1440
	return $image;
1441
}
1442
1443
/**
1444
 * Adds a 'wp-post-image' class to post thumbnails. Internal use only.
1445
 *
1446
 * Uses the {@see 'begin_fetch_post_thumbnail_html'} and {@see 'end_fetch_post_thumbnail_html'}
1447
 * action hooks to dynamically add/remove itself so as to only filter post thumbnails.
1448
 *
1449
 * @ignore
1450
 * @since 2.9.0
1451
 *
1452
 * @param array $attr Thumbnail attributes including src, class, alt, title.
1453
 * @return array Modified array of attributes including the new 'wp-post-image' class.
0 ignored issues
show
Consider making the return type a bit more specific; maybe use array<string,string>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
1454
 */
1455
function _wp_post_thumbnail_class_filter( $attr ) {
1456
	$attr['class'] .= ' wp-post-image';
1457
	return $attr;
1458
}
1459
1460
/**
1461
 * Adds '_wp_post_thumbnail_class_filter' callback to the 'wp_get_attachment_image_attributes'
1462
 * filter hook. Internal use only.
1463
 *
1464
 * @ignore
1465
 * @since 2.9.0
1466
 *
1467
 * @param array $attr Thumbnail attributes including src, class, alt, title.
1468
 */
1469
function _wp_post_thumbnail_class_filter_add( $attr ) {
0 ignored issues
show
The parameter $attr 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...
1470
	add_filter( 'wp_get_attachment_image_attributes', '_wp_post_thumbnail_class_filter' );
1471
}
1472
1473
/**
1474
 * Removes the '_wp_post_thumbnail_class_filter' callback from the 'wp_get_attachment_image_attributes'
1475
 * filter hook. Internal use only.
1476
 *
1477
 * @ignore
1478
 * @since 2.9.0
1479
 *
1480
 * @param array $attr Thumbnail attributes including src, class, alt, title.
1481
 */
1482
function _wp_post_thumbnail_class_filter_remove( $attr ) {
0 ignored issues
show
The parameter $attr 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...
1483
	remove_filter( 'wp_get_attachment_image_attributes', '_wp_post_thumbnail_class_filter' );
1484
}
1485
1486
add_shortcode('wp_caption', 'img_caption_shortcode');
1487
add_shortcode('caption', 'img_caption_shortcode');
1488
1489
/**
1490
 * Builds the Caption shortcode output.
1491
 *
1492
 * Allows a plugin to replace the content that would otherwise be returned. The
1493
 * filter is {@see 'img_caption_shortcode'} and passes an empty string, the attr
1494
 * parameter and the content parameter values.
1495
 *
1496
 * The supported attributes for the shortcode are 'id', 'align', 'width', and
1497
 * 'caption'.
1498
 *
1499
 * @since 2.6.0
1500
 *
1501
 * @param array  $attr {
1502
 *     Attributes of the caption shortcode.
1503
 *
1504
 *     @type string $id      ID of the div element for the caption.
1505
 *     @type string $align   Class name that aligns the caption. Default 'alignnone'. Accepts 'alignleft',
1506
 *                           'aligncenter', alignright', 'alignnone'.
1507
 *     @type int    $width   The width of the caption, in pixels.
1508
 *     @type string $caption The caption text.
1509
 *     @type string $class   Additional class name(s) added to the caption container.
1510
 * }
1511
 * @param string $content Shortcode content.
1512
 * @return string HTML content to display the caption.
1513
 */
1514
function img_caption_shortcode( $attr, $content = null ) {
1515
	// New-style shortcode with the caption inside the shortcode with the link and image tags.
1516
	if ( ! isset( $attr['caption'] ) ) {
1517
		if ( preg_match( '#((?:<a [^>]+>\s*)?<img [^>]+>(?:\s*</a>)?)(.*)#is', $content, $matches ) ) {
1518
			$content = $matches[1];
1519
			$attr['caption'] = trim( $matches[2] );
1520
		}
1521
	} elseif ( strpos( $attr['caption'], '<' ) !== false ) {
1522
		$attr['caption'] = wp_kses( $attr['caption'], 'post' );
1523
	}
1524
1525
	/**
1526
	 * Filters the default caption shortcode output.
1527
	 *
1528
	 * If the filtered output isn't empty, it will be used instead of generating
1529
	 * the default caption template.
1530
	 *
1531
	 * @since 2.6.0
1532
	 *
1533
	 * @see img_caption_shortcode()
1534
	 *
1535
	 * @param string $output  The caption output. Default empty.
1536
	 * @param array  $attr    Attributes of the caption shortcode.
1537
	 * @param string $content The image element, possibly wrapped in a hyperlink.
1538
	 */
1539
	$output = apply_filters( 'img_caption_shortcode', '', $attr, $content );
1540
	if ( $output != '' )
1541
		return $output;
1542
1543
	$atts = shortcode_atts( array(
1544
		'id'	  => '',
1545
		'align'	  => 'alignnone',
1546
		'width'	  => '',
1547
		'caption' => '',
1548
		'class'   => '',
1549
	), $attr, 'caption' );
1550
1551
	$atts['width'] = (int) $atts['width'];
1552
	if ( $atts['width'] < 1 || empty( $atts['caption'] ) )
1553
		return $content;
1554
1555 View Code Duplication
	if ( ! empty( $atts['id'] ) )
1556
		$atts['id'] = 'id="' . esc_attr( sanitize_html_class( $atts['id'] ) ) . '" ';
1557
1558
	$class = trim( 'wp-caption ' . $atts['align'] . ' ' . $atts['class'] );
1559
1560
	$html5 = current_theme_supports( 'html5', 'caption' );
1561
	// HTML5 captions never added the extra 10px to the image width
1562
	$width = $html5 ? $atts['width'] : ( 10 + $atts['width'] );
1563
1564
	/**
1565
	 * Filters the width of an image's caption.
1566
	 *
1567
	 * By default, the caption is 10 pixels greater than the width of the image,
1568
	 * to prevent post content from running up against a floated image.
1569
	 *
1570
	 * @since 3.7.0
1571
	 *
1572
	 * @see img_caption_shortcode()
1573
	 *
1574
	 * @param int    $width    Width of the caption in pixels. To remove this inline style,
1575
	 *                         return zero.
1576
	 * @param array  $atts     Attributes of the caption shortcode.
1577
	 * @param string $content  The image element, possibly wrapped in a hyperlink.
1578
	 */
1579
	$caption_width = apply_filters( 'img_caption_shortcode_width', $width, $atts, $content );
1580
1581
	$style = '';
1582
	if ( $caption_width ) {
1583
		$style = 'style="width: ' . (int) $caption_width . 'px" ';
1584
	}
1585
1586
	if ( $html5 ) {
1587
		$html = '<figure ' . $atts['id'] . $style . 'class="' . esc_attr( $class ) . '">'
1588
		. do_shortcode( $content ) . '<figcaption class="wp-caption-text">' . $atts['caption'] . '</figcaption></figure>';
1589
	} else {
1590
		$html = '<div ' . $atts['id'] . $style . 'class="' . esc_attr( $class ) . '">'
1591
		. do_shortcode( $content ) . '<p class="wp-caption-text">' . $atts['caption'] . '</p></div>';
1592
	}
1593
1594
	return $html;
1595
}
1596
1597
add_shortcode('gallery', 'gallery_shortcode');
1598
1599
/**
1600
 * Builds the Gallery shortcode output.
1601
 *
1602
 * This implements the functionality of the Gallery Shortcode for displaying
1603
 * WordPress images on a post.
1604
 *
1605
 * @since 2.5.0
1606
 *
1607
 * @staticvar int $instance
1608
 *
1609
 * @param array $attr {
1610
 *     Attributes of the gallery shortcode.
1611
 *
1612
 *     @type string       $order      Order of the images in the gallery. Default 'ASC'. Accepts 'ASC', 'DESC'.
1613
 *     @type string       $orderby    The field to use when ordering the images. Default 'menu_order ID'.
1614
 *                                    Accepts any valid SQL ORDERBY statement.
1615
 *     @type int          $id         Post ID.
1616
 *     @type string       $itemtag    HTML tag to use for each image in the gallery.
1617
 *                                    Default 'dl', or 'figure' when the theme registers HTML5 gallery support.
1618
 *     @type string       $icontag    HTML tag to use for each image's icon.
1619
 *                                    Default 'dt', or 'div' when the theme registers HTML5 gallery support.
1620
 *     @type string       $captiontag HTML tag to use for each image's caption.
1621
 *                                    Default 'dd', or 'figcaption' when the theme registers HTML5 gallery support.
1622
 *     @type int          $columns    Number of columns of images to display. Default 3.
1623
 *     @type string|array $size       Size of the images to display. Accepts any valid image size, or an array of width
1624
 *                                    and height values in pixels (in that order). Default 'thumbnail'.
1625
 *     @type string       $ids        A comma-separated list of IDs of attachments to display. Default empty.
1626
 *     @type string       $include    A comma-separated list of IDs of attachments to include. Default empty.
1627
 *     @type string       $exclude    A comma-separated list of IDs of attachments to exclude. Default empty.
1628
 *     @type string       $link       What to link each image to. Default empty (links to the attachment page).
1629
 *                                    Accepts 'file', 'none'.
1630
 * }
1631
 * @return string HTML content to display gallery.
1632
 */
1633
function gallery_shortcode( $attr ) {
1634
	$post = get_post();
1635
1636
	static $instance = 0;
1637
	$instance++;
1638
1639 View Code Duplication
	if ( ! empty( $attr['ids'] ) ) {
1640
		// 'ids' is explicitly ordered, unless you specify otherwise.
1641
		if ( empty( $attr['orderby'] ) ) {
1642
			$attr['orderby'] = 'post__in';
1643
		}
1644
		$attr['include'] = $attr['ids'];
1645
	}
1646
1647
	/**
1648
	 * Filters the default gallery shortcode output.
1649
	 *
1650
	 * If the filtered output isn't empty, it will be used instead of generating
1651
	 * the default gallery template.
1652
	 *
1653
	 * @since 2.5.0
1654
	 * @since 4.2.0 The `$instance` parameter was added.
1655
	 *
1656
	 * @see gallery_shortcode()
1657
	 *
1658
	 * @param string $output   The gallery output. Default empty.
1659
	 * @param array  $attr     Attributes of the gallery shortcode.
1660
	 * @param int    $instance Unique numeric ID of this gallery shortcode instance.
1661
	 */
1662
	$output = apply_filters( 'post_gallery', '', $attr, $instance );
1663
	if ( $output != '' ) {
1664
		return $output;
1665
	}
1666
1667
	$html5 = current_theme_supports( 'html5', 'gallery' );
1668
	$atts = shortcode_atts( array(
1669
		'order'      => 'ASC',
1670
		'orderby'    => 'menu_order ID',
1671
		'id'         => $post ? $post->ID : 0,
1672
		'itemtag'    => $html5 ? 'figure'     : 'dl',
1673
		'icontag'    => $html5 ? 'div'        : 'dt',
1674
		'captiontag' => $html5 ? 'figcaption' : 'dd',
1675
		'columns'    => 3,
1676
		'size'       => 'thumbnail',
1677
		'include'    => '',
1678
		'exclude'    => '',
1679
		'link'       => ''
1680
	), $attr, 'gallery' );
1681
1682
	$id = intval( $atts['id'] );
1683
1684
	if ( ! empty( $atts['include'] ) ) {
1685
		$_attachments = get_posts( array( 'include' => $atts['include'], 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'] ) );
1686
1687
		$attachments = array();
1688
		foreach ( $_attachments as $key => $val ) {
1689
			$attachments[$val->ID] = $_attachments[$key];
1690
		}
1691
	} elseif ( ! empty( $atts['exclude'] ) ) {
1692
		$attachments = get_children( array( 'post_parent' => $id, 'exclude' => $atts['exclude'], 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'] ) );
1693
	} else {
1694
		$attachments = get_children( array( 'post_parent' => $id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => $atts['order'], 'orderby' => $atts['orderby'] ) );
1695
	}
1696
1697
	if ( empty( $attachments ) ) {
1698
		return '';
1699
	}
1700
1701 View Code Duplication
	if ( is_feed() ) {
1702
		$output = "\n";
1703
		foreach ( $attachments as $att_id => $attachment ) {
1704
			$output .= wp_get_attachment_link( $att_id, $atts['size'], true ) . "\n";
1705
		}
1706
		return $output;
1707
	}
1708
1709
	$itemtag = tag_escape( $atts['itemtag'] );
1710
	$captiontag = tag_escape( $atts['captiontag'] );
1711
	$icontag = tag_escape( $atts['icontag'] );
1712
	$valid_tags = wp_kses_allowed_html( 'post' );
1713
	if ( ! isset( $valid_tags[ $itemtag ] ) ) {
1714
		$itemtag = 'dl';
1715
	}
1716
	if ( ! isset( $valid_tags[ $captiontag ] ) ) {
1717
		$captiontag = 'dd';
1718
	}
1719
	if ( ! isset( $valid_tags[ $icontag ] ) ) {
1720
		$icontag = 'dt';
1721
	}
1722
1723
	$columns = intval( $atts['columns'] );
1724
	$itemwidth = $columns > 0 ? floor(100/$columns) : 100;
1725
	$float = is_rtl() ? 'right' : 'left';
1726
1727
	$selector = "gallery-{$instance}";
1728
1729
	$gallery_style = '';
1730
1731
	/**
1732
	 * Filters whether to print default gallery styles.
1733
	 *
1734
	 * @since 3.1.0
1735
	 *
1736
	 * @param bool $print Whether to print default gallery styles.
1737
	 *                    Defaults to false if the theme supports HTML5 galleries.
1738
	 *                    Otherwise, defaults to true.
1739
	 */
1740
	if ( apply_filters( 'use_default_gallery_style', ! $html5 ) ) {
1741
		$gallery_style = "
1742
		<style type='text/css'>
1743
			#{$selector} {
1744
				margin: auto;
1745
			}
1746
			#{$selector} .gallery-item {
1747
				float: {$float};
1748
				margin-top: 10px;
1749
				text-align: center;
1750
				width: {$itemwidth}%;
1751
			}
1752
			#{$selector} img {
1753
				border: 2px solid #cfcfcf;
1754
			}
1755
			#{$selector} .gallery-caption {
1756
				margin-left: 0;
1757
			}
1758
			/* see gallery_shortcode() in wp-includes/media.php */
1759
		</style>\n\t\t";
1760
	}
1761
1762
	$size_class = sanitize_html_class( $atts['size'] );
1763
	$gallery_div = "<div id='$selector' class='gallery galleryid-{$id} gallery-columns-{$columns} gallery-size-{$size_class}'>";
1764
1765
	/**
1766
	 * Filters the default gallery shortcode CSS styles.
1767
	 *
1768
	 * @since 2.5.0
1769
	 *
1770
	 * @param string $gallery_style Default CSS styles and opening HTML div container
1771
	 *                              for the gallery shortcode output.
1772
	 */
1773
	$output = apply_filters( 'gallery_style', $gallery_style . $gallery_div );
1774
1775
	$i = 0;
1776
	foreach ( $attachments as $id => $attachment ) {
1777
1778
		$attr = ( trim( $attachment->post_excerpt ) ) ? array( 'aria-describedby' => "$selector-$id" ) : '';
1779
		if ( ! empty( $atts['link'] ) && 'file' === $atts['link'] ) {
1780
			$image_output = wp_get_attachment_link( $id, $atts['size'], false, false, false, $attr );
1781
		} elseif ( ! empty( $atts['link'] ) && 'none' === $atts['link'] ) {
1782
			$image_output = wp_get_attachment_image( $id, $atts['size'], false, $attr );
1783
		} else {
1784
			$image_output = wp_get_attachment_link( $id, $atts['size'], true, false, false, $attr );
1785
		}
1786
		$image_meta  = wp_get_attachment_metadata( $id );
1787
1788
		$orientation = '';
1789
		if ( isset( $image_meta['height'], $image_meta['width'] ) ) {
1790
			$orientation = ( $image_meta['height'] > $image_meta['width'] ) ? 'portrait' : 'landscape';
1791
		}
1792
		$output .= "<{$itemtag} class='gallery-item'>";
1793
		$output .= "
1794
			<{$icontag} class='gallery-icon {$orientation}'>
1795
				$image_output
1796
			</{$icontag}>";
1797
		if ( $captiontag && trim($attachment->post_excerpt) ) {
1798
			$output .= "
1799
				<{$captiontag} class='wp-caption-text gallery-caption' id='$selector-$id'>
1800
				" . wptexturize($attachment->post_excerpt) . "
1801
				</{$captiontag}>";
1802
		}
1803
		$output .= "</{$itemtag}>";
1804 View Code Duplication
		if ( ! $html5 && $columns > 0 && ++$i % $columns == 0 ) {
1805
			$output .= '<br style="clear: both" />';
1806
		}
1807
	}
1808
1809 View Code Duplication
	if ( ! $html5 && $columns > 0 && $i % $columns !== 0 ) {
1810
		$output .= "
1811
			<br style='clear: both' />";
1812
	}
1813
1814
	$output .= "
1815
		</div>\n";
1816
1817
	return $output;
1818
}
1819
1820
/**
1821
 * Outputs the templates used by playlists.
1822
 *
1823
 * @since 3.9.0
1824
 */
1825
function wp_underscore_playlist_templates() {
1826
?>
1827
<script type="text/html" id="tmpl-wp-playlist-current-item">
1828
	<# if ( data.image ) { #>
1829
	<img src="{{ data.thumb.src }}" alt="" />
1830
	<# } #>
1831
	<div class="wp-playlist-caption">
1832
		<span class="wp-playlist-item-meta wp-playlist-item-title"><?php
1833
			/* translators: playlist item title */
1834
			printf( _x( '&#8220;%s&#8221;', 'playlist item title' ), '{{ data.title }}' );
1835
		?></span>
1836
		<# if ( data.meta.album ) { #><span class="wp-playlist-item-meta wp-playlist-item-album">{{ data.meta.album }}</span><# } #>
1837
		<# if ( data.meta.artist ) { #><span class="wp-playlist-item-meta wp-playlist-item-artist">{{ data.meta.artist }}</span><# } #>
1838
	</div>
1839
</script>
1840
<script type="text/html" id="tmpl-wp-playlist-item">
1841
	<div class="wp-playlist-item">
1842
		<a class="wp-playlist-caption" href="{{ data.src }}">
1843
			{{ data.index ? ( data.index + '. ' ) : '' }}
1844
			<# if ( data.caption ) { #>
1845
				{{ data.caption }}
1846
			<# } else { #>
1847
				<span class="wp-playlist-item-title"><?php
1848
					/* translators: playlist item title */
1849
					printf( _x( '&#8220;%s&#8221;', 'playlist item title' ), '{{{ data.title }}}' );
1850
				?></span>
1851
				<# if ( data.artists && data.meta.artist ) { #>
1852
				<span class="wp-playlist-item-artist"> &mdash; {{ data.meta.artist }}</span>
1853
				<# } #>
1854
			<# } #>
1855
		</a>
1856
		<# if ( data.meta.length_formatted ) { #>
1857
		<div class="wp-playlist-item-length">{{ data.meta.length_formatted }}</div>
1858
		<# } #>
1859
	</div>
1860
</script>
1861
<?php
1862
}
1863
1864
/**
1865
 * Outputs and enqueue default scripts and styles for playlists.
1866
 *
1867
 * @since 3.9.0
1868
 *
1869
 * @param string $type Type of playlist. Accepts 'audio' or 'video'.
1870
 */
1871
function wp_playlist_scripts( $type ) {
1872
	wp_enqueue_style( 'wp-mediaelement' );
1873
	wp_enqueue_script( 'wp-playlist' );
1874
?>
1875
<!--[if lt IE 9]><script>document.createElement('<?php echo esc_js( $type ) ?>');</script><![endif]-->
1876
<?php
1877
	add_action( 'wp_footer', 'wp_underscore_playlist_templates', 0 );
1878
	add_action( 'admin_footer', 'wp_underscore_playlist_templates', 0 );
1879
}
1880
1881
/**
1882
 * Builds the Playlist shortcode output.
1883
 *
1884
 * This implements the functionality of the playlist shortcode for displaying
1885
 * a collection of WordPress audio or video files in a post.
1886
 *
1887
 * @since 3.9.0
1888
 *
1889
 * @global int $content_width
1890
 * @staticvar int $instance
1891
 *
1892
 * @param array $attr {
1893
 *     Array of default playlist attributes.
1894
 *
1895
 *     @type string  $type         Type of playlist to display. Accepts 'audio' or 'video'. Default 'audio'.
1896
 *     @type string  $order        Designates ascending or descending order of items in the playlist.
1897
 *                                 Accepts 'ASC', 'DESC'. Default 'ASC'.
1898
 *     @type string  $orderby      Any column, or columns, to sort the playlist. If $ids are
1899
 *                                 passed, this defaults to the order of the $ids array ('post__in').
1900
 *                                 Otherwise default is 'menu_order ID'.
1901
 *     @type int     $id           If an explicit $ids array is not present, this parameter
1902
 *                                 will determine which attachments are used for the playlist.
1903
 *                                 Default is the current post ID.
1904
 *     @type array   $ids          Create a playlist out of these explicit attachment IDs. If empty,
1905
 *                                 a playlist will be created from all $type attachments of $id.
1906
 *                                 Default empty.
1907
 *     @type array   $exclude      List of specific attachment IDs to exclude from the playlist. Default empty.
1908
 *     @type string  $style        Playlist style to use. Accepts 'light' or 'dark'. Default 'light'.
1909
 *     @type bool    $tracklist    Whether to show or hide the playlist. Default true.
1910
 *     @type bool    $tracknumbers Whether to show or hide the numbers next to entries in the playlist. Default true.
1911
 *     @type bool    $images       Show or hide the video or audio thumbnail (Featured Image/post
1912
 *                                 thumbnail). Default true.
1913
 *     @type bool    $artists      Whether to show or hide artist name in the playlist. Default true.
1914
 * }
1915
 *
1916
 * @return string Playlist output. Empty string if the passed type is unsupported.
1917
 */
1918
function wp_playlist_shortcode( $attr ) {
1919
	global $content_width;
1920
	$post = get_post();
1921
1922
	static $instance = 0;
1923
	$instance++;
1924
1925 View Code Duplication
	if ( ! empty( $attr['ids'] ) ) {
1926
		// 'ids' is explicitly ordered, unless you specify otherwise.
1927
		if ( empty( $attr['orderby'] ) ) {
1928
			$attr['orderby'] = 'post__in';
1929
		}
1930
		$attr['include'] = $attr['ids'];
1931
	}
1932
1933
	/**
1934
	 * Filters the playlist output.
1935
	 *
1936
	 * Passing a non-empty value to the filter will short-circuit generation
1937
	 * of the default playlist output, returning the passed value instead.
1938
	 *
1939
	 * @since 3.9.0
1940
	 * @since 4.2.0 The `$instance` parameter was added.
1941
	 *
1942
	 * @param string $output   Playlist output. Default empty.
1943
	 * @param array  $attr     An array of shortcode attributes.
1944
	 * @param int    $instance Unique numeric ID of this playlist shortcode instance.
1945
	 */
1946
	$output = apply_filters( 'post_playlist', '', $attr, $instance );
1947
	if ( $output != '' ) {
1948
		return $output;
1949
	}
1950
1951
	$atts = shortcode_atts( array(
1952
		'type'		=> 'audio',
1953
		'order'		=> 'ASC',
1954
		'orderby'	=> 'menu_order ID',
1955
		'id'		=> $post ? $post->ID : 0,
1956
		'include'	=> '',
1957
		'exclude'   => '',
1958
		'style'		=> 'light',
1959
		'tracklist' => true,
1960
		'tracknumbers' => true,
1961
		'images'	=> true,
1962
		'artists'	=> true
1963
	), $attr, 'playlist' );
1964
1965
	$id = intval( $atts['id'] );
1966
1967
	if ( $atts['type'] !== 'audio' ) {
1968
		$atts['type'] = 'video';
1969
	}
1970
1971
	$args = array(
1972
		'post_status' => 'inherit',
1973
		'post_type' => 'attachment',
1974
		'post_mime_type' => $atts['type'],
1975
		'order' => $atts['order'],
1976
		'orderby' => $atts['orderby']
1977
	);
1978
1979
	if ( ! empty( $atts['include'] ) ) {
1980
		$args['include'] = $atts['include'];
1981
		$_attachments = get_posts( $args );
1982
1983
		$attachments = array();
1984
		foreach ( $_attachments as $key => $val ) {
1985
			$attachments[$val->ID] = $_attachments[$key];
1986
		}
1987
	} elseif ( ! empty( $atts['exclude'] ) ) {
1988
		$args['post_parent'] = $id;
1989
		$args['exclude'] = $atts['exclude'];
1990
		$attachments = get_children( $args );
1991
	} else {
1992
		$args['post_parent'] = $id;
1993
		$attachments = get_children( $args );
1994
	}
1995
1996
	if ( empty( $attachments ) ) {
1997
		return '';
1998
	}
1999
2000 View Code Duplication
	if ( is_feed() ) {
2001
		$output = "\n";
2002
		foreach ( $attachments as $att_id => $attachment ) {
2003
			$output .= wp_get_attachment_link( $att_id ) . "\n";
2004
		}
2005
		return $output;
2006
	}
2007
2008
	$outer = 22; // default padding and border of wrapper
2009
2010
	$default_width = 640;
2011
	$default_height = 360;
2012
2013
	$theme_width = empty( $content_width ) ? $default_width : ( $content_width - $outer );
2014
	$theme_height = empty( $content_width ) ? $default_height : round( ( $default_height * $theme_width ) / $default_width );
2015
2016
	$data = array(
2017
		'type' => $atts['type'],
2018
		// don't pass strings to JSON, will be truthy in JS
2019
		'tracklist' => wp_validate_boolean( $atts['tracklist'] ),
2020
		'tracknumbers' => wp_validate_boolean( $atts['tracknumbers'] ),
2021
		'images' => wp_validate_boolean( $atts['images'] ),
2022
		'artists' => wp_validate_boolean( $atts['artists'] ),
2023
	);
2024
2025
	$tracks = array();
2026
	foreach ( $attachments as $attachment ) {
2027
		$url = wp_get_attachment_url( $attachment->ID );
2028
		$ftype = wp_check_filetype( $url, wp_get_mime_types() );
0 ignored issues
show
It seems like $url defined by wp_get_attachment_url($attachment->ID) on line 2027 can also be of type false; however, wp_check_filetype() 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...
2029
		$track = array(
2030
			'src' => $url,
2031
			'type' => $ftype['type'],
2032
			'title' => $attachment->post_title,
2033
			'caption' => $attachment->post_excerpt,
2034
			'description' => $attachment->post_content
2035
		);
2036
2037
		$track['meta'] = array();
2038
		$meta = wp_get_attachment_metadata( $attachment->ID );
2039
		if ( ! empty( $meta ) ) {
2040
2041
			foreach ( wp_get_attachment_id3_keys( $attachment ) as $key => $label ) {
2042
				if ( ! empty( $meta[ $key ] ) ) {
2043
					$track['meta'][ $key ] = $meta[ $key ];
2044
				}
2045
			}
2046
2047
			if ( 'video' === $atts['type'] ) {
2048
				if ( ! empty( $meta['width'] ) && ! empty( $meta['height'] ) ) {
2049
					$width = $meta['width'];
2050
					$height = $meta['height'];
2051
					$theme_height = round( ( $height * $theme_width ) / $width );
2052
				} else {
2053
					$width = $default_width;
2054
					$height = $default_height;
2055
				}
2056
2057
				$track['dimensions'] = array(
2058
					'original' => compact( 'width', 'height' ),
2059
					'resized' => array(
2060
						'width' => $theme_width,
2061
						'height' => $theme_height
2062
					)
2063
				);
2064
			}
2065
		}
2066
2067
		if ( $atts['images'] ) {
2068
			$thumb_id = get_post_thumbnail_id( $attachment->ID );
2069 View Code Duplication
			if ( ! empty( $thumb_id ) ) {
2070
				list( $src, $width, $height ) = wp_get_attachment_image_src( $thumb_id, 'full' );
2071
				$track['image'] = compact( 'src', 'width', 'height' );
2072
				list( $src, $width, $height ) = wp_get_attachment_image_src( $thumb_id, 'thumbnail' );
2073
				$track['thumb'] = compact( 'src', 'width', 'height' );
2074
			} else {
2075
				$src = wp_mime_type_icon( $attachment->ID );
2076
				$width = 48;
2077
				$height = 64;
2078
				$track['image'] = compact( 'src', 'width', 'height' );
2079
				$track['thumb'] = compact( 'src', 'width', 'height' );
2080
			}
2081
		}
2082
2083
		$tracks[] = $track;
2084
	}
2085
	$data['tracks'] = $tracks;
2086
2087
	$safe_type = esc_attr( $atts['type'] );
2088
	$safe_style = esc_attr( $atts['style'] );
2089
2090
	ob_start();
2091
2092
	if ( 1 === $instance ) {
2093
		/**
2094
		 * Prints and enqueues playlist scripts, styles, and JavaScript templates.
2095
		 *
2096
		 * @since 3.9.0
2097
		 *
2098
		 * @param string $type  Type of playlist. Possible values are 'audio' or 'video'.
2099
		 * @param string $style The 'theme' for the playlist. Core provides 'light' and 'dark'.
2100
		 */
2101
		do_action( 'wp_playlist_scripts', $atts['type'], $atts['style'] );
2102
	} ?>
2103
<div class="wp-playlist wp-<?php echo $safe_type ?>-playlist wp-playlist-<?php echo $safe_style ?>">
2104
	<?php if ( 'audio' === $atts['type'] ): ?>
2105
	<div class="wp-playlist-current-item"></div>
2106
	<?php endif ?>
2107
	<<?php echo $safe_type ?> controls="controls" preload="none" width="<?php
2108
		echo (int) $theme_width;
2109
	?>"<?php if ( 'video' === $safe_type ):
2110
		echo ' height="', (int) $theme_height, '"';
2111
	endif; ?>></<?php echo $safe_type ?>>
2112
	<div class="wp-playlist-next"></div>
2113
	<div class="wp-playlist-prev"></div>
2114
	<noscript>
2115
	<ol><?php
2116
	foreach ( $attachments as $att_id => $attachment ) {
2117
		printf( '<li>%s</li>', wp_get_attachment_link( $att_id ) );
2118
	}
2119
	?></ol>
2120
	</noscript>
2121
	<script type="application/json" class="wp-playlist-script"><?php echo wp_json_encode( $data ) ?></script>
2122
</div>
2123
	<?php
2124
	return ob_get_clean();
2125
}
2126
add_shortcode( 'playlist', 'wp_playlist_shortcode' );
2127
2128
/**
2129
 * Provides a No-JS Flash fallback as a last resort for audio / video.
2130
 *
2131
 * @since 3.6.0
2132
 *
2133
 * @param string $url The media element URL.
2134
 * @return string Fallback HTML.
2135
 */
2136
function wp_mediaelement_fallback( $url ) {
2137
	/**
2138
	 * Filters the Mediaelement fallback output for no-JS.
2139
	 *
2140
	 * @since 3.6.0
2141
	 *
2142
	 * @param string $output Fallback output for no-JS.
2143
	 * @param string $url    Media file URL.
2144
	 */
2145
	return apply_filters( 'wp_mediaelement_fallback', sprintf( '<a href="%1$s">%1$s</a>', esc_url( $url ) ), $url );
2146
}
2147
2148
/**
2149
 * Returns a filtered list of WP-supported audio formats.
2150
 *
2151
 * @since 3.6.0
2152
 *
2153
 * @return array Supported audio formats.
2154
 */
2155
function wp_get_audio_extensions() {
2156
	/**
2157
	 * Filters the list of supported audio formats.
2158
	 *
2159
	 * @since 3.6.0
2160
	 *
2161
	 * @param array $extensions An array of support audio formats. Defaults are
2162
	 *                          'mp3', 'ogg', 'm4a', 'wav'.
2163
	 */
2164
	return apply_filters( 'wp_audio_extensions', array( 'mp3', 'ogg', 'm4a', 'wav' ) );
2165
}
2166
2167
/**
2168
 * Returns useful keys to use to lookup data from an attachment's stored metadata.
2169
 *
2170
 * @since 3.9.0
2171
 *
2172
 * @param WP_Post $attachment The current attachment, provided for context.
2173
 * @param string  $context    Optional. The context. Accepts 'edit', 'display'. Default 'display'.
2174
 * @return array Key/value pairs of field keys to labels.
2175
 */
2176
function wp_get_attachment_id3_keys( $attachment, $context = 'display' ) {
2177
	$fields = array(
2178
		'artist' => __( 'Artist' ),
2179
		'album' => __( 'Album' ),
2180
	);
2181
2182
	if ( 'display' === $context ) {
2183
		$fields['genre']            = __( 'Genre' );
2184
		$fields['year']             = __( 'Year' );
2185
		$fields['length_formatted'] = _x( 'Length', 'video or audio' );
2186
	} elseif ( 'js' === $context ) {
2187
		$fields['bitrate']          = __( 'Bitrate' );
2188
		$fields['bitrate_mode']     = __( 'Bitrate Mode' );
2189
	}
2190
2191
	/**
2192
	 * Filters the editable list of keys to look up data from an attachment's metadata.
2193
	 *
2194
	 * @since 3.9.0
2195
	 *
2196
	 * @param array   $fields     Key/value pairs of field keys to labels.
2197
	 * @param WP_Post $attachment Attachment object.
2198
	 * @param string  $context    The context. Accepts 'edit', 'display'. Default 'display'.
2199
	 */
2200
	return apply_filters( 'wp_get_attachment_id3_keys', $fields, $attachment, $context );
2201
}
2202
/**
2203
 * Builds the Audio shortcode output.
2204
 *
2205
 * This implements the functionality of the Audio Shortcode for displaying
2206
 * WordPress mp3s in a post.
2207
 *
2208
 * @since 3.6.0
2209
 *
2210
 * @staticvar int $instance
2211
 *
2212
 * @param array  $attr {
2213
 *     Attributes of the audio shortcode.
2214
 *
2215
 *     @type string $src      URL to the source of the audio file. Default empty.
2216
 *     @type string $loop     The 'loop' attribute for the `<audio>` element. Default empty.
2217
 *     @type string $autoplay The 'autoplay' attribute for the `<audio>` element. Default empty.
2218
 *     @type string $preload  The 'preload' attribute for the `<audio>` element. Default 'none'.
2219
 *     @type string $class    The 'class' attribute for the `<audio>` element. Default 'wp-audio-shortcode'.
2220
 *     @type string $style    The 'style' attribute for the `<audio>` element. Default 'width: 100%;'.
2221
 * }
2222
 * @param string $content Shortcode content.
2223
 * @return string|void HTML content to display audio.
2224
 */
2225
function wp_audio_shortcode( $attr, $content = '' ) {
2226
	$post_id = get_post() ? get_the_ID() : 0;
2227
2228
	static $instance = 0;
2229
	$instance++;
2230
2231
	/**
2232
	 * Filters the default audio shortcode output.
2233
	 *
2234
	 * If the filtered output isn't empty, it will be used instead of generating the default audio template.
2235
	 *
2236
	 * @since 3.6.0
2237
	 *
2238
	 * @param string $html     Empty variable to be replaced with shortcode markup.
2239
	 * @param array  $attr     Attributes of the shortcode. @see wp_audio_shortcode()
2240
	 * @param string $content  Shortcode content.
2241
	 * @param int    $instance Unique numeric ID of this audio shortcode instance.
2242
	 */
2243
	$override = apply_filters( 'wp_audio_shortcode_override', '', $attr, $content, $instance );
2244
	if ( '' !== $override ) {
2245
		return $override;
2246
	}
2247
2248
	$audio = null;
2249
2250
	$default_types = wp_get_audio_extensions();
2251
	$defaults_atts = array(
2252
		'src'      => '',
2253
		'loop'     => '',
2254
		'autoplay' => '',
2255
		'preload'  => 'none',
2256
		'class'    => 'wp-audio-shortcode',
2257
		'style'    => 'width: 100%;'
2258
	);
2259
	foreach ( $default_types as $type ) {
2260
		$defaults_atts[$type] = '';
2261
	}
2262
2263
	$atts = shortcode_atts( $defaults_atts, $attr, 'audio' );
2264
2265
	$primary = false;
2266
	if ( ! empty( $atts['src'] ) ) {
2267
		$type = wp_check_filetype( $atts['src'], wp_get_mime_types() );
2268 View Code Duplication
		if ( ! in_array( strtolower( $type['ext'] ), $default_types ) ) {
2269
			return sprintf( '<a class="wp-embedded-audio" href="%s">%s</a>', esc_url( $atts['src'] ), esc_html( $atts['src'] ) );
2270
		}
2271
		$primary = true;
2272
		array_unshift( $default_types, 'src' );
2273 View Code Duplication
	} else {
2274
		foreach ( $default_types as $ext ) {
2275
			if ( ! empty( $atts[ $ext ] ) ) {
2276
				$type = wp_check_filetype( $atts[ $ext ], wp_get_mime_types() );
2277
				if ( strtolower( $type['ext'] ) === $ext ) {
2278
					$primary = true;
2279
				}
2280
			}
2281
		}
2282
	}
2283
2284 View Code Duplication
	if ( ! $primary ) {
2285
		$audios = get_attached_media( 'audio', $post_id );
2286
		if ( empty( $audios ) ) {
2287
			return;
2288
		}
2289
2290
		$audio = reset( $audios );
2291
		$atts['src'] = wp_get_attachment_url( $audio->ID );
2292
		if ( empty( $atts['src'] ) ) {
2293
			return;
2294
		}
2295
2296
		array_unshift( $default_types, 'src' );
2297
	}
2298
2299
	/**
2300
	 * Filters the media library used for the audio shortcode.
2301
	 *
2302
	 * @since 3.6.0
2303
	 *
2304
	 * @param string $library Media library used for the audio shortcode.
2305
	 */
2306
	$library = apply_filters( 'wp_audio_shortcode_library', 'mediaelement' );
2307
	if ( 'mediaelement' === $library && did_action( 'init' ) ) {
2308
		wp_enqueue_style( 'wp-mediaelement' );
2309
		wp_enqueue_script( 'wp-mediaelement' );
2310
	}
2311
2312
	/**
2313
	 * Filters the class attribute for the audio shortcode output container.
2314
	 *
2315
	 * @since 3.6.0
2316
	 *
2317
	 * @param string $class CSS class or list of space-separated classes.
2318
	 */
2319
	$atts['class'] = apply_filters( 'wp_audio_shortcode_class', $atts['class'] );
2320
2321
	$html_atts = array(
2322
		'class'    => $atts['class'],
2323
		'id'       => sprintf( 'audio-%d-%d', $post_id, $instance ),
2324
		'loop'     => wp_validate_boolean( $atts['loop'] ),
2325
		'autoplay' => wp_validate_boolean( $atts['autoplay'] ),
2326
		'preload'  => $atts['preload'],
2327
		'style'    => $atts['style'],
2328
	);
2329
2330
	// These ones should just be omitted altogether if they are blank
2331 View Code Duplication
	foreach ( array( 'loop', 'autoplay', 'preload' ) as $a ) {
2332
		if ( empty( $html_atts[$a] ) ) {
2333
			unset( $html_atts[$a] );
2334
		}
2335
	}
2336
2337
	$attr_strings = array();
2338 View Code Duplication
	foreach ( $html_atts as $k => $v ) {
2339
		$attr_strings[] = $k . '="' . esc_attr( $v ) . '"';
2340
	}
2341
2342
	$html = '';
2343
	if ( 'mediaelement' === $library && 1 === $instance ) {
2344
		$html .= "<!--[if lt IE 9]><script>document.createElement('audio');</script><![endif]-->\n";
2345
	}
2346
	$html .= sprintf( '<audio %s controls="controls">', join( ' ', $attr_strings ) );
2347
2348
	$fileurl = '';
2349
	$source = '<source type="%s" src="%s" />';
2350
	foreach ( $default_types as $fallback ) {
2351
		if ( ! empty( $atts[ $fallback ] ) ) {
2352
			if ( empty( $fileurl ) ) {
2353
				$fileurl = $atts[ $fallback ];
2354
			}
2355
			$type = wp_check_filetype( $atts[ $fallback ], wp_get_mime_types() );
2356
			$url = add_query_arg( '_', $instance, $atts[ $fallback ] );
2357
			$html .= sprintf( $source, $type['type'], esc_url( $url ) );
2358
		}
2359
	}
2360
2361
	if ( 'mediaelement' === $library ) {
2362
		$html .= wp_mediaelement_fallback( $fileurl );
2363
	}
2364
	$html .= '</audio>';
2365
2366
	/**
2367
	 * Filters the audio shortcode output.
2368
	 *
2369
	 * @since 3.6.0
2370
	 *
2371
	 * @param string $html    Audio shortcode HTML output.
2372
	 * @param array  $atts    Array of audio shortcode attributes.
2373
	 * @param string $audio   Audio file.
2374
	 * @param int    $post_id Post ID.
2375
	 * @param string $library Media library used for the audio shortcode.
2376
	 */
2377
	return apply_filters( 'wp_audio_shortcode', $html, $atts, $audio, $post_id, $library );
2378
}
2379
add_shortcode( 'audio', 'wp_audio_shortcode' );
2380
2381
/**
2382
 * Returns a filtered list of WP-supported video formats.
2383
 *
2384
 * @since 3.6.0
2385
 *
2386
 * @return array List of supported video formats.
2387
 */
2388
function wp_get_video_extensions() {
2389
	/**
2390
	 * Filters the list of supported video formats.
2391
	 *
2392
	 * @since 3.6.0
2393
	 *
2394
	 * @param array $extensions An array of support video formats. Defaults are
2395
	 *                          'mp4', 'm4v', 'webm', 'ogv', 'flv'.
2396
	 */
2397
	return apply_filters( 'wp_video_extensions', array( 'mp4', 'm4v', 'webm', 'ogv', 'flv' ) );
2398
}
2399
2400
/**
2401
 * Builds the Video shortcode output.
2402
 *
2403
 * This implements the functionality of the Video Shortcode for displaying
2404
 * WordPress mp4s in a post.
2405
 *
2406
 * @since 3.6.0
2407
 *
2408
 * @global int $content_width
2409
 * @staticvar int $instance
2410
 *
2411
 * @param array  $attr {
2412
 *     Attributes of the shortcode.
2413
 *
2414
 *     @type string $src      URL to the source of the video file. Default empty.
2415
 *     @type int    $height   Height of the video embed in pixels. Default 360.
2416
 *     @type int    $width    Width of the video embed in pixels. Default $content_width or 640.
2417
 *     @type string $poster   The 'poster' attribute for the `<video>` element. Default empty.
2418
 *     @type string $loop     The 'loop' attribute for the `<video>` element. Default empty.
2419
 *     @type string $autoplay The 'autoplay' attribute for the `<video>` element. Default empty.
2420
 *     @type string $preload  The 'preload' attribute for the `<video>` element.
2421
 *                            Default 'metadata'.
2422
 *     @type string $class    The 'class' attribute for the `<video>` element.
2423
 *                            Default 'wp-video-shortcode'.
2424
 * }
2425
 * @param string $content Shortcode content.
2426
 * @return string|void HTML content to display video.
2427
 */
2428
function wp_video_shortcode( $attr, $content = '' ) {
2429
	global $content_width;
2430
	$post_id = get_post() ? get_the_ID() : 0;
2431
2432
	static $instance = 0;
2433
	$instance++;
2434
2435
	/**
2436
	 * Filters the default video shortcode output.
2437
	 *
2438
	 * If the filtered output isn't empty, it will be used instead of generating
2439
	 * the default video template.
2440
	 *
2441
	 * @since 3.6.0
2442
	 *
2443
	 * @see wp_video_shortcode()
2444
	 *
2445
	 * @param string $html     Empty variable to be replaced with shortcode markup.
2446
	 * @param array  $attr     Attributes of the video shortcode.
2447
	 * @param string $content  Video shortcode content.
2448
	 * @param int    $instance Unique numeric ID of this video shortcode instance.
2449
	 */
2450
	$override = apply_filters( 'wp_video_shortcode_override', '', $attr, $content, $instance );
2451
	if ( '' !== $override ) {
2452
		return $override;
2453
	}
2454
2455
	$video = null;
2456
2457
	$default_types = wp_get_video_extensions();
2458
	$defaults_atts = array(
2459
		'src'      => '',
2460
		'poster'   => '',
2461
		'loop'     => '',
2462
		'autoplay' => '',
2463
		'preload'  => 'metadata',
2464
		'width'    => 640,
2465
		'height'   => 360,
2466
		'class'    => 'wp-video-shortcode',
2467
	);
2468
2469
	foreach ( $default_types as $type ) {
2470
		$defaults_atts[$type] = '';
2471
	}
2472
2473
	$atts = shortcode_atts( $defaults_atts, $attr, 'video' );
2474
2475
	if ( is_admin() ) {
2476
		// shrink the video so it isn't huge in the admin
2477
		if ( $atts['width'] > $defaults_atts['width'] ) {
2478
			$atts['height'] = round( ( $atts['height'] * $defaults_atts['width'] ) / $atts['width'] );
2479
			$atts['width'] = $defaults_atts['width'];
2480
		}
2481
	} else {
2482
		// if the video is bigger than the theme
2483
		if ( ! empty( $content_width ) && $atts['width'] > $content_width ) {
2484
			$atts['height'] = round( ( $atts['height'] * $content_width ) / $atts['width'] );
2485
			$atts['width'] = $content_width;
2486
		}
2487
	}
2488
2489
	$is_vimeo = $is_youtube = false;
2490
	$yt_pattern = '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#';
2491
	$vimeo_pattern = '#^https?://(.+\.)?vimeo\.com/.*#';
2492
2493
	$primary = false;
2494
	if ( ! empty( $atts['src'] ) ) {
2495
		$is_vimeo = ( preg_match( $vimeo_pattern, $atts['src'] ) );
2496
		$is_youtube = (  preg_match( $yt_pattern, $atts['src'] ) );
2497
		if ( ! $is_youtube && ! $is_vimeo ) {
2498
			$type = wp_check_filetype( $atts['src'], wp_get_mime_types() );
2499 View Code Duplication
			if ( ! in_array( strtolower( $type['ext'] ), $default_types ) ) {
2500
				return sprintf( '<a class="wp-embedded-video" href="%s">%s</a>', esc_url( $atts['src'] ), esc_html( $atts['src'] ) );
2501
			}
2502
		}
2503
2504
		if ( $is_vimeo ) {
2505
			wp_enqueue_script( 'froogaloop' );
2506
		}
2507
2508
		$primary = true;
2509
		array_unshift( $default_types, 'src' );
2510 View Code Duplication
	} else {
2511
		foreach ( $default_types as $ext ) {
2512
			if ( ! empty( $atts[ $ext ] ) ) {
2513
				$type = wp_check_filetype( $atts[ $ext ], wp_get_mime_types() );
2514
				if ( strtolower( $type['ext'] ) === $ext ) {
2515
					$primary = true;
2516
				}
2517
			}
2518
		}
2519
	}
2520
2521 View Code Duplication
	if ( ! $primary ) {
2522
		$videos = get_attached_media( 'video', $post_id );
2523
		if ( empty( $videos ) ) {
2524
			return;
2525
		}
2526
2527
		$video = reset( $videos );
2528
		$atts['src'] = wp_get_attachment_url( $video->ID );
2529
		if ( empty( $atts['src'] ) ) {
2530
			return;
2531
		}
2532
2533
		array_unshift( $default_types, 'src' );
2534
	}
2535
2536
	/**
2537
	 * Filters the media library used for the video shortcode.
2538
	 *
2539
	 * @since 3.6.0
2540
	 *
2541
	 * @param string $library Media library used for the video shortcode.
2542
	 */
2543
	$library = apply_filters( 'wp_video_shortcode_library', 'mediaelement' );
2544
	if ( 'mediaelement' === $library && did_action( 'init' ) ) {
2545
		wp_enqueue_style( 'wp-mediaelement' );
2546
		wp_enqueue_script( 'wp-mediaelement' );
2547
	}
2548
2549
	// Mediaelement has issues with some URL formats for Vimeo and YouTube, so
2550
	// update the URL to prevent the ME.js player from breaking.
2551
	if ( 'mediaelement' === $library ) {
2552
		if ( $is_youtube ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_youtube of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2553
			// Remove `feature` query arg and force SSL - see #40866.
2554
			$atts['src'] = remove_query_arg( 'feature', $atts['src'] );
2555
			$atts['src'] = set_url_scheme( $atts['src'], 'https' );
0 ignored issues
show
It seems like $atts['src'] can also be of type boolean; however, set_url_scheme() does only seem to accept string, 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...
2556
		} elseif ( $is_vimeo ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_vimeo of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2557
			// Remove all query arguments and force SSL - see #40866.
2558
			$parsed_vimeo_url = wp_parse_url( $atts['src'] );
2559
			$vimeo_src = 'https://' . $parsed_vimeo_url['host'] . $parsed_vimeo_url['path'];
2560
2561
			// Add loop param for mejs bug - see #40977, not needed after #39686.
2562
			$loop = $atts['loop'] ? '1' : '0';
2563
			$atts['src'] = add_query_arg( 'loop', $loop, $vimeo_src );
2564
		}
2565
	}
2566
2567
	/**
2568
	 * Filters the class attribute for the video shortcode output container.
2569
	 *
2570
	 * @since 3.6.0
2571
	 *
2572
	 * @param string $class CSS class or list of space-separated classes.
2573
	 */
2574
	$atts['class'] = apply_filters( 'wp_video_shortcode_class', $atts['class'] );
2575
2576
	$html_atts = array(
2577
		'class'    => $atts['class'],
2578
		'id'       => sprintf( 'video-%d-%d', $post_id, $instance ),
2579
		'width'    => absint( $atts['width'] ),
2580
		'height'   => absint( $atts['height'] ),
2581
		'poster'   => esc_url( $atts['poster'] ),
2582
		'loop'     => wp_validate_boolean( $atts['loop'] ),
2583
		'autoplay' => wp_validate_boolean( $atts['autoplay'] ),
2584
		'preload'  => $atts['preload'],
2585
	);
2586
2587
	// These ones should just be omitted altogether if they are blank
2588 View Code Duplication
	foreach ( array( 'poster', 'loop', 'autoplay', 'preload' ) as $a ) {
2589
		if ( empty( $html_atts[$a] ) ) {
2590
			unset( $html_atts[$a] );
2591
		}
2592
	}
2593
2594
	$attr_strings = array();
2595 View Code Duplication
	foreach ( $html_atts as $k => $v ) {
2596
		$attr_strings[] = $k . '="' . esc_attr( $v ) . '"';
2597
	}
2598
2599
	$html = '';
2600
	if ( 'mediaelement' === $library && 1 === $instance ) {
2601
		$html .= "<!--[if lt IE 9]><script>document.createElement('video');</script><![endif]-->\n";
2602
	}
2603
	$html .= sprintf( '<video %s controls="controls">', join( ' ', $attr_strings ) );
2604
2605
	$fileurl = '';
2606
	$source = '<source type="%s" src="%s" />';
2607
	foreach ( $default_types as $fallback ) {
2608
		if ( ! empty( $atts[ $fallback ] ) ) {
2609
			if ( empty( $fileurl ) ) {
2610
				$fileurl = $atts[ $fallback ];
2611
			}
2612
			if ( 'src' === $fallback && $is_youtube ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_youtube of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2613
				$type = array( 'type' => 'video/youtube' );
2614
			} elseif ( 'src' === $fallback && $is_vimeo ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $is_vimeo of type integer|false is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

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

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
2615
				$type = array( 'type' => 'video/vimeo' );
2616
			} else {
2617
				$type = wp_check_filetype( $atts[ $fallback ], wp_get_mime_types() );
2618
			}
2619
			$url = add_query_arg( '_', $instance, $atts[ $fallback ] );
2620
			$html .= sprintf( $source, $type['type'], esc_url( $url ) );
2621
		}
2622
	}
2623
2624
	if ( ! empty( $content ) ) {
2625 View Code Duplication
		if ( false !== strpos( $content, "\n" ) ) {
2626
			$content = str_replace( array( "\r\n", "\n", "\t" ), '', $content );
2627
		}
2628
		$html .= trim( $content );
2629
	}
2630
2631
	if ( 'mediaelement' === $library ) {
2632
		$html .= wp_mediaelement_fallback( $fileurl );
2633
	}
2634
	$html .= '</video>';
2635
2636
	$width_rule = '';
2637
	if ( ! empty( $atts['width'] ) ) {
2638
		$width_rule = sprintf( 'width: %dpx;', $atts['width'] );
2639
	}
2640
	$output = sprintf( '<div style="%s" class="wp-video">%s</div>', $width_rule, $html );
2641
2642
	/**
2643
	 * Filters the output of the video shortcode.
2644
	 *
2645
	 * @since 3.6.0
2646
	 *
2647
	 * @param string $output  Video shortcode HTML output.
2648
	 * @param array  $atts    Array of video shortcode attributes.
2649
	 * @param string $video   Video file.
2650
	 * @param int    $post_id Post ID.
2651
	 * @param string $library Media library used for the video shortcode.
2652
	 */
2653
	return apply_filters( 'wp_video_shortcode', $output, $atts, $video, $post_id, $library );
2654
}
2655
add_shortcode( 'video', 'wp_video_shortcode' );
2656
2657
/**
2658
 * Displays previous image link that has the same post parent.
2659
 *
2660
 * @since 2.5.0
2661
 *
2662
 * @see adjacent_image_link()
2663
 *
2664
 * @param string|array $size Optional. Image size. Accepts any valid image size, an array of width and
2665
 *                           height values in pixels (in that order), 0, or 'none'. 0 or 'none' will
2666
 *                           default to 'post_title' or `$text`. Default 'thumbnail'.
2667
 * @param string       $text Optional. Link text. Default false.
2668
 */
2669
function previous_image_link( $size = 'thumbnail', $text = false ) {
2670
	adjacent_image_link(true, $size, $text);
0 ignored issues
show
It seems like $text defined by parameter $text on line 2669 can also be of type string; however, adjacent_image_link() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2671
}
2672
2673
/**
2674
 * Displays next image link that has the same post parent.
2675
 *
2676
 * @since 2.5.0
2677
 *
2678
 * @see adjacent_image_link()
2679
 *
2680
 * @param string|array $size Optional. Image size. Accepts any valid image size, an array of width and
2681
 *                           height values in pixels (in that order), 0, or 'none'. 0 or 'none' will
2682
 *                           default to 'post_title' or `$text`. Default 'thumbnail'.
2683
 * @param string       $text Optional. Link text. Default false.
2684
 */
2685
function next_image_link( $size = 'thumbnail', $text = false ) {
2686
	adjacent_image_link(false, $size, $text);
0 ignored issues
show
It seems like $text defined by parameter $text on line 2685 can also be of type string; however, adjacent_image_link() does only seem to accept boolean, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2687
}
2688
2689
/**
2690
 * Displays next or previous image link that has the same post parent.
2691
 *
2692
 * Retrieves the current attachment object from the $post global.
2693
 *
2694
 * @since 2.5.0
2695
 *
2696
 * @param bool         $prev Optional. Whether to display the next (false) or previous (true) link. Default true.
2697
 * @param string|array $size Optional. Image size. Accepts any valid image size, or an array of width and height
2698
 *                           values in pixels (in that order). Default 'thumbnail'.
2699
 * @param bool         $text Optional. Link text. Default false.
2700
 */
2701
function adjacent_image_link( $prev = true, $size = 'thumbnail', $text = false ) {
2702
	$post = get_post();
2703
	$attachments = array_values( get_children( array( 'post_parent' => $post->post_parent, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order ID' ) ) );
2704
2705
	foreach ( $attachments as $k => $attachment ) {
2706
		if ( $attachment->ID == $post->ID ) {
2707
			break;
2708
		}
2709
	}
2710
2711
	$output = '';
2712
	$attachment_id = 0;
2713
2714
	if ( $attachments ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $attachments of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
2715
		$k = $prev ? $k - 1 : $k + 1;
0 ignored issues
show
The variable $k does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
2716
2717
		if ( isset( $attachments[ $k ] ) ) {
2718
			$attachment_id = $attachments[ $k ]->ID;
2719
			$output = wp_get_attachment_link( $attachment_id, $size, true, false, $text );
2720
		}
2721
	}
2722
2723
	$adjacent = $prev ? 'previous' : 'next';
2724
2725
	/**
2726
	 * Filters the adjacent image link.
2727
	 *
2728
	 * The dynamic portion of the hook name, `$adjacent`, refers to the type of adjacency,
2729
	 * either 'next', or 'previous'.
2730
	 *
2731
	 * @since 3.5.0
2732
	 *
2733
	 * @param string $output        Adjacent image HTML markup.
2734
	 * @param int    $attachment_id Attachment ID
2735
	 * @param string $size          Image size.
2736
	 * @param string $text          Link text.
2737
	 */
2738
	echo apply_filters( "{$adjacent}_image_link", $output, $attachment_id, $size, $text );
2739
}
2740
2741
/**
2742
 * Retrieves taxonomies attached to given the attachment.
2743
 *
2744
 * @since 2.5.0
2745
 * @since 4.7.0 Introduced the `$output` parameter.
2746
 *
2747
 * @param int|array|object $attachment Attachment ID, data array, or data object.
2748
 * @param string           $output     Output type. 'names' to return an array of taxonomy names,
2749
 *                                     or 'objects' to return an array of taxonomy objects.
2750
 *                                     Default is 'names'.
2751
 * @return array Empty array on failure. List of taxonomies on success.
2752
 */
2753
function get_attachment_taxonomies( $attachment, $output = 'names' ) {
2754
	if ( is_int( $attachment ) ) {
2755
		$attachment = get_post( $attachment );
2756
	} elseif ( is_array( $attachment ) ) {
2757
		$attachment = (object) $attachment;
2758
	}
2759
	if ( ! is_object($attachment) )
2760
		return array();
2761
2762
	$file = get_attached_file( $attachment->ID );
2763
	$filename = basename( $file );
2764
2765
	$objects = array('attachment');
2766
2767
	if ( false !== strpos($filename, '.') )
2768
		$objects[] = 'attachment:' . substr($filename, strrpos($filename, '.') + 1);
2769
	if ( !empty($attachment->post_mime_type) ) {
2770
		$objects[] = 'attachment:' . $attachment->post_mime_type;
2771
		if ( false !== strpos($attachment->post_mime_type, '/') )
2772
			foreach ( explode('/', $attachment->post_mime_type) as $token )
2773
				if ( !empty($token) )
2774
					$objects[] = "attachment:$token";
2775
	}
2776
2777
	$taxonomies = array();
2778
	foreach ( $objects as $object ) {
2779
		if ( $taxes = get_object_taxonomies( $object, $output ) ) {
2780
			$taxonomies = array_merge( $taxonomies, $taxes );
2781
		}
2782
	}
2783
2784
	if ( 'names' === $output ) {
2785
		$taxonomies = array_unique( $taxonomies );
2786
	}
2787
2788
	return $taxonomies;
2789
}
2790
2791
/**
2792
 * Retrieves all of the taxonomy names that are registered for attachments.
2793
 *
2794
 * Handles mime-type-specific taxonomies such as attachment:image and attachment:video.
2795
 *
2796
 * @since 3.5.0
2797
 *
2798
 * @see get_taxonomies()
2799
 *
2800
 * @param string $output Optional. The type of taxonomy output to return. Accepts 'names' or 'objects'.
2801
 *                       Default 'names'.
2802
 * @return array The names of all taxonomy of $object_type.
2803
 */
2804
function get_taxonomies_for_attachments( $output = 'names' ) {
2805
	$taxonomies = array();
2806
	foreach ( get_taxonomies( array(), 'objects' ) as $taxonomy ) {
2807
		foreach ( $taxonomy->object_type as $object_type ) {
2808
			if ( 'attachment' == $object_type || 0 === strpos( $object_type, 'attachment:' ) ) {
2809
				if ( 'names' == $output )
2810
					$taxonomies[] = $taxonomy->name;
2811
				else
2812
					$taxonomies[ $taxonomy->name ] = $taxonomy;
2813
				break;
2814
			}
2815
		}
2816
	}
2817
2818
	return $taxonomies;
2819
}
2820
2821
/**
2822
 * Create new GD image resource with transparency support
2823
 *
2824
 * @todo: Deprecate if possible.
2825
 *
2826
 * @since 2.9.0
2827
 *
2828
 * @param int $width  Image width in pixels.
2829
 * @param int $height Image height in pixels..
2830
 * @return resource The GD image resource.
2831
 */
2832
function wp_imagecreatetruecolor($width, $height) {
2833
	$img = imagecreatetruecolor($width, $height);
2834
	if ( is_resource($img) && function_exists('imagealphablending') && function_exists('imagesavealpha') ) {
2835
		imagealphablending($img, false);
2836
		imagesavealpha($img, true);
2837
	}
2838
	return $img;
2839
}
2840
2841
/**
2842
 * Based on a supplied width/height example, return the biggest possible dimensions based on the max width/height.
2843
 *
2844
 * @since 2.9.0
2845
 *
2846
 * @see wp_constrain_dimensions()
2847
 *
2848
 * @param int $example_width  The width of an example embed.
2849
 * @param int $example_height The height of an example embed.
2850
 * @param int $max_width      The maximum allowed width.
2851
 * @param int $max_height     The maximum allowed height.
2852
 * @return array The maximum possible width and height based on the example ratio.
2853
 */
2854
function wp_expand_dimensions( $example_width, $example_height, $max_width, $max_height ) {
2855
	$example_width  = (int) $example_width;
2856
	$example_height = (int) $example_height;
2857
	$max_width      = (int) $max_width;
2858
	$max_height     = (int) $max_height;
2859
2860
	return wp_constrain_dimensions( $example_width * 1000000, $example_height * 1000000, $max_width, $max_height );
2861
}
2862
2863
/**
2864
 * Determines the maximum upload size allowed in php.ini.
2865
 *
2866
 * @since 2.5.0
2867
 *
2868
 * @return int Allowed upload size.
2869
 */
2870
function wp_max_upload_size() {
2871
	$u_bytes = wp_convert_hr_to_bytes( ini_get( 'upload_max_filesize' ) );
2872
	$p_bytes = wp_convert_hr_to_bytes( ini_get( 'post_max_size' ) );
2873
2874
	/**
2875
	 * Filters the maximum upload size allowed in php.ini.
2876
	 *
2877
	 * @since 2.5.0
2878
	 *
2879
	 * @param int $size    Max upload size limit in bytes.
2880
	 * @param int $u_bytes Maximum upload filesize in bytes.
2881
	 * @param int $p_bytes Maximum size of POST data in bytes.
2882
	 */
2883
	return apply_filters( 'upload_size_limit', min( $u_bytes, $p_bytes ), $u_bytes, $p_bytes );
2884
}
2885
2886
/**
2887
 * Returns a WP_Image_Editor instance and loads file into it.
2888
 *
2889
 * @since 3.5.0
2890
 *
2891
 * @param string $path Path to the file to load.
2892
 * @param array  $args Optional. Additional arguments for retrieving the image editor.
2893
 *                     Default empty array.
2894
 * @return WP_Image_Editor|WP_Error The WP_Image_Editor object if successful, an WP_Error
2895
 *                                  object otherwise.
2896
 */
2897
function wp_get_image_editor( $path, $args = array() ) {
2898
	$args['path'] = $path;
2899
2900
	if ( ! isset( $args['mime_type'] ) ) {
2901
		$file_info = wp_check_filetype( $args['path'] );
2902
2903
		// If $file_info['type'] is false, then we let the editor attempt to
2904
		// figure out the file type, rather than forcing a failure based on extension.
2905
		if ( isset( $file_info ) && $file_info['type'] )
2906
			$args['mime_type'] = $file_info['type'];
2907
	}
2908
2909
	$implementation = _wp_image_editor_choose( $args );
2910
2911
	if ( $implementation ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $implementation of type string|false is loosely compared to true; 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...
2912
		$editor = new $implementation( $path );
2913
		$loaded = $editor->load();
2914
2915
		if ( is_wp_error( $loaded ) )
2916
			return $loaded;
2917
2918
		return $editor;
2919
	}
2920
2921
	return new WP_Error( 'image_no_editor', __('No editor could be selected.') );
2922
}
2923
2924
/**
2925
 * Tests whether there is an editor that supports a given mime type or methods.
2926
 *
2927
 * @since 3.5.0
2928
 *
2929
 * @param string|array $args Optional. Array of arguments to retrieve the image editor supports.
2930
 *                           Default empty array.
2931
 * @return bool True if an eligible editor is found; false otherwise.
2932
 */
2933
function wp_image_editor_supports( $args = array() ) {
2934
	return (bool) _wp_image_editor_choose( $args );
0 ignored issues
show
It seems like $args defined by parameter $args on line 2933 can also be of type string; however, _wp_image_editor_choose() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
2935
}
2936
2937
/**
2938
 * Tests which editors are capable of supporting the request.
2939
 *
2940
 * @ignore
2941
 * @since 3.5.0
2942
 *
2943
 * @param array $args Optional. Array of arguments for choosing a capable editor. Default empty array.
2944
 * @return string|false Class name for the first editor that claims to support the request. False if no
2945
 *                     editor claims to support the request.
2946
 */
2947
function _wp_image_editor_choose( $args = array() ) {
2948
	require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
2949
	require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
2950
	require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
2951
	/**
2952
	 * Filters the list of image editing library classes.
2953
	 *
2954
	 * @since 3.5.0
2955
	 *
2956
	 * @param array $image_editors List of available image editors. Defaults are
2957
	 *                             'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD'.
2958
	 */
2959
	$implementations = apply_filters( 'wp_image_editors', array( 'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD' ) );
2960
2961
	foreach ( $implementations as $implementation ) {
2962
		if ( ! call_user_func( array( $implementation, 'test' ), $args ) )
2963
			continue;
2964
2965
		if ( isset( $args['mime_type'] ) &&
2966
			! call_user_func(
2967
				array( $implementation, 'supports_mime_type' ),
2968
				$args['mime_type'] ) ) {
2969
			continue;
2970
		}
2971
2972
		if ( isset( $args['methods'] ) &&
2973
			 array_diff( $args['methods'], get_class_methods( $implementation ) ) ) {
2974
			continue;
2975
		}
2976
2977
		return $implementation;
2978
	}
2979
2980
	return false;
2981
}
2982
2983
/**
2984
 * Prints default Plupload arguments.
2985
 *
2986
 * @since 3.4.0
2987
 */
2988
function wp_plupload_default_settings() {
2989
	$wp_scripts = wp_scripts();
2990
2991
	$data = $wp_scripts->get_data( 'wp-plupload', 'data' );
2992
	if ( $data && false !== strpos( $data, '_wpPluploadSettings' ) )
2993
		return;
2994
2995
	$max_upload_size = wp_max_upload_size();
2996
	$allowed_extensions = array_keys( get_allowed_mime_types() );
2997
	$extensions = array();
2998
	foreach ( $allowed_extensions as $extension ) {
2999
		$extensions = array_merge( $extensions, explode( '|', $extension ) );
3000
	}
3001
3002
	$defaults = array(
3003
		'runtimes'            => 'html5,flash,silverlight,html4',
3004
		'file_data_name'      => 'async-upload', // key passed to $_FILE.
3005
		'url'                 => admin_url( 'async-upload.php', 'relative' ),
3006
		'flash_swf_url'       => includes_url( 'js/plupload/plupload.flash.swf' ),
3007
		'silverlight_xap_url' => includes_url( 'js/plupload/plupload.silverlight.xap' ),
3008
		'filters' => array(
3009
			'max_file_size'   => $max_upload_size . 'b',
3010
			'mime_types'      => array( array( 'extensions' => implode( ',', $extensions ) ) ),
3011
		),
3012
	);
3013
3014
	// Currently only iOS Safari supports multiple files uploading but iOS 7.x has a bug that prevents uploading of videos
3015
	// when enabled. See #29602.
3016 View Code Duplication
	if ( wp_is_mobile() && strpos( $_SERVER['HTTP_USER_AGENT'], 'OS 7_' ) !== false &&
3017
		strpos( $_SERVER['HTTP_USER_AGENT'], 'like Mac OS X' ) !== false ) {
3018
3019
		$defaults['multi_selection'] = false;
3020
	}
3021
3022
	/**
3023
	 * Filters the Plupload default settings.
3024
	 *
3025
	 * @since 3.4.0
3026
	 *
3027
	 * @param array $defaults Default Plupload settings array.
3028
	 */
3029
	$defaults = apply_filters( 'plupload_default_settings', $defaults );
3030
3031
	$params = array(
3032
		'action' => 'upload-attachment',
3033
	);
3034
3035
	/**
3036
	 * Filters the Plupload default parameters.
3037
	 *
3038
	 * @since 3.4.0
3039
	 *
3040
	 * @param array $params Default Plupload parameters array.
3041
	 */
3042
	$params = apply_filters( 'plupload_default_params', $params );
3043
	$params['_wpnonce'] = wp_create_nonce( 'media-form' );
3044
	$defaults['multipart_params'] = $params;
3045
3046
	$settings = array(
3047
		'defaults' => $defaults,
3048
		'browser'  => array(
3049
			'mobile'    => wp_is_mobile(),
3050
			'supported' => _device_can_upload(),
3051
		),
3052
		'limitExceeded' => is_multisite() && ! is_upload_space_available()
3053
	);
3054
3055
	$script = 'var _wpPluploadSettings = ' . wp_json_encode( $settings ) . ';';
3056
3057
	if ( $data )
3058
		$script = "$data\n$script";
3059
3060
	$wp_scripts->add_data( 'wp-plupload', 'data', $script );
3061
}
3062
3063
/**
3064
 * Prepares an attachment post object for JS, where it is expected
3065
 * to be JSON-encoded and fit into an Attachment model.
3066
 *
3067
 * @since 3.5.0
3068
 *
3069
 * @param mixed $attachment Attachment ID or object.
3070
 * @return array|void Array of attachment details.
3071
 */
3072
function wp_prepare_attachment_for_js( $attachment ) {
3073
	if ( ! $attachment = get_post( $attachment ) )
3074
		return;
3075
3076
	if ( 'attachment' != $attachment->post_type )
3077
		return;
3078
3079
	$meta = wp_get_attachment_metadata( $attachment->ID );
3080 View Code Duplication
	if ( false !== strpos( $attachment->post_mime_type, '/' ) )
3081
		list( $type, $subtype ) = explode( '/', $attachment->post_mime_type );
3082
	else
3083
		list( $type, $subtype ) = array( $attachment->post_mime_type, '' );
3084
3085
	$attachment_url = wp_get_attachment_url( $attachment->ID );
3086
	$base_url = str_replace( wp_basename( $attachment_url ), '', $attachment_url );
0 ignored issues
show
It seems like $attachment_url defined by wp_get_attachment_url($attachment->ID) on line 3085 can also be of type false; however, wp_basename() 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...
3087
3088
	$response = array(
3089
		'id'          => $attachment->ID,
3090
		'title'       => $attachment->post_title,
3091
		'filename'    => wp_basename( get_attached_file( $attachment->ID ) ),
0 ignored issues
show
It seems like get_attached_file($attachment->ID) targeting get_attached_file() can also be of type false; however, wp_basename() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
3092
		'url'         => $attachment_url,
3093
		'link'        => get_attachment_link( $attachment->ID ),
3094
		'alt'         => get_post_meta( $attachment->ID, '_wp_attachment_image_alt', true ),
3095
		'author'      => $attachment->post_author,
3096
		'description' => $attachment->post_content,
3097
		'caption'     => $attachment->post_excerpt,
3098
		'name'        => $attachment->post_name,
3099
		'status'      => $attachment->post_status,
3100
		'uploadedTo'  => $attachment->post_parent,
3101
		'date'        => strtotime( $attachment->post_date_gmt ) * 1000,
3102
		'modified'    => strtotime( $attachment->post_modified_gmt ) * 1000,
3103
		'menuOrder'   => $attachment->menu_order,
3104
		'mime'        => $attachment->post_mime_type,
3105
		'type'        => $type,
3106
		'subtype'     => $subtype,
3107
		'icon'        => wp_mime_type_icon( $attachment->ID ),
3108
		'dateFormatted' => mysql2date( __( 'F j, Y' ), $attachment->post_date ),
3109
		'nonces'      => array(
3110
			'update' => false,
3111
			'delete' => false,
3112
			'edit'   => false
3113
		),
3114
		'editLink'   => false,
3115
		'meta'       => false,
3116
	);
3117
3118
	$author = new WP_User( $attachment->post_author );
3119
	if ( $author->exists() ) {
3120
		$response['authorName'] = html_entity_decode( $author->display_name, ENT_QUOTES, get_bloginfo( 'charset' ) );
3121
	} else {
3122
		$response['authorName'] = __( '(no author)' );
3123
	}
3124
3125
	if ( $attachment->post_parent ) {
3126
		$post_parent = get_post( $attachment->post_parent );
3127
	} else {
3128
		$post_parent = false;
3129
	}
3130
3131
	if ( $post_parent ) {
3132
		$parent_type = get_post_type_object( $post_parent->post_type );
3133
3134
		if ( $parent_type && $parent_type->show_ui && current_user_can( 'edit_post', $attachment->post_parent ) ) {
3135
			$response['uploadedToLink'] = get_edit_post_link( $attachment->post_parent, 'raw' );
3136
		}
3137
3138
		if ( $parent_type && current_user_can( 'read_post', $attachment->post_parent ) ) {
3139
			$response['uploadedToTitle'] = $post_parent->post_title ? $post_parent->post_title : __( '(no title)' );
3140
		}
3141
	}
3142
3143
	$attached_file = get_attached_file( $attachment->ID );
3144
3145
	if ( isset( $meta['filesize'] ) ) {
3146
		$bytes = $meta['filesize'];
3147
	} elseif ( file_exists( $attached_file ) ) {
3148
		$bytes = filesize( $attached_file );
3149
	} else {
3150
		$bytes = '';
3151
	}
3152
3153
	if ( $bytes ) {
3154
		$response['filesizeInBytes'] = $bytes;
3155
		$response['filesizeHumanReadable'] = size_format( $bytes );
3156
	}
3157
3158
	if ( current_user_can( 'edit_post', $attachment->ID ) ) {
3159
		$response['nonces']['update'] = wp_create_nonce( 'update-post_' . $attachment->ID );
3160
		$response['nonces']['edit'] = wp_create_nonce( 'image_editor-' . $attachment->ID );
3161
		$response['editLink'] = get_edit_post_link( $attachment->ID, 'raw' );
3162
	}
3163
3164
	if ( current_user_can( 'delete_post', $attachment->ID ) )
3165
		$response['nonces']['delete'] = wp_create_nonce( 'delete-post_' . $attachment->ID );
3166
3167
	if ( $meta && ( 'image' === $type || ! empty( $meta['sizes'] ) ) ) {
3168
		$sizes = array();
3169
3170
		/** This filter is documented in wp-admin/includes/media.php */
3171
		$possible_sizes = apply_filters( 'image_size_names_choose', array(
3172
			'thumbnail' => __('Thumbnail'),
3173
			'medium'    => __('Medium'),
3174
			'large'     => __('Large'),
3175
			'full'      => __('Full Size'),
3176
		) );
3177
		unset( $possible_sizes['full'] );
3178
3179
		// Loop through all potential sizes that may be chosen. Try to do this with some efficiency.
3180
		// First: run the image_downsize filter. If it returns something, we can use its data.
3181
		// If the filter does not return something, then image_downsize() is just an expensive
3182
		// way to check the image metadata, which we do second.
3183
		foreach ( $possible_sizes as $size => $label ) {
3184
3185
			/** This filter is documented in wp-includes/media.php */
3186
			if ( $downsize = apply_filters( 'image_downsize', false, $attachment->ID, $size ) ) {
3187
				if ( empty( $downsize[3] ) ) {
3188
					continue;
3189
				}
3190
3191
				$sizes[ $size ] = array(
3192
					'height'      => $downsize[2],
3193
					'width'       => $downsize[1],
3194
					'url'         => $downsize[0],
3195
					'orientation' => $downsize[2] > $downsize[1] ? 'portrait' : 'landscape',
3196
				);
3197
			} elseif ( isset( $meta['sizes'][ $size ] ) ) {
3198
				// Nothing from the filter, so consult image metadata if we have it.
3199
				$size_meta = $meta['sizes'][ $size ];
3200
3201
				// We have the actual image size, but might need to further constrain it if content_width is narrower.
3202
				// Thumbnail, medium, and full sizes are also checked against the site's height/width options.
3203
				list( $width, $height ) = image_constrain_size_for_editor( $size_meta['width'], $size_meta['height'], $size, 'edit' );
3204
3205
				$sizes[ $size ] = array(
3206
					'height'      => $height,
3207
					'width'       => $width,
3208
					'url'         => $base_url . $size_meta['file'],
3209
					'orientation' => $height > $width ? 'portrait' : 'landscape',
3210
				);
3211
			}
3212
		}
3213
3214
		if ( 'image' === $type ) {
3215
			$sizes['full'] = array( 'url' => $attachment_url );
3216
3217
			if ( isset( $meta['height'], $meta['width'] ) ) {
3218
				$sizes['full']['height'] = $meta['height'];
3219
				$sizes['full']['width'] = $meta['width'];
3220
				$sizes['full']['orientation'] = $meta['height'] > $meta['width'] ? 'portrait' : 'landscape';
3221
			}
3222
3223
			$response = array_merge( $response, $sizes['full'] );
3224
		} elseif ( $meta['sizes']['full']['file'] ) {
3225
			$sizes['full'] = array(
3226
				'url'         => $base_url . $meta['sizes']['full']['file'],
3227
				'height'      => $meta['sizes']['full']['height'],
3228
				'width'       => $meta['sizes']['full']['width'],
3229
				'orientation' => $meta['sizes']['full']['height'] > $meta['sizes']['full']['width'] ? 'portrait' : 'landscape'
3230
			);
3231
		}
3232
3233
		$response = array_merge( $response, array( 'sizes' => $sizes ) );
3234
	}
3235
3236
	if ( $meta && 'video' === $type ) {
3237
		if ( isset( $meta['width'] ) )
3238
			$response['width'] = (int) $meta['width'];
3239
		if ( isset( $meta['height'] ) )
3240
			$response['height'] = (int) $meta['height'];
3241
	}
3242
3243
	if ( $meta && ( 'audio' === $type || 'video' === $type ) ) {
3244
		if ( isset( $meta['length_formatted'] ) )
3245
			$response['fileLength'] = $meta['length_formatted'];
3246
3247
		$response['meta'] = array();
3248
		foreach ( wp_get_attachment_id3_keys( $attachment, 'js' ) as $key => $label ) {
3249
			$response['meta'][ $key ] = false;
3250
3251
			if ( ! empty( $meta[ $key ] ) ) {
3252
				$response['meta'][ $key ] = $meta[ $key ];
3253
			}
3254
		}
3255
3256
		$id = get_post_thumbnail_id( $attachment->ID );
3257 View Code Duplication
		if ( ! empty( $id ) ) {
3258
			list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'full' );
3259
			$response['image'] = compact( 'src', 'width', 'height' );
3260
			list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'thumbnail' );
3261
			$response['thumb'] = compact( 'src', 'width', 'height' );
3262
		} else {
3263
			$src = wp_mime_type_icon( $attachment->ID );
3264
			$width = 48;
3265
			$height = 64;
3266
			$response['image'] = compact( 'src', 'width', 'height' );
3267
			$response['thumb'] = compact( 'src', 'width', 'height' );
3268
		}
3269
	}
3270
3271
	if ( function_exists('get_compat_media_markup') )
3272
		$response['compat'] = get_compat_media_markup( $attachment->ID, array( 'in_modal' => true ) );
3273
3274
	/**
3275
	 * Filters the attachment data prepared for JavaScript.
3276
	 *
3277
	 * @since 3.5.0
3278
	 *
3279
	 * @param array      $response   Array of prepared attachment data.
3280
	 * @param int|object $attachment Attachment ID or object.
3281
	 * @param array      $meta       Array of attachment meta data.
3282
	 */
3283
	return apply_filters( 'wp_prepare_attachment_for_js', $response, $attachment, $meta );
3284
}
3285
3286
/**
3287
 * Enqueues all scripts, styles, settings, and templates necessary to use
3288
 * all media JS APIs.
3289
 *
3290
 * @since 3.5.0
3291
 *
3292
 * @global int       $content_width
3293
 * @global wpdb      $wpdb
3294
 * @global WP_Locale $wp_locale
3295
 *
3296
 * @param array $args {
3297
 *     Arguments for enqueuing media scripts.
3298
 *
3299
 *     @type int|WP_Post A post object or ID.
3300
 * }
3301
 */
3302
function wp_enqueue_media( $args = array() ) {
3303
	// Enqueue me just once per page, please.
3304
	if ( did_action( 'wp_enqueue_media' ) )
3305
		return;
3306
3307
	global $content_width, $wpdb, $wp_locale;
3308
3309
	$defaults = array(
3310
		'post' => null,
3311
	);
3312
	$args = wp_parse_args( $args, $defaults );
3313
3314
	// We're going to pass the old thickbox media tabs to `media_upload_tabs`
3315
	// to ensure plugins will work. We will then unset those tabs.
3316
	$tabs = array(
3317
		// handler action suffix => tab label
3318
		'type'     => '',
3319
		'type_url' => '',
3320
		'gallery'  => '',
3321
		'library'  => '',
3322
	);
3323
3324
	/** This filter is documented in wp-admin/includes/media.php */
3325
	$tabs = apply_filters( 'media_upload_tabs', $tabs );
3326
	unset( $tabs['type'], $tabs['type_url'], $tabs['gallery'], $tabs['library'] );
3327
3328
	$props = array(
3329
		'link'  => get_option( 'image_default_link_type' ), // db default is 'file'
3330
		'align' => get_option( 'image_default_align' ), // empty default
3331
		'size'  => get_option( 'image_default_size' ),  // empty default
3332
	);
3333
3334
	$exts = array_merge( wp_get_audio_extensions(), wp_get_video_extensions() );
3335
	$mimes = get_allowed_mime_types();
3336
	$ext_mimes = array();
3337 View Code Duplication
	foreach ( $exts as $ext ) {
3338
		foreach ( $mimes as $ext_preg => $mime_match ) {
3339
			if ( preg_match( '#' . $ext . '#i', $ext_preg ) ) {
3340
				$ext_mimes[ $ext ] = $mime_match;
3341
				break;
3342
			}
3343
		}
3344
	}
3345
3346
	/**
3347
	 * Allows showing or hiding the "Create Audio Playlist" button in the media library.
3348
	 *
3349
	 * By default, the "Create Audio Playlist" button will always be shown in
3350
	 * the media library.  If this filter returns `null`, a query will be run
3351
	 * to determine whether the media library contains any audio items.  This
3352
	 * was the default behavior prior to version 4.8.0, but this query is
3353
	 * expensive for large media libraries.
3354
	 *
3355
	 * @since 4.7.4
3356
	 * @since 4.8.0 The filter's default value is `true` rather than `null`.
3357
	 *
3358
	 * @link https://core.trac.wordpress.org/ticket/31071
3359
	 *
3360
	 * @param bool|null Whether to show the button, or `null` to decide based
3361
	 *                  on whether any audio files exist in the media library.
3362
	 */
3363
	$show_audio_playlist = apply_filters( 'media_library_show_audio_playlist', true );
3364
	if ( null === $show_audio_playlist ) {
3365
		$show_audio_playlist = $wpdb->get_var( "
3366
			SELECT ID
3367
			FROM $wpdb->posts
3368
			WHERE post_type = 'attachment'
3369
			AND post_mime_type LIKE 'audio%'
3370
			LIMIT 1
3371
		" );
3372
	}
3373
3374
	/**
3375
	 * Allows showing or hiding the "Create Video Playlist" button in the media library.
3376
	 *
3377
	 * By default, the "Create Video Playlist" button will always be shown in
3378
	 * the media library.  If this filter returns `null`, a query will be run
3379
	 * to determine whether the media library contains any video items.  This
3380
	 * was the default behavior prior to version 4.8.0, but this query is
3381
	 * expensive for large media libraries.
3382
	 *
3383
	 * @since 4.7.4
3384
	 * @since 4.8.0 The filter's default value is `true` rather than `null`.
3385
	 *
3386
	 * @link https://core.trac.wordpress.org/ticket/31071
3387
	 *
3388
	 * @param bool|null Whether to show the button, or `null` to decide based
3389
	 *                  on whether any video files exist in the media library.
3390
	 */
3391
	$show_video_playlist = apply_filters( 'media_library_show_video_playlist', true );
3392
	if ( null === $show_video_playlist ) {
3393
		$show_video_playlist = $wpdb->get_var( "
3394
			SELECT ID
3395
			FROM $wpdb->posts
3396
			WHERE post_type = 'attachment'
3397
			AND post_mime_type LIKE 'video%'
3398
			LIMIT 1
3399
		" );
3400
	}
3401
3402
	/**
3403
	 * Allows overriding the list of months displayed in the media library.
3404
	 *
3405
	 * By default (if this filter does not return an array), a query will be
3406
	 * run to determine the months that have media items.  This query can be
3407
	 * expensive for large media libraries, so it may be desirable for sites to
3408
	 * override this behavior.
3409
	 *
3410
	 * @since 4.7.4
3411
	 *
3412
	 * @link https://core.trac.wordpress.org/ticket/31071
3413
	 *
3414
	 * @param array|null An array of objects with `month` and `year`
3415
	 *                   properties, or `null` (or any other non-array value)
3416
	 *                   for default behavior.
3417
	 */
3418
	$months = apply_filters( 'media_library_months_with_files', null );
3419
	if ( ! is_array( $months ) ) {
3420
		$months = $wpdb->get_results( $wpdb->prepare( "
3421
			SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
3422
			FROM $wpdb->posts
3423
			WHERE post_type = %s
3424
			ORDER BY post_date DESC
3425
		", 'attachment' ) );
3426
	}
3427
	foreach ( $months as $month_year ) {
3428
		$month_year->text = sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $month_year->month ), $month_year->year );
3429
	}
3430
3431
	$settings = array(
3432
		'tabs'      => $tabs,
3433
		'tabUrl'    => add_query_arg( array( 'chromeless' => true ), admin_url('media-upload.php') ),
3434
		'mimeTypes' => wp_list_pluck( get_post_mime_types(), 0 ),
3435
		/** This filter is documented in wp-admin/includes/media.php */
3436
		'captions'  => ! apply_filters( 'disable_captions', '' ),
3437
		'nonce'     => array(
3438
			'sendToEditor' => wp_create_nonce( 'media-send-to-editor' ),
3439
			'wpRestApi'    => wp_create_nonce( 'wp_rest' ),
3440
		),
3441
		'post'    => array(
3442
			'id' => 0,
3443
		),
3444
		'defaultProps' => $props,
3445
		'attachmentCounts' => array(
3446
			'audio' => ( $show_audio_playlist ) ? 1 : 0,
3447
			'video' => ( $show_video_playlist ) ? 1 : 0,
3448
		),
3449
		'oEmbedProxyUrl' => rest_url( 'oembed/1.0/proxy' ),
3450
		'embedExts'    => $exts,
3451
		'embedMimes'   => $ext_mimes,
3452
		'contentWidth' => $content_width,
3453
		'months'       => $months,
3454
		'mediaTrash'   => MEDIA_TRASH ? 1 : 0,
3455
	);
3456
3457
	$post = null;
3458
	if ( isset( $args['post'] ) ) {
3459
		$post = get_post( $args['post'] );
3460
		$settings['post'] = array(
3461
			'id' => $post->ID,
3462
			'nonce' => wp_create_nonce( 'update-post_' . $post->ID ),
3463
		);
3464
3465
		$thumbnail_support = current_theme_supports( 'post-thumbnails', $post->post_type ) && post_type_supports( $post->post_type, 'thumbnail' );
3466 View Code Duplication
		if ( ! $thumbnail_support && 'attachment' === $post->post_type && $post->post_mime_type ) {
3467
			if ( wp_attachment_is( 'audio', $post ) ) {
3468
				$thumbnail_support = post_type_supports( 'attachment:audio', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:audio' );
3469
			} elseif ( wp_attachment_is( 'video', $post ) ) {
3470
				$thumbnail_support = post_type_supports( 'attachment:video', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:video' );
3471
			}
3472
		}
3473
3474
		if ( $thumbnail_support ) {
3475
			$featured_image_id = get_post_meta( $post->ID, '_thumbnail_id', true );
3476
			$settings['post']['featuredImageId'] = $featured_image_id ? $featured_image_id : -1;
3477
		}
3478
	}
3479
3480
	if ( $post ) {
3481
		$post_type_object = get_post_type_object( $post->post_type );
3482
	} else {
3483
		$post_type_object = get_post_type_object( 'post' );
3484
	}
3485
3486
	$strings = array(
3487
		// Generic
3488
		'url'         => __( 'URL' ),
3489
		'addMedia'    => __( 'Add Media' ),
3490
		'search'      => __( 'Search' ),
3491
		'select'      => __( 'Select' ),
3492
		'cancel'      => __( 'Cancel' ),
3493
		'update'      => __( 'Update' ),
3494
		'replace'     => __( 'Replace' ),
3495
		'remove'      => __( 'Remove' ),
3496
		'back'        => __( 'Back' ),
3497
		/* translators: This is a would-be plural string used in the media manager.
3498
		   If there is not a word you can use in your language to avoid issues with the
3499
		   lack of plural support here, turn it into "selected: %d" then translate it.
3500
		 */
3501
		'selected'    => __( '%d selected' ),
3502
		'dragInfo'    => __( 'Drag and drop to reorder media files.' ),
3503
3504
		// Upload
3505
		'uploadFilesTitle'  => __( 'Upload Files' ),
3506
		'uploadImagesTitle' => __( 'Upload Images' ),
3507
3508
		// Library
3509
		'mediaLibraryTitle'      => __( 'Media Library' ),
3510
		'insertMediaTitle'       => __( 'Insert Media' ),
3511
		'createNewGallery'       => __( 'Create a new gallery' ),
3512
		'createNewPlaylist'      => __( 'Create a new playlist' ),
3513
		'createNewVideoPlaylist' => __( 'Create a new video playlist' ),
3514
		'returnToLibrary'        => __( '&#8592; Return to library' ),
3515
		'allMediaItems'          => __( 'All media items' ),
3516
		'allDates'               => __( 'All dates' ),
3517
		'noItemsFound'           => __( 'No items found.' ),
3518
		'insertIntoPost'         => $post_type_object->labels->insert_into_item,
3519
		'unattached'             => __( 'Unattached' ),
3520
		'trash'                  => _x( 'Trash', 'noun' ),
3521
		'uploadedToThisPost'     => $post_type_object->labels->uploaded_to_this_item,
3522
		'warnDelete'             => __( "You are about to permanently delete this item from your site.\nThis action cannot be undone.\n 'Cancel' to stop, 'OK' to delete." ),
3523
		'warnBulkDelete'         => __( "You are about to permanently delete these items from your site.\nThis action cannot be undone.\n 'Cancel' to stop, 'OK' to delete." ),
3524
		'warnBulkTrash'          => __( "You are about to trash these items.\n  'Cancel' to stop, 'OK' to delete." ),
3525
		'bulkSelect'             => __( 'Bulk Select' ),
3526
		'cancelSelection'        => __( 'Cancel Selection' ),
3527
		'trashSelected'          => __( 'Trash Selected' ),
3528
		'untrashSelected'        => __( 'Untrash Selected' ),
3529
		'deleteSelected'         => __( 'Delete Selected' ),
3530
		'deletePermanently'      => __( 'Delete Permanently' ),
3531
		'apply'                  => __( 'Apply' ),
3532
		'filterByDate'           => __( 'Filter by date' ),
3533
		'filterByType'           => __( 'Filter by type' ),
3534
		'searchMediaLabel'       => __( 'Search Media' ),
3535
		'searchMediaPlaceholder' => __( 'Search media items...' ), // placeholder (no ellipsis)
3536
		'noMedia'                => __( 'No media files found.' ),
3537
3538
		// Library Details
3539
		'attachmentDetails'  => __( 'Attachment Details' ),
3540
3541
		// From URL
3542
		'insertFromUrlTitle' => __( 'Insert from URL' ),
3543
3544
		// Featured Images
3545
		'setFeaturedImageTitle' => $post_type_object->labels->featured_image,
3546
		'setFeaturedImage'      => $post_type_object->labels->set_featured_image,
3547
3548
		// Gallery
3549
		'createGalleryTitle' => __( 'Create Gallery' ),
3550
		'editGalleryTitle'   => __( 'Edit Gallery' ),
3551
		'cancelGalleryTitle' => __( '&#8592; Cancel Gallery' ),
3552
		'insertGallery'      => __( 'Insert gallery' ),
3553
		'updateGallery'      => __( 'Update gallery' ),
3554
		'addToGallery'       => __( 'Add to gallery' ),
3555
		'addToGalleryTitle'  => __( 'Add to Gallery' ),
3556
		'reverseOrder'       => __( 'Reverse order' ),
3557
3558
		// Edit Image
3559
		'imageDetailsTitle'     => __( 'Image Details' ),
3560
		'imageReplaceTitle'     => __( 'Replace Image' ),
3561
		'imageDetailsCancel'    => __( 'Cancel Edit' ),
3562
		'editImage'             => __( 'Edit Image' ),
3563
3564
		// Crop Image
3565
		'chooseImage' => __( 'Choose Image' ),
3566
		'selectAndCrop' => __( 'Select and Crop' ),
3567
		'skipCropping' => __( 'Skip Cropping' ),
3568
		'cropImage' => __( 'Crop Image' ),
3569
		'cropYourImage' => __( 'Crop your image' ),
3570
		'cropping' => __( 'Cropping&hellip;' ),
3571
		/* translators: 1: suggested width number, 2: suggested height number. */
3572
		'suggestedDimensions' => __( 'Suggested image dimensions: %1$s by %2$s pixels.' ),
3573
		'cropError' => __( 'There has been an error cropping your image.' ),
3574
3575
		// Edit Audio
3576
		'audioDetailsTitle'     => __( 'Audio Details' ),
3577
		'audioReplaceTitle'     => __( 'Replace Audio' ),
3578
		'audioAddSourceTitle'   => __( 'Add Audio Source' ),
3579
		'audioDetailsCancel'    => __( 'Cancel Edit' ),
3580
3581
		// Edit Video
3582
		'videoDetailsTitle'     => __( 'Video Details' ),
3583
		'videoReplaceTitle'     => __( 'Replace Video' ),
3584
		'videoAddSourceTitle'   => __( 'Add Video Source' ),
3585
		'videoDetailsCancel'    => __( 'Cancel Edit' ),
3586
		'videoSelectPosterImageTitle' => __( 'Select Poster Image' ),
3587
		'videoAddTrackTitle'	=> __( 'Add Subtitles' ),
3588
3589
 		// Playlist
3590
 		'playlistDragInfo'    => __( 'Drag and drop to reorder tracks.' ),
3591
 		'createPlaylistTitle' => __( 'Create Audio Playlist' ),
3592
 		'editPlaylistTitle'   => __( 'Edit Audio Playlist' ),
3593
 		'cancelPlaylistTitle' => __( '&#8592; Cancel Audio Playlist' ),
3594
 		'insertPlaylist'      => __( 'Insert audio playlist' ),
3595
 		'updatePlaylist'      => __( 'Update audio playlist' ),
3596
 		'addToPlaylist'       => __( 'Add to audio playlist' ),
3597
 		'addToPlaylistTitle'  => __( 'Add to Audio Playlist' ),
3598
3599
 		// Video Playlist
3600
 		'videoPlaylistDragInfo'    => __( 'Drag and drop to reorder videos.' ),
3601
 		'createVideoPlaylistTitle' => __( 'Create Video Playlist' ),
3602
 		'editVideoPlaylistTitle'   => __( 'Edit Video Playlist' ),
3603
 		'cancelVideoPlaylistTitle' => __( '&#8592; Cancel Video Playlist' ),
3604
 		'insertVideoPlaylist'      => __( 'Insert video playlist' ),
3605
 		'updateVideoPlaylist'      => __( 'Update video playlist' ),
3606
 		'addToVideoPlaylist'       => __( 'Add to video playlist' ),
3607
 		'addToVideoPlaylistTitle'  => __( 'Add to Video Playlist' ),
3608
	);
3609
3610
	/**
3611
	 * Filters the media view settings.
3612
	 *
3613
	 * @since 3.5.0
3614
	 *
3615
	 * @param array   $settings List of media view settings.
3616
	 * @param WP_Post $post     Post object.
3617
	 */
3618
	$settings = apply_filters( 'media_view_settings', $settings, $post );
3619
3620
	/**
3621
	 * Filters the media view strings.
3622
	 *
3623
	 * @since 3.5.0
3624
	 *
3625
	 * @param array   $strings List of media view strings.
3626
	 * @param WP_Post $post    Post object.
3627
	 */
3628
	$strings = apply_filters( 'media_view_strings', $strings,  $post );
3629
3630
	$strings['settings'] = $settings;
3631
3632
	// Ensure we enqueue media-editor first, that way media-views is
3633
	// registered internally before we try to localize it. see #24724.
3634
	wp_enqueue_script( 'media-editor' );
3635
	wp_localize_script( 'media-views', '_wpMediaViewsL10n', $strings );
3636
3637
	wp_enqueue_script( 'media-audiovideo' );
3638
	wp_enqueue_style( 'media-views' );
3639
	if ( is_admin() ) {
3640
		wp_enqueue_script( 'mce-view' );
3641
		wp_enqueue_script( 'image-edit' );
3642
	}
3643
	wp_enqueue_style( 'imgareaselect' );
3644
	wp_plupload_default_settings();
3645
3646
	require_once ABSPATH . WPINC . '/media-template.php';
3647
	add_action( 'admin_footer', 'wp_print_media_templates' );
3648
	add_action( 'wp_footer', 'wp_print_media_templates' );
3649
	add_action( 'customize_controls_print_footer_scripts', 'wp_print_media_templates' );
3650
3651
	/**
3652
	 * Fires at the conclusion of wp_enqueue_media().
3653
	 *
3654
	 * @since 3.5.0
3655
	 */
3656
	do_action( 'wp_enqueue_media' );
3657
}
3658
3659
/**
3660
 * Retrieves media attached to the passed post.
3661
 *
3662
 * @since 3.6.0
3663
 *
3664
 * @param string      $type Mime type.
3665
 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
3666
 * @return array Found attachments.
3667
 */
3668
function get_attached_media( $type, $post = 0 ) {
3669
	if ( ! $post = get_post( $post ) )
3670
		return array();
3671
3672
	$args = array(
3673
		'post_parent' => $post->ID,
3674
		'post_type' => 'attachment',
3675
		'post_mime_type' => $type,
3676
		'posts_per_page' => -1,
3677
		'orderby' => 'menu_order',
3678
		'order' => 'ASC',
3679
	);
3680
3681
	/**
3682
	 * Filters arguments used to retrieve media attached to the given post.
3683
	 *
3684
	 * @since 3.6.0
3685
	 *
3686
	 * @param array  $args Post query arguments.
3687
	 * @param string $type Mime type of the desired media.
3688
	 * @param mixed  $post Post ID or object.
3689
	 */
3690
	$args = apply_filters( 'get_attached_media_args', $args, $type, $post );
3691
3692
	$children = get_children( $args );
3693
3694
	/**
3695
	 * Filters the list of media attached to the given post.
3696
	 *
3697
	 * @since 3.6.0
3698
	 *
3699
	 * @param array  $children Associative array of media attached to the given post.
3700
	 * @param string $type     Mime type of the media desired.
3701
	 * @param mixed  $post     Post ID or object.
3702
	 */
3703
	return (array) apply_filters( 'get_attached_media', $children, $type, $post );
3704
}
3705
3706
/**
3707
 * Check the content blob for an audio, video, object, embed, or iframe tags.
3708
 *
3709
 * @since 3.6.0
3710
 *
3711
 * @param string $content A string which might contain media data.
3712
 * @param array  $types   An array of media types: 'audio', 'video', 'object', 'embed', or 'iframe'.
0 ignored issues
show
Should the type for parameter $types not be array|null? Also, consider making the array more specific, something like array<String>, or String[].

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive. In addition it looks for parameters that have the generic type array and suggests a stricter type like array<String>.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
3713
 * @return array A list of found HTML media embeds.
3714
 */
3715
function get_media_embedded_in_content( $content, $types = null ) {
3716
	$html = array();
3717
3718
	/**
3719
	 * Filters the embedded media types that are allowed to be returned from the content blob.
3720
	 *
3721
	 * @since 4.2.0
3722
	 *
3723
	 * @param array $allowed_media_types An array of allowed media types. Default media types are
3724
	 *                                   'audio', 'video', 'object', 'embed', and 'iframe'.
3725
	 */
3726
	$allowed_media_types = apply_filters( 'media_embedded_in_content_allowed_types', array( 'audio', 'video', 'object', 'embed', 'iframe' ) );
3727
3728
	if ( ! empty( $types ) ) {
3729
		if ( ! is_array( $types ) ) {
3730
			$types = array( $types );
3731
		}
3732
3733
		$allowed_media_types = array_intersect( $allowed_media_types, $types );
3734
	}
3735
3736
	$tags = implode( '|', $allowed_media_types );
3737
3738
	if ( preg_match_all( '#<(?P<tag>' . $tags . ')[^<]*?(?:>[\s\S]*?<\/(?P=tag)>|\s*\/>)#', $content, $matches ) ) {
3739
		foreach ( $matches[0] as $match ) {
3740
			$html[] = $match;
3741
		}
3742
	}
3743
3744
	return $html;
3745
}
3746
3747
/**
3748
 * Retrieves galleries from the passed post's content.
3749
 *
3750
 * @since 3.6.0
3751
 *
3752
 * @param int|WP_Post $post Post ID or object.
3753
 * @param bool        $html Optional. Whether to return HTML or data in the array. Default true.
3754
 * @return array A list of arrays, each containing gallery data and srcs parsed
3755
 *               from the expanded shortcode.
3756
 */
3757
function get_post_galleries( $post, $html = true ) {
3758
	if ( ! $post = get_post( $post ) )
3759
		return array();
3760
3761
	if ( ! has_shortcode( $post->post_content, 'gallery' ) )
3762
		return array();
3763
3764
	$galleries = array();
3765
	if ( preg_match_all( '/' . get_shortcode_regex() . '/s', $post->post_content, $matches, PREG_SET_ORDER ) ) {
3766
		foreach ( $matches as $shortcode ) {
3767
			if ( 'gallery' === $shortcode[2] ) {
3768
				$srcs = array();
3769
3770
				$shortcode_attrs = shortcode_parse_atts( $shortcode[3] );
3771
				if ( ! is_array( $shortcode_attrs ) ) {
3772
					$shortcode_attrs = array();
3773
				}
3774
3775
				// Specify the post id of the gallery we're viewing if the shortcode doesn't reference another post already.
3776
				if ( ! isset( $shortcode_attrs['id'] ) ) {
3777
					$shortcode[3] .= ' id="' . intval( $post->ID ) . '"';
3778
				}
3779
3780
				$gallery = do_shortcode_tag( $shortcode );
3781
				if ( $html ) {
3782
					$galleries[] = $gallery;
3783
				} else {
3784
					preg_match_all( '#src=([\'"])(.+?)\1#is', $gallery, $src, PREG_SET_ORDER );
3785
					if ( ! empty( $src ) ) {
3786
						foreach ( $src as $s ) {
3787
							$srcs[] = $s[2];
3788
						}
3789
					}
3790
3791
					$galleries[] = array_merge(
3792
						$shortcode_attrs,
3793
						array(
3794
							'src' => array_values( array_unique( $srcs ) )
3795
						)
3796
					);
3797
				}
3798
			}
3799
		}
3800
	}
3801
3802
	/**
3803
	 * Filters the list of all found galleries in the given post.
3804
	 *
3805
	 * @since 3.6.0
3806
	 *
3807
	 * @param array   $galleries Associative array of all found post galleries.
3808
	 * @param WP_Post $post      Post object.
3809
	 */
3810
	return apply_filters( 'get_post_galleries', $galleries, $post );
3811
}
3812
3813
/**
3814
 * Check a specified post's content for gallery and, if present, return the first
3815
 *
3816
 * @since 3.6.0
3817
 *
3818
 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
3819
 * @param bool        $html Optional. Whether to return HTML or data. Default is true.
3820
 * @return string|array Gallery data and srcs parsed from the expanded shortcode.
3821
 */
3822
function get_post_gallery( $post = 0, $html = true ) {
3823
	$galleries = get_post_galleries( $post, $html );
3824
	$gallery = reset( $galleries );
3825
3826
	/**
3827
	 * Filters the first-found post gallery.
3828
	 *
3829
	 * @since 3.6.0
3830
	 *
3831
	 * @param array       $gallery   The first-found post gallery.
3832
	 * @param int|WP_Post $post      Post ID or object.
3833
	 * @param array       $galleries Associative array of all found post galleries.
3834
	 */
3835
	return apply_filters( 'get_post_gallery', $gallery, $post, $galleries );
3836
}
3837
3838
/**
3839
 * Retrieve the image srcs from galleries from a post's content, if present
3840
 *
3841
 * @since 3.6.0
3842
 *
3843
 * @see get_post_galleries()
3844
 *
3845
 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global `$post`.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
3846
 * @return array A list of lists, each containing image srcs parsed.
3847
 *               from an expanded shortcode
3848
 */
3849
function get_post_galleries_images( $post = 0 ) {
3850
	$galleries = get_post_galleries( $post, false );
3851
	return wp_list_pluck( $galleries, 'src' );
3852
}
3853
3854
/**
3855
 * Checks a post's content for galleries and return the image srcs for the first found gallery
3856
 *
3857
 * @since 3.6.0
3858
 *
3859
 * @see get_post_gallery()
3860
 *
3861
 * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global `$post`.
0 ignored issues
show
Consider making the type for parameter $post a bit more specific; maybe use integer.
Loading history...
3862
 * @return array A list of a gallery's image srcs in order.
3863
 */
3864
function get_post_gallery_images( $post = 0 ) {
3865
	$gallery = get_post_gallery( $post, false );
3866
	return empty( $gallery['src'] ) ? array() : $gallery['src'];
3867
}
3868
3869
/**
3870
 * Maybe attempts to generate attachment metadata, if missing.
3871
 *
3872
 * @since 3.9.0
3873
 *
3874
 * @param WP_Post $attachment Attachment object.
3875
 */
3876
function wp_maybe_generate_attachment_metadata( $attachment ) {
3877
	if ( empty( $attachment ) || ( empty( $attachment->ID ) || ! $attachment_id = (int) $attachment->ID ) ) {
3878
		return;
3879
	}
3880
3881
	$file = get_attached_file( $attachment_id );
3882
	$meta = wp_get_attachment_metadata( $attachment_id );
3883
	if ( empty( $meta ) && file_exists( $file ) ) {
3884
		$_meta = get_post_meta( $attachment_id );
3885
		$regeneration_lock = 'wp_generating_att_' . $attachment_id;
3886
		if ( ! array_key_exists( '_wp_attachment_metadata', $_meta ) && ! get_transient( $regeneration_lock ) ) {
3887
			set_transient( $regeneration_lock, $file );
3888
			wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file ) );
0 ignored issues
show
It seems like $file defined by get_attached_file($attachment_id) on line 3881 can also be of type false; however, wp_generate_attachment_metadata() 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...
3889
			delete_transient( $regeneration_lock );
3890
		}
3891
	}
3892
}
3893
3894
/**
3895
 * Tries to convert an attachment URL into a post ID.
3896
 *
3897
 * @since 4.0.0
3898
 *
3899
 * @global wpdb $wpdb WordPress database abstraction object.
3900
 *
3901
 * @param string $url The URL to resolve.
3902
 * @return int The found post ID, or 0 on failure.
3903
 */
3904
function attachment_url_to_postid( $url ) {
3905
	global $wpdb;
3906
3907
	$dir = wp_get_upload_dir();
3908
	$path = $url;
3909
3910
	$site_url = parse_url( $dir['url'] );
3911
	$image_path = parse_url( $path );
3912
3913
	//force the protocols to match if needed
3914
	if ( isset( $image_path['scheme'] ) && ( $image_path['scheme'] !== $site_url['scheme'] ) ) {
3915
		$path = str_replace( $image_path['scheme'], $site_url['scheme'], $path );
3916
	}
3917
3918
	if ( 0 === strpos( $path, $dir['baseurl'] . '/' ) ) {
3919
		$path = substr( $path, strlen( $dir['baseurl'] . '/' ) );
3920
	}
3921
3922
	$sql = $wpdb->prepare(
3923
		"SELECT post_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attached_file' AND meta_value = %s",
3924
		$path
3925
	);
3926
	$post_id = $wpdb->get_var( $sql );
3927
3928
	/**
3929
	 * Filters an attachment id found by URL.
3930
	 *
3931
	 * @since 4.2.0
3932
	 *
3933
	 * @param int|null $post_id The post_id (if any) found by the function.
3934
	 * @param string   $url     The URL being looked up.
3935
	 */
3936
	return (int) apply_filters( 'attachment_url_to_postid', $post_id, $url );
3937
}
3938
3939
/**
3940
 * Returns the URLs for CSS files used in an iframe-sandbox'd TinyMCE media view.
3941
 *
3942
 * @since 4.0.0
3943
 *
3944
 * @return array The relevant CSS file URLs.
0 ignored issues
show
Consider making the return type a bit more specific; maybe use string[].

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
3945
 */
3946
function wpview_media_sandbox_styles() {
3947
 	$version = 'ver=' . get_bloginfo( 'version' );
3948
 	$mediaelement = includes_url( "js/mediaelement/mediaelementplayer.min.css?$version" );
3949
 	$wpmediaelement = includes_url( "js/mediaelement/wp-mediaelement.css?$version" );
3950
3951
	return array( $mediaelement, $wpmediaelement );
3952
}
3953