PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 All 504 releases
← All changes | class.photon.php +25 -1361 12.0.3 → 16.3-a.1 View file →
@@ -2,1388 +2,52 @@
2 2 /**
3 3 * Class for photon functionality.
4 4 *
5 5 * @package automattic/jetpack
6 + * @deprecated 12.2 Use Automattic\Jetpack\Image_CDN\Image_CDN instead.
6 7 */
7 8
8 -use Automattic\Jetpack\Assets;
9 +use Automattic\Jetpack\Image_CDN\Image_CDN;
9 10
10 11 /**
11 12 * Class Jetpack_Photon
13 + *
14 + * @deprecated 12.2 Use Automattic\Jetpack\Image_CDN\Image_CDN instead.
12 15 */
13 16 class Jetpack_Photon {
14 - /**
15 - * Singleton.
16 - *
17 - * @var null
18 - */
19 - private static $instance = null;
20 17
21 18 /**
22 - * Allowed extensions.
19 + * Forward all method calls to the Image_CDN class.
23 20 *
24 - * @var string[] Allowed extensions must match https://code.trac.wordpress.org/browser/photon/index.php#L41
25 - */
26 - protected static $extensions = array(
27 - 'gif',
28 - 'jpg',
29 - 'jpeg',
30 - 'png',
31 - 'webp', // Jetpack assumes Photon_OpenCV backend class is being used on the server. See link in docblock.
32 - );
33 -
34 - /**
35 - * Image sizes.
21 + * @param string $name The name of the method.
22 + * @param array $arguments The arguments to pass to the method.
36 23 *
37 - * Don't access this directly. Instead, use self::image_sizes() so it's actually populated with something.
38 - *
39 - * @var array Image sizes.
24 + * @throws Exception If the method is not found.
40 25 */
41 - protected static $image_sizes = null;
42 -
43 - /**
44 - * Singleton implementation
45 - *
46 - * @return object
47 - */
48 - public static function instance() {
49 - if ( ! is_a( self::$instance, 'Jetpack_Photon' ) ) {
50 - self::$instance = new Jetpack_Photon();
51 - self::$instance->setup();
26 + public function __call( $name, $arguments ) {
27 + if ( method_exists( Image_CDN::class, $name ) ) {
28 + _deprecated_function( __CLASS__ . '::' . esc_html( $name ), 'jetpack-12.2', 'Automattic\Jetpack\Image_CDN\Image_CDN::' . esc_html( $name ) );
29 + return Image_CDN::instance()->$name( ...$arguments );
30 + } else {
31 + // Handle cases where the method is not found
32 + throw new Exception( sprintf( 'Undefined method: %s', esc_html( $name ) ) );
52 33 }
53 -
54 - return self::$instance;
55 34 }
56 35
57 36 /**
58 - * Silence is golden.
59 - */
60 - private function __construct() {}
61 -
62 - /**
63 - * Register actions and filters, but only if basic Photon functions are available.
64 - * The basic functions are found in ./functions.photon.php.
37 + * Forward all static method calls to the Image_CDN class.
65 38 *
66 - * @uses add_action, add_filter
67 - * @return null
68 - */
69 - private function setup() {
70 - if ( ! function_exists( 'jetpack_photon_url' ) ) {
71 - return;
72 - }
73 -
74 - // Images in post content and galleries.
75 - add_filter( 'the_content', array( __CLASS__, 'filter_the_content' ), 999999 );
76 - add_filter( 'get_post_galleries', array( __CLASS__, 'filter_the_galleries' ), 999999 );
77 - add_filter( 'widget_media_image_instance', array( __CLASS__, 'filter_the_image_widget' ), 999999 );
78 -
79 - // Core image retrieval.
80 - add_filter( 'image_downsize', array( $this, 'filter_image_downsize' ), 10, 3 );
81 - add_filter( 'rest_request_before_callbacks', array( $this, 'should_rest_photon_image_downsize' ), 10, 3 );
82 - add_action( 'rest_after_insert_attachment', array( $this, 'should_rest_photon_image_downsize_insert_attachment' ), 10, 2 );
83 - add_filter( 'rest_request_after_callbacks', array( $this, 'cleanup_rest_photon_image_downsize' ) );
84 -
85 - // Responsive image srcset substitution.
86 - add_filter( 'wp_calculate_image_srcset', array( $this, 'filter_srcset_array' ), 10, 5 );
87 - add_filter( 'wp_calculate_image_sizes', array( $this, 'filter_sizes' ), 1, 2 ); // Early so themes can still easily filter.
88 -
89 - // Helpers for maniuplated images.
90 - add_action( 'wp_enqueue_scripts', array( $this, 'action_wp_enqueue_scripts' ), 9 );
91 -
92 - /**
93 - * Allow Photon to disable uploaded images resizing and use its own resize capabilities instead.
94 - *
95 - * @module photon
96 - *
97 - * @since 7.1.0
98 - *
99 - * @param bool false Should Photon enable noresize mode. Default to false.
100 - */
101 - if ( apply_filters( 'jetpack_photon_noresize_mode', false ) ) {
102 - $this->enable_noresize_mode();
103 - }
104 - }
105 -
106 - /**
107 - * Enables the noresize mode for Photon, allowing to avoid intermediate size files generation.
108 - */
109 - private function enable_noresize_mode() {
110 - require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.jetpack-photon-image-sizes.php';
111 -
112 - // The main objective of noresize mode is to disable additional resized image versions creation.
113 - // This filter handles removal of additional sizes.
114 - add_filter( 'intermediate_image_sizes_advanced', array( __CLASS__, 'filter_photon_noresize_intermediate_sizes' ) );
115 -
116 - // Load the noresize srcset solution on priority of 20, allowing other plugins to set sizes earlier.
117 - add_filter( 'wp_get_attachment_metadata', array( __CLASS__, 'filter_photon_norezise_maybe_inject_sizes' ), 20, 2 );
118 -
119 - // Photonize thumbnail URLs in the API response.
120 - add_filter( 'rest_api_thumbnail_size_urls', array( __CLASS__, 'filter_photon_noresize_thumbnail_urls' ) );
121 -
122 - // This allows to assign the Photon domain to images that normally use the home URL as base.
123 - add_filter( 'jetpack_photon_domain', array( __CLASS__, 'filter_photon_norezise_domain' ), 10, 2 );
124 -
125 - add_filter( 'the_content', array( __CLASS__, 'filter_content_add' ), 0 );
126 -
127 - // Jetpack hooks in at six nines (999999) so this filter does at seven.
128 - add_filter( 'the_content', array( __CLASS__, 'filter_content_remove' ), 9999999 );
129 -
130 - // Regular Photon operation mode filter doesn't run when is_admin(), so we need an additional filter.
131 - // This is temporary until Jetpack allows more easily running these filters for is_admin().
132 - if ( is_admin() ) {
133 - add_filter( 'image_downsize', array( $this, 'filter_image_downsize' ), 5, 3 );
134 -
135 - // Allows any image that gets passed to Photon to be resized via Photon.
136 - add_filter( 'jetpack_photon_admin_allow_image_downsize', '__return_true' );
137 - }
138 - }
139 -
140 - /**
141 - * This is our catch-all to strip dimensions from intermediate images in content.
142 - * Since this primarily only impacts post_content we do a little dance to add the filter early
143 - * to `the_content` and then remove it later on in the same hook.
39 + * @param string $name The name of the method.
40 + * @param array $arguments The arguments to pass to the method.
144 41 *
145 - * @param String $content the post content.
146 - * @return String the post content unchanged.
42 + * @throws Exception If the method is not found.
147 43 */
148 - public static function filter_content_add( $content ) {
149 - add_filter( 'jetpack_photon_pre_image_url', array( __CLASS__, 'strip_image_dimensions_maybe' ) );
150 - return $content;
151 - }
152 -
153 - /**
154 - * Removing the content filter that was set previously.
155 - *
156 - * @param String $content the post content.
157 - * @return String the post content unchanged.
158 - */
159 - public static function filter_content_remove( $content ) {
160 - remove_filter( 'jetpack_photon_pre_image_url', array( __CLASS__, 'strip_image_dimensions_maybe' ) );
161 - return $content;
162 - }
163 -
164 - /**
165 - * Short circuits the Photon filter to enable Photon processing for any URL.
166 - *
167 - * @param String $photon_url a proposed Photon URL for the media file.
168 - *
169 - * @return String an URL to be used for the media file.
170 - */
171 - public static function filter_photon_norezise_domain( $photon_url ) {
172 - return $photon_url;
173 - }
174 -
175 - /**
176 - * Disables intermediate sizes to disallow resizing.
177 - *
178 - * @return array Empty array.
179 - */
180 - public static function filter_photon_noresize_intermediate_sizes() {
181 - return array();
182 - }
183 -
184 - /**
185 - * Filter thumbnail URLS to not generate.
186 - *
187 - * @param array $sizes Image sizes.
188 - *
189 - * @return mixed
190 - */
191 - public static function filter_photon_noresize_thumbnail_urls( $sizes ) {
192 - foreach ( $sizes as $size => $url ) {
193 - $parts = explode( '?', $url );
194 - $arguments = isset( $parts[1] ) ? $parts[1] : array();
195 -
196 - $sizes[ $size ] = jetpack_photon_url( $url, wp_parse_args( $arguments ) );
197 - }
198 -
199 - return $sizes;
200 - }
201 -
202 - /**
203 - * Inject image sizes to attachment metadata.
204 - *
205 - * @param array $data Attachment metadata.
206 - * @param int $attachment_id Attachment's post ID.
207 - *
208 - * @return array Attachment metadata.
209 - */
210 - public static function filter_photon_norezise_maybe_inject_sizes( $data, $attachment_id ) {
211 - // Can't do much if data is empty.
212 - if ( empty( $data ) ) {
213 - return $data;
214 - }
215 - $sizes_already_exist = (
216 - true === is_array( $data )
217 - && true === array_key_exists( 'sizes', $data )
218 - && true === is_array( $data['sizes'] )
219 - && false === empty( $data['sizes'] )
220 - );
221 - if ( $sizes_already_exist ) {
222 - return $data;
223 - }
224 - // Missing some critical data we need to determine sizes, not processing.
225 - if ( ! isset( $data['file'] )
226 - || ! isset( $data['width'] )
227 - || ! isset( $data['height'] )
228 - ) {
229 - return $data;
230 - }
231 -
232 - $mime_type = get_post_mime_type( $attachment_id );
233 - $attachment_is_image = preg_match( '!^image/!', $mime_type );
234 -
235 - if ( 1 === $attachment_is_image ) {
236 - $image_sizes = new Jetpack_Photon_ImageSizes( $attachment_id, $data );
237 - $data['sizes'] = $image_sizes->generate_sizes_meta();
238 - }
239 - return $data;
240 - }
241 -
242 - /**
243 - * Inject image sizes to Jetpack REST API responses. This wraps the filter_photon_norezise_maybe_inject_sizes function.
244 - *
245 - * @param array $sizes Attachment sizes data.
246 - * @param int $attachment_id Attachment's post ID.
247 - *
248 - * @return array Attachment sizes array.
249 - */
250 - public static function filter_photon_norezise_maybe_inject_sizes_api( $sizes, $attachment_id ) {
251 - return self::filter_photon_norezise_maybe_inject_sizes( wp_get_attachment_metadata( $attachment_id ), $attachment_id );
252 - }
253 -
254 - /**
255 - * * IN-CONTENT IMAGE MANIPULATION FUNCTIONS
256 - **/
257 -
258 - /**
259 - * Match all images and any relevant <a> tags in a block of HTML.
260 - *
261 - * @param string $content Some HTML.
262 - * @return array An array of $images matches, where $images[0] is
263 - * an array of full matches, and the link_url, img_tag,
264 - * and img_url keys are arrays of those matches.
265 - */
266 - public static function parse_images_from_html( $content ) {
267 - $images = array();
268 -
269 - if ( preg_match_all( '#(?:<a[^>]+?href=["|\'](?P<link_url>[^\s]+?)["|\'][^>]*?>\s*)?(?P<img_tag><(?:img|amp-img|amp-anim)[^>]*?\s+?src=["|\'](?P<img_url>[^\s]+?)["|\'].*?>){1}(?:\s*</a>)?#is', $content, $images ) ) {
270 - foreach ( $images as $key => $unused ) {
271 - // Simplify the output as much as possible, mostly for confirming test results.
272 - if ( is_numeric( $key ) && $key > 0 ) {
273 - unset( $images[ $key ] );
274 - }
275 - }
276 -
277 - return $images;
278 - }
279 -
280 - return array();
281 - }
282 -
283 - /**
284 - * Try to determine height and width from strings WP appends to resized image filenames.
285 - *
286 - * @param string $src The image URL.
287 - * @return array An array consisting of width and height.
288 - */
289 - public static function parse_dimensions_from_filename( $src ) {
290 - $width_height_string = array();
291 -
292 - if ( preg_match( '#-(\d+)x(\d+)\.(?:' . implode( '|', self::$extensions ) . '){1}$#i', $src, $width_height_string ) ) {
293 - $width = (int) $width_height_string[1];
294 - $height = (int) $width_height_string[2];
295 -
296 - if ( $width && $height ) {
297 - return array( $width, $height );
298 - }
299 - }
300 -
301 - return array( false, false );
302 - }
303 -
304 - /**
305 - * Identify images in post content, and if images are local (uploaded to the current site), pass through Photon.
306 - *
307 - * @param string $content The content.
308 - *
309 - * @uses self::validate_image_url, apply_filters, jetpack_photon_url, esc_url
310 - * @filter the_content
311 - *
312 - * @return string
313 - */
314 - public static function filter_the_content( $content ) {
315 - $images = self::parse_images_from_html( $content );
316 -
317 - if ( ! empty( $images ) ) {
318 - $content_width = Jetpack::get_content_width();
319 -
320 - $image_sizes = self::image_sizes();
321 -
322 - $upload_dir = wp_get_upload_dir();
323 -
324 - foreach ( $images[0] as $index => $tag ) {
325 - // Default to resize, though fit may be used in certain cases where a dimension cannot be ascertained.
326 - $transform = 'resize';
327 -
328 - // Start with a clean attachment ID each time.
329 - $attachment_id = false;
330 -
331 - // Flag if we need to munge a fullsize URL.
332 - $fullsize_url = false;
333 -
334 - // Identify image source.
335 - $src_orig = $images['img_url'][ $index ];
336 - $src = $src_orig;
337 -
338 - /**
339 - * Allow specific images to be skipped by Photon.
340 - *
341 - * @module photon
342 - *
343 - * @since 2.0.3
344 - *
345 - * @param bool false Should Photon ignore this image. Default to false.
346 - * @param string $src Image URL.
347 - * @param string $tag Image Tag (Image HTML output).
348 - */
349 - if ( apply_filters( 'jetpack_photon_skip_image', false, $src, $tag ) ) {
350 - continue;
351 - }
352 -
353 - // Support Automattic's Lazy Load plugin.
354 - // Can't modify $tag yet as we need unadulterated version later.
355 - if ( preg_match( '#data-lazy-src=["|\'](.+?)["|\']#i', $images['img_tag'][ $index ], $lazy_load_src ) ) {
356 - $placeholder_src_orig = $src;
357 - $placeholder_src = $placeholder_src_orig;
358 - $src_orig = $lazy_load_src[1];
359 - $src = $src_orig;
360 - } elseif ( preg_match( '#data-lazy-original=["|\'](.+?)["|\']#i', $images['img_tag'][ $index ], $lazy_load_src ) ) {
361 - $placeholder_src_orig = $src;
362 - $placeholder_src = $placeholder_src_orig;
363 - $src_orig = $lazy_load_src[1];
364 - $src = $src_orig;
365 - }
366 -
367 - // Check if image URL should be used with Photon.
368 - if ( self::validate_image_url( $src ) ) {
369 - // Find the width and height attributes.
370 - $width = false;
371 - $height = false;
372 -
373 - // First, check the image tag. Note we only check for pixel sizes now; HTML4 percentages have never been correctly
374 - // supported, so we stopped pretending to support them in JP 9.1.0.
375 - if ( preg_match( '#[\s|"|\']width=["|\']?([\d%]+)["|\']?#i', $images['img_tag'][ $index ], $width_string ) ) {
376 - $width = false === strpos( $width_string[1], '%' ) ? $width_string[1] : false;
377 - }
378 -
379 - if ( preg_match( '#[\s|"|\']height=["|\']?([\d%]+)["|\']?#i', $images['img_tag'][ $index ], $height_string ) ) {
380 - $height = false === strpos( $height_string[1], '%' ) ? $height_string[1] : false;
381 - }
382 -
383 - // Detect WP registered image size from HTML class.
384 - if ( preg_match( '#class=["|\']?[^"\']*size-([^"\'\s]+)[^"\']*["|\']?#i', $images['img_tag'][ $index ], $size ) ) {
385 - $size = array_pop( $size );
386 -
387 - if ( false === $width && false === $height && 'full' !== $size && array_key_exists( $size, $image_sizes ) ) {
388 - $width = (int) $image_sizes[ $size ]['width'];
389 - $height = (int) $image_sizes[ $size ]['height'];
390 - $transform = $image_sizes[ $size ]['crop'] ? 'resize' : 'fit';
391 - }
392 - } else {
393 - unset( $size );
394 - }
395 -
396 - // WP Attachment ID, if uploaded to this site.
397 - if (
398 - preg_match( '#class=["|\']?[^"\']*wp-image-([\d]+)[^"\']*["|\']?#i', $images['img_tag'][ $index ], $attachment_id ) &&
399 - 0 === strpos( $src, $upload_dir['baseurl'] ) &&
400 - /**
401 - * Filter whether an image using an attachment ID in its class has to be uploaded to the local site to go through Photon.
402 - *
403 - * @module photon
404 - *
405 - * @since 2.0.3
406 - *
407 - * @param bool false Was the image uploaded to the local site. Default to false.
408 - * @param array $args {
409 - * Array of image details.
410 - *
411 - * @type $src Image URL.
412 - * @type tag Image tag (Image HTML output).
413 - * @type $images Array of information about the image.
414 - * @type $index Image index.
415 - * }
416 - */
417 - apply_filters( 'jetpack_photon_image_is_local', false, compact( 'src', 'tag', 'images', 'index' ) )
418 - ) {
419 - $attachment_id = (int) array_pop( $attachment_id );
420 -
421 - if ( $attachment_id ) {
422 - $attachment = get_post( $attachment_id );
423 -
424 - // Basic check on returned post object.
425 - if ( is_object( $attachment ) && ! is_wp_error( $attachment ) && 'attachment' === $attachment->post_type ) {
426 - $src_per_wp = wp_get_attachment_image_src( $attachment_id, isset( $size ) ? $size : 'full' );
427 -
428 - if ( self::validate_image_url( $src_per_wp[0] ) ) {
429 - $src = $src_per_wp[0];
430 - $fullsize_url = true;
431 -
432 - // Prevent image distortion if a detected dimension exceeds the image's natural dimensions.
433 - if ( ( false !== $width && $width > $src_per_wp[1] ) || ( false !== $height && $height > $src_per_wp[2] ) ) {
434 - $width = false === $width ? false : min( $width, $src_per_wp[1] );
435 - $height = false === $height ? false : min( $height, $src_per_wp[2] );
436 - }
437 -
438 - // If no width and height are found, max out at source image's natural dimensions.
439 - // Otherwise, respect registered image sizes' cropping setting.
440 - if ( false === $width && false === $height ) {
441 - $width = $src_per_wp[1];
442 - $height = $src_per_wp[2];
443 - $transform = 'fit';
444 - } elseif ( isset( $size ) && array_key_exists( $size, $image_sizes ) && isset( $image_sizes[ $size ]['crop'] ) ) {
445 - $transform = (bool) $image_sizes[ $size ]['crop'] ? 'resize' : 'fit';
446 - }
447 - }
448 - } else {
449 - unset( $attachment_id );
450 - unset( $attachment );
451 - }
452 - }
453 - }
454 -
455 - // If image tag lacks width and height arguments, try to determine from strings WP appends to resized image filenames.
456 - if ( false === $width && false === $height ) {
457 - list( $width, $height ) = self::parse_dimensions_from_filename( $src );
458 - }
459 -
460 - $width_orig = $width;
461 - $height_orig = $height;
462 - $transform_orig = $transform;
463 -
464 - // If width is available, constrain to $content_width.
465 - if ( false !== $width && is_numeric( $content_width ) && $width > $content_width ) {
466 - if ( false !== $height ) {
467 - $height = round( ( $content_width * $height ) / $width );
468 - }
469 - $width = $content_width;
470 - }
471 -
472 - // Set a width if none is found and $content_width is available.
473 - // If width is set in this manner and height is available, use `fit` instead of `resize` to prevent skewing.
474 - if ( false === $width && is_numeric( $content_width ) ) {
475 - $width = (int) $content_width;
476 -
477 - if ( false !== $height ) {
478 - $transform = 'fit';
479 - }
480 - }
481 -
482 - // Detect if image source is for a custom-cropped thumbnail and prevent further URL manipulation.
483 - if ( ! $fullsize_url && preg_match_all( '#-e[a-z0-9]+(-\d+x\d+)?\.(' . implode( '|', self::$extensions ) . '){1}$#i', basename( $src ), $filename ) ) {
484 - $fullsize_url = true;
485 - }
486 -
487 - // Build URL, first maybe removing WP's resized string so we pass the original image to Photon.
488 - if ( ! $fullsize_url && 0 === strpos( $src, $upload_dir['baseurl'] ) ) {
489 - $src = self::strip_image_dimensions_maybe( $src );
490 - }
491 -
492 - // Build array of Photon args and expose to filter before passing to Photon URL function.
493 - $args = array();
494 -
495 - if ( false !== $width && false !== $height ) {
496 - $args[ $transform ] = $width . ',' . $height;
497 - } elseif ( false !== $width ) {
498 - $args['w'] = $width;
499 - } elseif ( false !== $height ) {
500 - $args['h'] = $height;
501 - }
502 -
503 - /**
504 - * Filter the array of Photon arguments added to an image when it goes through Photon.
505 - * By default, only includes width and height values.
506 - *
507 - * @see https://developer.wordpress.com/docs/photon/api/
508 - *
509 - * @module photon
510 - *
511 - * @since 2.0.0
512 - *
513 - * @param array $args Array of Photon Arguments.
514 - * @param array $details {
515 - * Array of image details.
516 - *
517 - * @type string $tag Image tag (Image HTML output).
518 - * @type string $src Image URL.
519 - * @type string $src_orig Original Image URL.
520 - * @type int|false $width Image width.
521 - * @type int|false $height Image height.
522 - * @type int|false $width_orig Original image width before constrained by content_width.
523 - * @type int|false $height_orig Original Image height before constrained by content_width.
524 - * @type string $transform Transform.
525 - * @type string $transform_orig Original transform before constrained by content_width.
526 - * }
527 - */
528 - $args = apply_filters( 'jetpack_photon_post_image_args', $args, compact( 'tag', 'src', 'src_orig', 'width', 'height', 'width_orig', 'height_orig', 'transform', 'transform_orig' ) );
529 -
530 - $photon_url = jetpack_photon_url( $src, $args );
531 -
532 - // Modify image tag if Photon function provides a URL
533 - // Ensure changes are only applied to the current image by copying and modifying the matched tag, then replacing the entire tag with our modified version.
534 - if ( $src !== $photon_url ) {
535 - $new_tag = $tag;
536 -
537 - // If present, replace the link href with a Photoned URL for the full-size image.
538 - if ( ! empty( $images['link_url'][ $index ] ) && self::validate_image_url( $images['link_url'][ $index ] ) ) {
539 - $new_tag = preg_replace( '#(href=["|\'])' . preg_quote( $images['link_url'][ $index ], '#' ) . '(["|\'])#i', '\1' . jetpack_photon_url( $images['link_url'][ $index ] ) . '\2', $new_tag, 1 );
540 - }
541 -
542 - // Supplant the original source value with our Photon URL.
543 - $photon_url = esc_url( $photon_url );
544 - $new_tag = str_replace( $src_orig, $photon_url, $new_tag );
545 -
546 - // If Lazy Load is in use, pass placeholder image through Photon.
547 - if ( isset( $placeholder_src ) && self::validate_image_url( $placeholder_src ) ) {
548 - $placeholder_src = jetpack_photon_url( $placeholder_src );
549 -
550 - if ( $placeholder_src !== $placeholder_src_orig ) {
551 - $new_tag = str_replace( $placeholder_src_orig, esc_url( $placeholder_src ), $new_tag );
552 - }
553 -
554 - unset( $placeholder_src );
555 - }
556 -
557 - // If we are not transforming the image with resize, fit, or letterbox (lb), then we should remove
558 - // the width and height arguments (including HTML4 percentages) from the image to prevent distortion.
559 - // Even if $args['w'] and $args['h'] are present, Photon does not crop to those dimensions. Instead,
560 - // it appears to favor height.
561 - //
562 - // If we are transforming the image via one of those methods, let's update the width and height attributes.
563 - if ( empty( $args['resize'] ) && empty( $args['fit'] ) && empty( $args['lb'] ) ) {
564 - $new_tag = preg_replace( '#(?<=\s)(width|height)=["|\']?[\d%]+["|\']?\s?#i', '', $new_tag );
565 - } else {
566 - $resize_args = isset( $args['resize'] ) ? $args['resize'] : false;
567 - if ( false === $resize_args ) {
568 - $resize_args = ( ! $resize_args && isset( $args['fit'] ) )
569 - ? $args['fit']
570 - : false;
571 - }
572 - if ( false === $resize_args ) {
573 - $resize_args = ( ! $resize_args && isset( $args['lb'] ) )
574 - ? $args['lb']
575 - : false;
576 - }
577 -
578 - $resize_args = array_map( 'trim', explode( ',', $resize_args ) );
579 -
580 - // (?<=\s) - Ensure width or height attribute is preceded by a space
581 - // (width=["|\']?) - Matches, and captures, width=, width=", or width='
582 - // [\d%]+ - Matches 1 or more digits or percent signs
583 - // (["|\']?) - Matches, and captures, ", ', or empty string
584 - // \s - Ensures there's a space after the attribute
585 - $new_tag = preg_replace( '#(?<=\s)(width=["|\']?)[\d%]+(["|\']?)\s?#i', sprintf( '${1}%d${2} ', $resize_args[0] ), $new_tag );
586 - $new_tag = preg_replace( '#(?<=\s)(height=["|\']?)[\d%]+(["|\']?)\s?#i', sprintf( '${1}%d${2} ', $resize_args[1] ), $new_tag );
587 - }
588 -
589 - // Tag an image for dimension checking.
590 - if ( ! self::is_amp_endpoint() ) {
591 - $new_tag = preg_replace( '#(\s?/)?>(\s*</a>)?$#i', ' data-recalc-dims="1"\1>\2', $new_tag );
592 - }
593 -
594 - // Replace original tag with modified version.
595 - $content = str_replace( $tag, $new_tag, $content );
596 - }
597 - } elseif ( preg_match( '#^http(s)?://i[\d]{1}.wp.com#', $src ) && ! empty( $images['link_url'][ $index ] ) && self::validate_image_url( $images['link_url'][ $index ] ) ) {
598 - $new_tag = preg_replace( '#(href=["|\'])' . preg_quote( $images['link_url'][ $index ], '#' ) . '(["|\'])#i', '\1' . jetpack_photon_url( $images['link_url'][ $index ] ) . '\2', $tag, 1 );
599 -
600 - $content = str_replace( $tag, $new_tag, $content );
601 - }
602 - }
603 - }
604 -
605 - return $content;
606 - }
607 -
608 - /**
609 - * Filter Core galleries
610 - *
611 - * @param array $galleries Gallery array.
612 - *
613 - * @return array
614 - */
615 - public static function filter_the_galleries( $galleries ) {
616 - if ( empty( $galleries ) || ! is_array( $galleries ) ) {
617 - return $galleries;
618 - }
619 -
620 - // Pass by reference, so we can modify them in place.
621 - foreach ( $galleries as &$this_gallery ) {
622 - if ( is_string( $this_gallery ) ) {
623 - $this_gallery = self::filter_the_content( $this_gallery );
624 - }
625 - }
626 - unset( $this_gallery ); // break the reference.
627 -
628 - return $galleries;
629 - }
630 -
631 - /**
632 - * Runs the image widget through photon.
633 - *
634 - * @param array $instance Image widget instance data.
635 - * @return array
636 - */
637 - public static function filter_the_image_widget( $instance ) {
638 - if ( Jetpack::is_module_active( 'photon' ) && ! $instance['attachment_id'] && $instance['url'] ) {
639 - jetpack_photon_url(
640 - $instance['url'],
641 - array(
642 - 'w' => $instance['width'],
643 - 'h' => $instance['height'],
644 - )
645 - );
646 - }
647 -
648 - return $instance;
649 - }
650 -
651 - /**
652 - * * CORE IMAGE RETRIEVAL
653 - **/
654 -
655 - /**
656 - * Filter post thumbnail image retrieval, passing images through Photon
657 - *
658 - * @param string|bool $image Image URL.
659 - * @param int $attachment_id Attachment ID.
660 - * @param string|array $size Declared size or a size array.
661 - * @uses is_admin, apply_filters, wp_get_attachment_url, self::validate_image_url, this::image_sizes, jetpack_photon_url
662 - * @filter image_downsize
663 - * @return string|bool
664 - */
665 - public function filter_image_downsize( $image, $attachment_id, $size ) {
666 - // Don't foul up the admin side of things, unless a plugin wants to.
667 - if ( is_admin() &&
668 - /**
669 - * Provide plugins a way of running Photon for images in the WordPress Dashboard (wp-admin).
670 - *
671 - * Note: enabling this will result in Photon URLs added to your post content, which could make migrations across domains (and off Photon) a bit more challenging.
672 - *
673 - * @module photon
674 - *
675 - * @since 4.8.0
676 - *
677 - * @param bool false Stop Photon from being run on the Dashboard. Default to false.
678 - * @param array $args {
679 - * Array of image details.
680 - *
681 - * @type $image Image URL.
682 - * @type $attachment_id Attachment ID of the image.
683 - * @type $size Image size. Can be a string (name of the image size, e.g. full) or an array of width and height.
684 - * }
685 - */
686 - false === apply_filters( 'jetpack_photon_admin_allow_image_downsize', false, compact( 'image', 'attachment_id', 'size' ) )
687 - ) {
688 - return $image;
689 - }
690 -
691 - /**
692 - * Provide plugins a way of preventing Photon from being applied to images retrieved from WordPress Core.
693 - *
694 - * @module photon
695 - *
696 - * @since 2.0.0
697 - *
698 - * @param bool false Stop Photon from being applied to the image. Default to false.
699 - * @param array $args {
700 - * Array of image details.
701 - *
702 - * @type $image Image URL.
703 - * @type $attachment_id Attachment ID of the image.
704 - * @type $size Image size. Can be a string (name of the image size, e.g. full) or an array of width and height.
705 - * }
706 - */
707 - if ( apply_filters( 'jetpack_photon_override_image_downsize', false, compact( 'image', 'attachment_id', 'size' ) ) ) {
708 - return $image;
709 - }
710 -
711 - // Get the image URL and proceed with Photon-ification if successful.
712 - $image_url = wp_get_attachment_url( $attachment_id );
713 -
714 - // Set this to true later when we know we have size meta.
715 - $has_size_meta = false;
716 -
717 - if ( $image_url ) {
718 - // Check if image URL should be used with Photon.
719 - if ( ! self::validate_image_url( $image_url ) ) {
720 - return $image;
721 - }
722 -
723 - $intermediate = true; // For the fourth array item returned by the image_downsize filter.
724 -
725 - // If an image is requested with a size known to WordPress, use that size's settings with Photon.
726 - // WP states that `add_image_size()` should use a string for the name, but doesn't enforce that.
727 - // Due to differences in how Core and Photon check for the registered image size, we check both types.
728 - if ( ( is_string( $size ) || is_int( $size ) ) && array_key_exists( $size, self::image_sizes() ) ) {
729 - $image_args = self::image_sizes();
730 - $image_args = $image_args[ $size ];
731 -
732 - $photon_args = array();
733 -
734 - $image_meta = image_get_intermediate_size( $attachment_id, $size );
735 -
736 - // 'full' is a special case: We need consistent data regardless of the requested size.
737 - if ( 'full' === $size ) {
738 - $image_meta = wp_get_attachment_metadata( $attachment_id );
739 - $intermediate = false;
740 - } elseif ( ! $image_meta ) {
741 - // If we still don't have any image meta at this point, it's probably from a custom thumbnail size
742 - // for an image that was uploaded before the custom image was added to the theme. Try to determine the size manually.
743 - $image_meta = wp_get_attachment_metadata( $attachment_id );
744 -
745 - if ( isset( $image_meta['width'], $image_meta['height'] ) ) {
746 - $image_resized = image_resize_dimensions( $image_meta['width'], $image_meta['height'], $image_args['width'], $image_args['height'], $image_args['crop'] );
747 - if ( $image_resized ) { // This could be false when the requested image size is larger than the full-size image.
748 - $image_meta['width'] = $image_resized[6];
749 - $image_meta['height'] = $image_resized[7];
750 - }
751 - }
752 - }
753 -
754 - if ( isset( $image_meta['width'], $image_meta['height'] ) ) {
755 - $image_args['width'] = (int) $image_meta['width'];
756 - $image_args['height'] = (int) $image_meta['height'];
757 -
758 - list( $image_args['width'], $image_args['height'] ) = image_constrain_size_for_editor( $image_args['width'], $image_args['height'], $size, 'display' );
759 - $has_size_meta = true;
760 - }
761 -
762 - // Expose determined arguments to a filter before passing to Photon.
763 - $transform = $image_args['crop'] ? 'resize' : 'fit';
764 -
765 - // Check specified image dimensions and account for possible zero values; photon fails to resize if a dimension is zero.
766 - if ( 0 === $image_args['width'] || 0 === $image_args['height'] ) {
767 - if ( 0 === $image_args['width'] && 0 < $image_args['height'] ) {
768 - $photon_args['h'] = $image_args['height'];
769 - } elseif ( 0 === $image_args['height'] && 0 < $image_args['width'] ) {
770 - $photon_args['w'] = $image_args['width'];
771 - }
772 - } else {
773 - $image_meta = wp_get_attachment_metadata( $attachment_id );
774 - if ( ( 'resize' === $transform ) && $image_meta ) {
775 - if ( isset( $image_meta['width'], $image_meta['height'] ) ) {
776 - // Lets make sure that we don't upscale images since wp never upscales them as well.
777 - $smaller_width = ( ( $image_meta['width'] < $image_args['width'] ) ? $image_meta['width'] : $image_args['width'] );
778 - $smaller_height = ( ( $image_meta['height'] < $image_args['height'] ) ? $image_meta['height'] : $image_args['height'] );
779 -
780 - $photon_args[ $transform ] = $smaller_width . ',' . $smaller_height;
781 - }
782 - } else {
783 - $photon_args[ $transform ] = $image_args['width'] . ',' . $image_args['height'];
784 - }
785 - }
786 -
787 - /**
788 - * Filter the Photon Arguments added to an image when going through Photon, when that image size is a string.
789 - * Image size will be a string (e.g. "full", "medium") when it is known to WordPress.
790 - *
791 - * @module photon
792 - *
793 - * @since 2.0.0
794 - *
795 - * @param array $photon_args Array of Photon arguments.
796 - * @param array $args {
797 - * Array of image details.
798 - *
799 - * @type array $image_args Array of Image arguments (width, height, crop).
800 - * @type string $image_url Image URL.
801 - * @type int $attachment_id Attachment ID of the image.
802 - * @type string|int $size Image size. Can be a string (name of the image size, e.g. full) or an integer.
803 - * @type string $transform Value can be resize or fit.
804 - * @see https://developer.wordpress.com/docs/photon/api
805 - * }
806 - */
807 - $photon_args = apply_filters( 'jetpack_photon_image_downsize_string', $photon_args, compact( 'image_args', 'image_url', 'attachment_id', 'size', 'transform' ) );
808 -
809 - // Generate Photon URL.
810 - $image = array(
811 - jetpack_photon_url( $image_url, $photon_args ),
812 - $has_size_meta ? $image_args['width'] : false,
813 - $has_size_meta ? $image_args['height'] : false,
814 - $intermediate,
815 - );
816 - } elseif ( is_array( $size ) ) {
817 - // Pull width and height values from the provided array, if possible.
818 - $width = isset( $size[0] ) ? (int) $size[0] : false;
819 - $height = isset( $size[1] ) ? (int) $size[1] : false;
820 -
821 - // Don't bother if necessary parameters aren't passed.
822 - if ( ! $width || ! $height ) {
823 - return $image;
824 - }
825 -
826 - $image_meta = wp_get_attachment_metadata( $attachment_id );
827 - if ( isset( $image_meta['width'], $image_meta['height'] ) ) {
828 - $image_resized = image_resize_dimensions( $image_meta['width'], $image_meta['height'], $width, $height );
829 -
830 - if ( $image_resized ) { // This could be false when the requested image size is larger than the full-size image.
831 - $width = $image_resized[6];
832 - $height = $image_resized[7];
833 - } else {
834 - $width = $image_meta['width'];
835 - $height = $image_meta['height'];
836 - }
837 -
838 - $has_size_meta = true;
839 - }
840 -
841 - list( $width, $height ) = image_constrain_size_for_editor( $width, $height, $size );
842 -
843 - // Expose arguments to a filter before passing to Photon.
844 - $photon_args = array(
845 - 'fit' => $width . ',' . $height,
846 - );
847 -
848 - /**
849 - * Filter the Photon Arguments added to an image when going through Photon,
850 - * when the image size is an array of height and width values.
851 - *
852 - * @module photon
853 - *
854 - * @since 2.0.0
855 - *
856 - * @param array $photon_args Array of Photon arguments.
857 - * @param array $args {
858 - * Array of image details.
859 - *
860 - * @type $width Image width.
861 - * @type height Image height.
862 - * @type $image_url Image URL.
863 - * @type $attachment_id Attachment ID of the image.
864 - * }
865 - */
866 - $photon_args = apply_filters( 'jetpack_photon_image_downsize_array', $photon_args, compact( 'width', 'height', 'image_url', 'attachment_id' ) );
867 -
868 - // Generate Photon URL.
869 - $image = array(
870 - jetpack_photon_url( $image_url, $photon_args ),
871 - $has_size_meta ? $width : false,
872 - $has_size_meta ? $height : false,
873 - $intermediate,
874 - );
875 - }
876 - }
877 -
878 - return $image;
879 - }
880 -
881 - /**
882 - * Filters an array of image `srcset` values, replacing each URL with its Photon equivalent.
883 - *
884 - * @since 3.8.0
885 - * @since 4.0.4 Added automatically additional sizes beyond declared image sizes.
886 - *
887 - * @param array $sources An array of image urls and widths.
888 - * @param array $size_array The size array for srcset.
889 - * @param array $image_src The image srcs.
890 - * @param array $image_meta The image meta.
891 - * @param int $attachment_id Attachment ID.
892 - *
893 - * @uses self::validate_image_url, jetpack_photon_url, Jetpack_Photon::parse_from_filename
894 - * @uses Jetpack_Photon::strip_image_dimensions_maybe, Jetpack::get_content_width
895 - *
896 - * @return array An array of Photon image urls and widths.
897 - */
898 - public function filter_srcset_array( $sources = array(), $size_array = array(), $image_src = array(), $image_meta = array(), $attachment_id = 0 ) {
899 - if ( ! is_array( $sources ) || array() === $sources ) {
900 - return $sources;
901 - }
902 - $upload_dir = wp_get_upload_dir();
903 -
904 - foreach ( $sources as $i => $source ) {
905 - if ( ! self::validate_image_url( $source['url'] ) ) {
906 - continue;
907 - }
908 -
909 - /** This filter is already documented in class.photon.php */
910 - if ( apply_filters( 'jetpack_photon_skip_image', false, $source['url'], $source ) ) {
911 - continue;
912 - }
913 -
914 - $url = $source['url'];
915 - list( $width, $height ) = self::parse_dimensions_from_filename( $url );
916 -
917 - // It's quicker to get the full size with the data we have already, if available.
918 - if ( ! empty( $attachment_id ) ) {
919 - $url = wp_get_attachment_url( $attachment_id );
920 - } else {
921 - $url = self::strip_image_dimensions_maybe( $url );
922 - }
923 -
924 - $args = array();
925 - if ( 'w' === $source['descriptor'] ) {
926 - if ( $height && ( (int) $source['value'] === $width ) ) {
927 - $args['resize'] = $width . ',' . $height;
928 - } else {
929 - $args['w'] = $source['value'];
930 - }
931 - }
932 -
933 - $sources[ $i ]['url'] = jetpack_photon_url( $url, $args );
934 - }
935 -
936 - /**
937 - * At this point, $sources is the original srcset with Photonized URLs.
938 - * Now, we're going to construct additional sizes based on multiples of the content_width.
939 - * This will reduce the gap between the largest defined size and the original image.
940 - */
941 -
942 - /**
943 - * Filter the multiplier Photon uses to create new srcset items.
944 - * Return false to short-circuit and bypass auto-generation.
945 - *
946 - * @module photon
947 - *
948 - * @since 4.0.4
949 - *
950 - * @param array|bool $multipliers Array of multipliers to use or false to bypass.
951 - */
952 - $multipliers = apply_filters( 'jetpack_photon_srcset_multipliers', array( 2, 3 ) );
953 - $url = trailingslashit( $upload_dir['baseurl'] ) . $image_meta['file'];
954 -
955 - if (
956 - /** Short-circuit via jetpack_photon_srcset_multipliers filter. */
957 - is_array( $multipliers )
958 - /** This filter is already documented in class.photon.php */
959 - && ! apply_filters( 'jetpack_photon_skip_image', false, $url, null )
960 - /** Verify basic meta is intact. */
961 - && isset( $image_meta['width'] ) && isset( $image_meta['height'] ) && isset( $image_meta['file'] )
962 - /** Verify we have the requested width/height. */
963 - && isset( $size_array[0] ) && isset( $size_array[1] )
964 - ) {
965 -
966 - $fullwidth = $image_meta['width'];
967 - $fullheight = $image_meta['height'];
968 - $reqwidth = $size_array[0];
969 - $reqheight = $size_array[1];
970 -
971 - $constrained_size = wp_constrain_dimensions( $fullwidth, $fullheight, $reqwidth );
972 - $expected_size = array( $reqwidth, $reqheight );
973 -
974 - if ( abs( $constrained_size[0] - $expected_size[0] ) <= 1 && abs( $constrained_size[1] - $expected_size[1] ) <= 1 ) {
975 - $crop = 'soft';
976 - $base = Jetpack::get_content_width() ? Jetpack::get_content_width() : 1000; // Provide a default width if none set by the theme.
977 - } else {
978 - $crop = 'hard';
979 - $base = $reqwidth;
980 - }
981 -
982 - $currentwidths = array_keys( $sources );
983 - $newsources = null;
984 -
985 - foreach ( $multipliers as $multiplier ) {
986 -
987 - $newwidth = $base * $multiplier;
988 - foreach ( $currentwidths as $currentwidth ) {
989 - // If a new width would be within 100 pixes of an existing one or larger than the full size image, skip.
990 - if ( abs( $currentwidth - $newwidth ) < 50 || ( $newwidth > $fullwidth ) ) {
991 - continue 2; // Bump out back to the $multipliers as $multiplier.
992 - }
993 - } //end foreach ( $currentwidths as $currentwidth ){
994 -
995 - if ( 'soft' === $crop ) {
996 - $args = array(
997 - 'w' => $newwidth,
998 - );
999 - } else { // hard crop, e.g. add_image_size( 'example', 200, 200, true ).
1000 - $args = array(
1001 - 'zoom' => $multiplier,
1002 - 'resize' => $reqwidth . ',' . $reqheight,
1003 - );
1004 - }
1005 -
1006 - $newsources[ $newwidth ] = array(
1007 - 'url' => jetpack_photon_url( $url, $args ),
1008 - 'descriptor' => 'w',
1009 - 'value' => $newwidth,
1010 - );
1011 - } //end foreach ( $multipliers as $multiplier )
1012 - if ( is_array( $newsources ) ) {
1013 - $sources = array_replace( $sources, $newsources );
1014 - }
1015 - } //end if isset( $image_meta['width'] ) && isset( $image_meta['file'] ) )
1016 -
1017 - return $sources;
1018 - }
1019 -
1020 - /**
1021 - * Filters an array of image `sizes` values, using $content_width instead of image's full size.
1022 - *
1023 - * @since 4.0.4
1024 - * @since 4.1.0 Returns early for images not within the_content.
1025 - * @param array $sizes An array of media query breakpoints.
1026 - * @param array $size Width and height of the image.
1027 - * @uses Jetpack::get_content_width
1028 - * @return array An array of media query breakpoints.
1029 - */
1030 - public function filter_sizes( $sizes, $size ) {
1031 - if ( ! doing_filter( 'the_content' ) ) {
1032 - return $sizes;
1033 - }
1034 - $content_width = Jetpack::get_content_width();
1035 - if ( ! $content_width ) {
1036 - $content_width = 1000;
1037 - }
1038 -
1039 - if ( ( is_array( $size ) && $size[0] < $content_width ) ) {
1040 - return $sizes;
1041 - }
1042 -
1043 - return sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $content_width );
1044 - }
1045 -
1046 - /**
1047 - * * GENERAL FUNCTIONS
1048 - **/
1049 -
1050 - /**
1051 - * Ensure image URL is valid for Photon.
1052 - * Though Photon functions address some of the URL issues, we should avoid unnecessary processing if we know early on that the image isn't supported.
1053 - *
1054 - * @param string $url Image URL.
1055 - * @uses wp_parse_args
1056 - * @return bool
1057 - */
1058 - protected static function validate_image_url( $url ) {
1059 - $parsed_url = wp_parse_url( $url );
1060 -
1061 - if ( ! $parsed_url ) {
1062 - return false;
1063 - }
1064 -
1065 - // Parse URL and ensure needed keys exist, since the array returned by `wp_parse_url` only includes the URL components it finds.
1066 - $url_info = wp_parse_args(
1067 - $parsed_url,
1068 - array(
1069 - 'scheme' => null,
1070 - 'host' => null,
1071 - 'port' => null,
1072 - 'path' => null,
1073 - )
1074 - );
1075 -
1076 - // Bail if scheme isn't http or port is set that isn't port 80.
1077 - if (
1078 - ( 'http' !== $url_info['scheme'] || ! in_array( $url_info['port'], array( 80, null ), true ) ) &&
1079 - /**
1080 - * Allow Photon to fetch images that are served via HTTPS.
1081 - *
1082 - * @module photon
1083 - *
1084 - * @since 2.4.0
1085 - * @since 3.9.0 Default to false.
1086 - *
1087 - * @param bool $reject_https Should Photon ignore images using the HTTPS scheme. Default to false.
1088 - */
1089 - apply_filters( 'jetpack_photon_reject_https', false )
1090 - ) {
1091 - return false;
1092 - }
1093 -
1094 - // Bail if no host is found.
1095 - if ( $url_info['host'] === null ) {
1096 - return false;
1097 - }
1098 -
1099 - // Bail if the image already went through Photon.
1100 - if ( preg_match( '#^i[\d]{1}.wp.com$#i', $url_info['host'] ) ) {
1101 - return false;
1102 - }
1103 -
1104 - // Bail if no path is found.
1105 - if ( $url_info['path'] === null ) {
1106 - return false;
1107 - }
1108 -
1109 - // Ensure image extension is acceptable.
1110 - if ( ! in_array( strtolower( pathinfo( $url_info['path'], PATHINFO_EXTENSION ) ), self::$extensions, true ) ) {
1111 - return false;
1112 - }
1113 -
1114 - // If we got this far, we should have an acceptable image URL
1115 - // But let folks filter to decline if they prefer.
1116 - /**
1117 - * Overwrite the results of the validation steps an image goes through before to be considered valid to be used by Photon.
1118 - *
1119 - * @module photon
1120 - *
1121 - * @since 3.0.0
1122 - *
1123 - * @param bool true Is the image URL valid and can it be used by Photon. Default to true.
1124 - * @param string $url Image URL.
1125 - * @param array $parsed_url Array of information about the image.
1126 - */
1127 - return apply_filters( 'photon_validate_image_url', true, $url, $parsed_url );
1128 - }
1129 -
1130 - /**
1131 - * Checks if the file exists before it passes the file to photon.
1132 - *
1133 - * @param string $src The image URL.
1134 - * @return string
1135 - **/
1136 - public static function strip_image_dimensions_maybe( $src ) {
1137 - $stripped_src = $src;
1138 -
1139 - // Build URL, first removing WP's resized string so we pass the original image to Photon.
1140 - if ( preg_match( '#(-\d+x\d+)\.(' . implode( '|', self::$extensions ) . '){1}$#i', $src, $src_parts ) ) {
1141 - $stripped_src = str_replace( $src_parts[1], '', $src );
1142 - $upload_dir = wp_get_upload_dir();
1143 -
1144 - // Extracts the file path to the image minus the base url.
1145 - $file_path = substr( $stripped_src, strlen( $upload_dir['baseurl'] ) );
1146 -
1147 - if ( file_exists( $upload_dir['basedir'] . $file_path ) ) {
1148 - $src = $stripped_src;
1149 - }
1150 - }
1151 -
1152 - return $src;
1153 - }
1154 -
1155 - /**
1156 - * Provide an array of available image sizes and corresponding dimensions.
1157 - * Similar to get_intermediate_image_sizes() except that it includes image sizes' dimensions, not just their names.
1158 - *
1159 - * @global $wp_additional_image_sizes
1160 - * @uses get_option
1161 - * @return array
1162 - */
1163 - protected static function image_sizes() {
1164 - if ( null === self::$image_sizes ) {
1165 - global $_wp_additional_image_sizes;
1166 -
1167 - // Populate an array matching the data structure of $_wp_additional_image_sizes so we have a consistent structure for image sizes.
1168 - $images = array(
1169 - 'thumb' => array(
1170 - 'width' => (int) get_option( 'thumbnail_size_w' ),
1171 - 'height' => (int) get_option( 'thumbnail_size_h' ),
1172 - 'crop' => (bool) get_option( 'thumbnail_crop' ),
1173 - ),
1174 - 'medium' => array(
1175 - 'width' => (int) get_option( 'medium_size_w' ),
1176 - 'height' => (int) get_option( 'medium_size_h' ),
1177 - 'crop' => false,
1178 - ),
1179 - 'medium_large' => array(
1180 - 'width' => (int) get_option( 'medium_large_size_w' ),
1181 - 'height' => (int) get_option( 'medium_large_size_h' ),
1182 - 'crop' => false,
1183 - ),
1184 - 'large' => array(
1185 - 'width' => (int) get_option( 'large_size_w' ),
1186 - 'height' => (int) get_option( 'large_size_h' ),
1187 - 'crop' => false,
1188 - ),
1189 - 'full' => array(
1190 - 'width' => null,
1191 - 'height' => null,
1192 - 'crop' => false,
1193 - ),
1194 - );
1195 -
1196 - // Compatibility mapping as found in wp-includes/media.php.
1197 - $images['thumbnail'] = $images['thumb'];
1198 -
1199 - // Update class variable, merging in $_wp_additional_image_sizes if any are set.
1200 - if ( is_array( $_wp_additional_image_sizes ) && ! empty( $_wp_additional_image_sizes ) ) {
1201 - self::$image_sizes = array_merge( $images, $_wp_additional_image_sizes );
1202 - } else {
1203 - self::$image_sizes = $images;
1204 - }
1205 - }
1206 -
1207 - return is_array( self::$image_sizes ) ? self::$image_sizes : array();
1208 - }
1209 -
1210 - /**
1211 - * Pass og:image URLs through Photon
1212 - *
1213 - * @param array $tags Open graph tags.
1214 - * @param array $parameters Image parameters.
1215 - * @uses jetpack_photon_url
1216 - * @return array Open graph tags.
1217 - */
1218 - public function filter_open_graph_tags( $tags, $parameters ) {
1219 - if ( empty( $tags['og:image'] ) ) {
1220 - return $tags;
1221 - }
1222 -
1223 - $photon_args = array(
1224 - 'fit' => sprintf( '%d,%d', 2 * $parameters['image_width'], 2 * $parameters['image_height'] ),
1225 - );
1226 -
1227 - if ( is_array( $tags['og:image'] ) ) {
1228 - $images = array();
1229 - foreach ( $tags['og:image'] as $image ) {
1230 - $images[] = jetpack_photon_url( $image, $photon_args );
1231 - }
1232 - $tags['og:image'] = $images;
44 + public static function __callStatic( $name, $arguments ) {
45 + if ( method_exists( Image_CDN::class, $name ) ) {
46 + _deprecated_function( __CLASS__ . '::' . esc_html( $name ), 'jetpack-12.2', 'Automattic\Jetpack\Image_CDN\Image_CDN::' . esc_html( $name ) );
47 + return Image_CDN::$name( ...$arguments );
1233 48 } else {
1234 - $tags['og:image'] = jetpack_photon_url( $tags['og:image'], $photon_args );
49 + // Handle cases where the method is not found
50 + throw new Exception( sprintf( 'Undefined static method: %s', esc_html( $name ) ) );
1235 51 }
1236 -
1237 - return $tags;
1238 - }
1239 -
1240 - /**
1241 - * Enqueue Photon helper script
1242 - *
1243 - * @uses wp_enqueue_script, plugins_url
1244 - * @action wp_enqueue_script
1245 - * @return null
1246 - */
1247 - public function action_wp_enqueue_scripts() {
1248 - if ( self::is_amp_endpoint() ) {
1249 - return;
1250 - }
1251 - wp_enqueue_script(
1252 - 'jetpack-photon',
1253 - Assets::get_file_url_for_environment(
1254 - '_inc/build/photon/photon.min.js',
1255 - 'modules/photon/photon.js'
1256 - ),
1257 - array(),
1258 - 20191001,
1259 - true
1260 - );
1261 - }
1262 -
1263 - /**
1264 - * Determine if image_downsize should utilize Photon via REST API.
1265 - *
1266 - * The WordPress Block Editor (Gutenberg) and other REST API consumers using the wp/v2/media endpoint, especially in the "edit"
1267 - * context is more akin to the is_admin usage of Photon (see filter_image_downsize). Since consumers are trying to edit content in posts,
1268 - * Photon should not fire as it will fire later on display. By aborting an attempt to Photonize an image here, we
1269 - * prevents issues like https://github.com/Automattic/jetpack/issues/10580 .
1270 - *
1271 - * To determine if we're using the wp/v2/media endpoint, we hook onto the `rest_request_before_callbacks` filter and
1272 - * if determined we are using it in the edit context, we'll false out the `jetpack_photon_override_image_downsize` filter.
1273 - *
1274 - * @see Jetpack_Photon::filter_image_downsize()
1275 - *
1276 - * @param null|WP_Error $response REST API response.
1277 - * @param array $endpoint_data Endpoint data. Not used, but part of the filter.
1278 - * @param WP_REST_Request $request Request used to generate the response.
1279 - *
1280 - * @return null|WP_Error The original response object without modification.
1281 - */
1282 - public function should_rest_photon_image_downsize( $response, $endpoint_data, $request ) {
1283 - if ( ! is_a( $request, 'WP_REST_Request' ) ) {
1284 - return $response; // Something odd is happening. Do nothing and return the response.
1285 - }
1286 -
1287 - if ( is_wp_error( $response ) ) {
1288 - // If we're going to return an error, we don't need to do anything with Photon.
1289 - return $response;
1290 - }
1291 -
1292 - $this->should_rest_photon_image_downsize_override( $request );
1293 -
1294 - return $response;
1295 - }
1296 -
1297 - /**
1298 - * Helper function to check if a WP_REST_Request is the media endpoint in the edit context.
1299 - *
1300 - * @param WP_REST_Request $request The current REST request.
1301 - */
1302 - private function should_rest_photon_image_downsize_override( WP_REST_Request $request ) {
1303 - $route = $request->get_route();
1304 -
1305 - if (
1306 - (
1307 - false !== strpos( $route, 'wp/v2/media' )
1308 - && 'edit' === $request->get_param( 'context' )
1309 - )
1310 - || false !== strpos( $route, 'wpcom/v2/external-media/copy' )
1311 - || (bool) $request->get_header( 'x-wp-api-fetch-from-editor' )
1312 - ) {
1313 - // Don't use `__return_true()`: Use something unique. See ::_override_image_downsize_in_rest_edit_context()
1314 - // Late execution to avoid conflict with other plugins as we really don't want to run in this situation.
1315 - add_filter(
1316 - 'jetpack_photon_override_image_downsize',
1317 - array(
1318 - $this,
1319 - 'override_image_downsize_in_rest_edit_context',
1320 - ),
1321 - 999999
1322 - );
1323 - }
1324 - }
1325 -
1326 - /**
1327 - * Brings in should_rest_photon_image_downsize for the rest_after_insert_attachment hook.
1328 - *
1329 - * @since 8.7.0
1330 - *
1331 - * @param WP_Post $attachment Inserted or updated attachment object.
1332 - * @param WP_REST_Request $request Request object.
1333 - */
1334 - public function should_rest_photon_image_downsize_insert_attachment( WP_Post $attachment, WP_REST_Request $request ) {
1335 - if ( ! is_a( $request, 'WP_REST_Request' ) ) {
1336 - // Something odd is happening.
1337 - return;
1338 - }
1339 -
1340 - $this->should_rest_photon_image_downsize_override( $request );
1341 - }
1342 -
1343 - /**
1344 - * Remove the override we may have added in ::should_rest_photon_image_downsize()
1345 - * Since ::_override_image_downsize_in_rest_edit_context() is only
1346 - * every used here, we can always remove it without ever worrying
1347 - * about breaking any other configuration.
1348 - *
1349 - * @param mixed $response REST API Response.
1350 - * @return mixed Unchanged $response
1351 - */
1352 - public function cleanup_rest_photon_image_downsize( $response ) {
1353 - remove_filter(
1354 - 'jetpack_photon_override_image_downsize',
1355 - array(
1356 - $this,
1357 - 'override_image_downsize_in_rest_edit_context',
1358 - ),
1359 - 999999
1360 - );
1361 - return $response;
1362 - }
1363 -
1364 - /**
1365 - * Used internally by ::should_rest_photon_image_downsize() to not photonize
1366 - * image URLs in ?context=edit REST requests.
1367 - * MUST NOT be used anywhere else.
1368 - * We use a unique function instead of __return_true so that we can clean up
1369 - * after ourselves without breaking anyone else's filters.
1370 - *
1371 - * @internal
1372 - * @return true
1373 - */
1374 - public function override_image_downsize_in_rest_edit_context() {
1375 - return true;
1376 - }
1377 -
1378 - /**
1379 - * Return whether the current page is AMP.
1380 - *
1381 - * This is only present for the sake of WordPress.com where the Jetpack_AMP_Support
1382 - * class does not yet exist. This mehod may only be called at the wp action or later.
1383 - *
1384 - * @return bool Whether AMP page.
1385 - */
1386 - private static function is_amp_endpoint() {
1387 - return class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request();
1388 52 }
1389 53 }