Completed
Push — fix/post-images-detection-colu... ( 80844f...af615a )
by Jeremy
07:09
created

Jetpack_PostImages::is_nested_block()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Useful for finding an image to display alongside/in representation of a specific post.
5
 *
6
 * Includes a few different methods, all of which return a similar-format array containing
7
 * details of any images found. Everything can (should) be called statically, it's just a
8
 * function-bucket. You can also call Jetpack_PostImages::get_image() to cycle through all of the methods until
9
 * one of them finds something useful.
10
 *
11
 * This file is included verbatim in Jetpack
12
 */
13
class Jetpack_PostImages {
14
	/**
15
	 * If a slideshow is embedded within a post, then parse out the images involved and return them
16
	 */
17
	static function from_slideshow( $post_id, $width = 200, $height = 200 ) {
18
		$images = array();
19
20
		$post = get_post( $post_id );
21
22
		if ( ! $post ) {
23
			return $images;
24
		}
25
26
		if ( ! empty( $post->post_password ) ) {
27
			return $images;
28
		}
29
30
		if ( false === has_shortcode( $post->post_content, 'slideshow' ) ) {
31
			return $images; // no slideshow - bail
32
		}
33
34
		$permalink = get_permalink( $post->ID );
35
36
		// Mechanic: Somebody set us up the bomb
37
		$old_post = $GLOBALS['post'];
38
		$GLOBALS['post'] = $post;
39
		$old_shortcodes = $GLOBALS['shortcode_tags'];
40
		$GLOBALS['shortcode_tags'] = array( 'slideshow' => $old_shortcodes['slideshow'] );
41
42
		// Find all the slideshows
43
		preg_match_all( '/' . get_shortcode_regex() . '/sx', $post->post_content, $slideshow_matches, PREG_SET_ORDER );
44
45
		ob_start(); // The slideshow shortcode handler calls wp_print_scripts and wp_print_styles... not too happy about that
46
47
		foreach ( $slideshow_matches as $slideshow_match ) {
0 ignored issues
show
Bug introduced by
The expression $slideshow_matches of type null|array<integer,array<integer,string>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

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

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

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

Loading history...
48
			$slideshow = do_shortcode_tag( $slideshow_match );
49
			if ( false === $pos = stripos( $slideshow, 'jetpack-slideshow' ) ) // must be something wrong - or we changed the output format in which case none of the following will work
50
				continue;
51
			$start = strpos( $slideshow, '[', $pos );
52
			$end = strpos( $slideshow, ']', $start );
53
			$post_images = json_decode( wp_specialchars_decode( str_replace( "'", '"', substr( $slideshow, $start, $end - $start + 1 ) ), ENT_QUOTES ) ); // parse via JSON
54
			// If the JSON didn't decode don't try and act on it.
55
			if ( is_array( $post_images ) ) {
56
				foreach ( $post_images as $post_image ) {
57
					if ( !$post_image_id = absint( $post_image->id ) )
58
						continue;
59
60
					$meta = wp_get_attachment_metadata( $post_image_id );
61
62
					// Must be larger than 200x200 (or user-specified)
63
					if ( !isset( $meta['width'] ) || $meta['width'] < $width )
64
						continue;
65
					if ( !isset( $meta['height'] ) || $meta['height'] < $height )
66
						continue;
67
68
					$url = wp_get_attachment_url( $post_image_id );
69
70
					$images[] = array(
71
						'type'       => 'image',
72
						'from'       => 'slideshow',
73
						'src'        => $url,
74
						'src_width'  => $meta['width'],
75
						'src_height' => $meta['height'],
76
						'href'       => $permalink,
77
					);
78
				}
79
			}
80
		}
81
		ob_end_clean();
82
83
		// Operator: Main screen turn on
84
		$GLOBALS['shortcode_tags'] = $old_shortcodes;
85
		$GLOBALS['post'] = $old_post;
86
87
		return $images;
88
	}
89
90
	/**
91
	 * If a gallery is detected, then get all the images from it.
92
	 */
93
	static function from_gallery( $post_id ) {
94
		$images = array();
95
96
		$post = get_post( $post_id );
97
98
		if ( ! $post ) {
99
			return $images;
100
		}
101
102
		if ( ! empty( $post->post_password ) ) {
103
			return $images;
104
		}
105
106
		$permalink = get_permalink( $post->ID );
107
108
		/**
109
		 *  Juggle global post object because the gallery shortcode uses the
110
		 *  global object.
111
		 *
112
		 *  See core ticket:
113
		 *  https://core.trac.wordpress.org/ticket/39304
114
		 */
115
		if ( isset( $GLOBALS['post'] ) ) {
116
			$juggle_post = $GLOBALS['post'];
117
			$GLOBALS['post'] = $post;
118
			$galleries = get_post_galleries( $post->ID, false );
119
			$GLOBALS['post'] = $juggle_post;
120
		} else {
121
			$GLOBALS['post'] = $post;
122
			$galleries = get_post_galleries( $post->ID, false );
123
			unset( $GLOBALS['post'] );
124
		}
125
126
		foreach ( $galleries as $gallery ) {
127
			if ( isset( $gallery['type'] ) && 'slideshow' === $gallery['type'] && ! empty( $gallery['ids'] ) ) {
128
				$image_ids = explode( ',', $gallery['ids'] );
129
				$image_size = isset( $gallery['size'] ) ? $gallery['size'] : 'thumbnail';
130
				foreach ( $image_ids as $image_id ) {
131
					$image = wp_get_attachment_image_src( $image_id, $image_size );
132 View Code Duplication
					if ( ! empty( $image[0] ) ) {
133
						list( $raw_src ) = explode( '?', $image[0] ); // pull off any Query string (?w=250)
134
						$raw_src = wp_specialchars_decode( $raw_src ); // rawify it
135
						$raw_src = esc_url_raw( $raw_src ); // clean it
136
						$images[] = array(
137
							'type'  => 'image',
138
							'from'  => 'gallery',
139
							'src'   => $raw_src,
140
							'href'  => $permalink,
141
						);
142
					}
143
				}
144 View Code Duplication
			} elseif ( ! empty( $gallery['src'] ) ) {
145
				foreach ( $gallery['src'] as $src ) {
146
					list( $raw_src ) = explode( '?', $src ); // pull off any Query string (?w=250)
147
					$raw_src = wp_specialchars_decode( $raw_src ); // rawify it
148
					$raw_src = esc_url_raw( $raw_src ); // clean it
149
					$images[] = array(
150
						'type'  => 'image',
151
						'from'  => 'gallery',
152
						'src'   => $raw_src,
153
						'href'  => $permalink,
154
					);
155
				}
156
			}
157
		}
158
159
		return $images;
160
	}
161
162
	/**
163
	 * Get attachment images for a specified post and return them. Also make sure
164
	 * their dimensions are at or above a required minimum.
165
	 */
166
	static function from_attachment( $post_id, $width = 200, $height = 200 ) {
167
		$images = array();
168
169
		$post = get_post( $post_id );
170
171
		if ( ! empty( $post->post_password ) ) {
172
			return $images;
173
		}
174
175
		$post_images = get_posts( array(
176
			'post_parent' => $post_id,   // Must be children of post
177
			'numberposts' => 5,          // No more than 5
178
			'post_type' => 'attachment', // Must be attachments
179
			'post_mime_type' => 'image', // Must be images
180
			'suppress_filters' => false,
181
		) );
182
183
		if ( ! $post_images ) {
184
			return $images;
185
		}
186
187
		$permalink = get_permalink( $post_id );
188
189
		foreach ( $post_images as $post_image ) {
190
			$current_image = self::get_attachment_data( $post_image->ID, $permalink, $width, $height );
191
			if ( false !== $current_image ) {
192
				$images[] = $current_image;
193
			}
194
		}
195
196
		/*
197
		* We only want to pass back attached images that were actually inserted.
198
		* We can load up all the images found in the HTML source and then
199
		* compare URLs to see if an image is attached AND inserted.
200
		*/
201
		$html_images = self::from_html( $post_id );
202
		$inserted_images = array();
203
204
		foreach( $html_images as $html_image ) {
205
			$src = parse_url( $html_image['src'] );
206
			// strip off any query strings from src
207
			if( ! empty( $src['scheme'] ) && ! empty( $src['host'] ) ) {
208
				$inserted_images[] = $src['scheme'] . '://' . $src['host'] . $src['path'];
209
			} elseif( ! empty( $src['host'] ) ) {
210
				$inserted_images[] = set_url_scheme( 'http://' . $src['host'] . $src['path'] );
211
			} else {
212
				$inserted_images[] = site_url( '/' ) . $src['path'];
213
			}
214
		}
215
		foreach( $images as $i => $image ) {
216
			if ( !in_array( $image['src'], $inserted_images ) )
217
				unset( $images[$i] );
218
		}
219
220
		return $images;
221
	}
222
223
	/**
224
	 * Check if a Featured Image is set for this post, and return it in a similar
225
	 * format to the other images?_from_*() methods.
226
	 * @param  int $post_id The post ID to check
227
	 * @return Array containing details of the Featured Image, or empty array if none.
228
	 */
229
	static function from_thumbnail( $post_id, $width = 200, $height = 200 ) {
230
		$images = array();
231
232
		$post = get_post( $post_id );
233
234
		if ( ! empty( $post->post_password ) ) {
235
			return $images;
236
		}
237
238
		if ( 'attachment' === get_post_type( $post ) && wp_attachment_is_image( $post ) ) {
239
			$thumb = $post_id;
240
		} else {
241
			$thumb = get_post_thumbnail_id( $post );
242
		}
243
244
		if ( $thumb ) {
245
			$meta = wp_get_attachment_metadata( $thumb );
246
			// Must be larger than requested minimums
247
			if ( !isset( $meta['width'] ) || $meta['width'] < $width )
248
				return $images;
249
			if ( !isset( $meta['height'] ) || $meta['height'] < $height )
250
				return $images;
251
252
			$too_big = ( ( ! empty( $meta['width'] ) && $meta['width'] > 1200 ) || ( ! empty( $meta['height'] ) && $meta['height'] > 1200 ) );
253
254
			if (
255
				$too_big &&
256
				(
257
					( method_exists( 'Jetpack', 'is_module_active' ) && Jetpack::is_module_active( 'photon' ) ) ||
258
					( defined( 'IS_WPCOM' ) && IS_WPCOM )
259
				)
260
			) {
261
				$img_src = wp_get_attachment_image_src( $thumb, array( 1200, 1200 ) );
262
			} else {
263
				$img_src = wp_get_attachment_image_src( $thumb, 'full' );
264
			}
265
			if ( ! is_array( $img_src ) ) {
266
				// If wp_get_attachment_image_src returns false but we know that there should be an image that could be used.
267
				// we try a bit harder and user the data that we have.
268
				$thumb_post_data = get_post( $thumb );
269
				$img_src = array( $thumb_post_data->guid, $meta['width'], $meta['height'] );
270
			}
271
272
			$url    = $img_src[0];
273
			$images = array( array( // Other methods below all return an array of arrays
274
				'type'       => 'image',
275
				'from'       => 'thumbnail',
276
				'src'        => $url,
277
				'src_width'  => $img_src[1],
278
				'src_height' => $img_src[2],
279
				'href'       => get_permalink( $thumb ),
280
				'alt_text'   => self::get_alt_text( $thumb ),
281
			) );
282
283
		}
284
285
		if ( empty( $images ) && ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ) {
286
			$meta_thumbnail = get_post_meta( $post_id, '_jetpack_post_thumbnail', true );
287
			if ( ! empty( $meta_thumbnail ) ) {
288
				if ( ! isset( $meta_thumbnail['width'] ) || $meta_thumbnail['width'] < $width ) {
289
					return $images;
290
				}
291
292
				if ( ! isset( $meta_thumbnail['height'] ) || $meta_thumbnail['height'] < $height ) {
293
					return $images;
294
				}
295
296
				$images = array( array( // Other methods below all return an array of arrays
297
					'type'       => 'image',
298
					'from'       => 'thumbnail',
299
					'src'        => $meta_thumbnail['URL'],
300
					'src_width'  => $meta_thumbnail['width'],
301
					'src_height' => $meta_thumbnail['height'],
302
					'href'       => $meta_thumbnail['URL'],
303
					'alt_text'   => self::get_alt_text( $thumb ),
304
				) );
305
			}
306
		}
307
308
		return $images;
309
	}
310
311
	/**
312
	 * Get images from Gutenberg Image blocks.
313
	 *
314
	 * @since 6.9.0
315
	 *
316
	 * @param mixed $html_or_id The HTML string to parse for images, or a post id.
317
	 * @param int   $width      Minimum Image width.
318
	 * @param int   $height     Minimum Image height.
319
	 */
320
	public static function from_blocks( $html_or_id, $width = 200, $height = 200 ) {
321
		$images = array();
322
323
		$html_info = self::get_post_html( $html_or_id );
324
325
		if ( empty( $html_info['html'] ) ) {
326
			return $images;
327
		}
328
329
		// Look for block information in the HTML.
330
		$blocks = parse_blocks( $html_info['html'] );
331
		if ( empty( $blocks ) ) {
332
			return $images;
333
		}
334
335
		/*
336
		 * Let's loop through our blocks.
337
		 * Some blocks may include some other blocks. Let's go 2 levels deep to look for blocks
338
		 * that we support and that may include images (see get_images_from_block)
339
		 *
340
		 * @to-do: instead of looping manually (that's a lot of if and loops), search recursively instead.
341
		 */
342
		foreach ( $blocks as $block ) {
343
			if ( ! self::is_nested_block( $block ) ) {
344
				$images = self::get_images_from_block( $images, $block, $html_info, $width, $height );
0 ignored issues
show
Bug introduced by
It seems like $html_info defined by self::get_post_html($html_or_id) on line 323 can also be of type string; however, Jetpack_PostImages::get_images_from_block() does only seem to accept array, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
345
			} else {
346
				foreach ( $block['innerBlocks'] as $inner_block ) {
347
					if ( ! self::is_nested_block( $inner_block ) ) {
348
						$images = self::get_images_from_block( $images, $inner_block, $html_info, $width, $height );
0 ignored issues
show
Bug introduced by
It seems like $html_info defined by self::get_post_html($html_or_id) on line 323 can also be of type string; however, Jetpack_PostImages::get_images_from_block() does only seem to accept array, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
349
					} else {
350
						foreach ( $inner_block['innerBlocks'] as $inner_inner_block ) {
351
							$images = self::get_images_from_block( $images, $inner_inner_block, $html_info, $width, $height );
0 ignored issues
show
Bug introduced by
It seems like $html_info defined by self::get_post_html($html_or_id) on line 323 can also be of type string; however, Jetpack_PostImages::get_images_from_block() does only seem to accept array, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
352
						}
353
					}
354
				}
355
			}
356
		}
357
358
		/**
359
		 * Returning a filtered array because get_attachment_data returns false
360
		 * for unsuccessful attempts.
361
		 */
362
		return array_filter( $images );
363
	}
364
365
	/**
366
	 * Very raw -- just parse the HTML and pull out any/all img tags and return their src
367
	 *
368
	 * @param mixed $html_or_id The HTML string to parse for images, or a post id.
369
	 * @param int   $width      Minimum Image width.
370
	 * @param int   $height     Minimum Image height.
371
	 *
372
	 * @uses DOMDocument
373
	 *
374
	 * @return Array containing images
375
	 */
376
	static function from_html( $html_or_id, $width = 200, $height = 200 ) {
377
		$images = array();
378
379
		$html_info = self::get_post_html( $html_or_id );
380
381
		if ( empty( $html_info['html'] ) ) {
382
			return $images;
383
		}
384
385
		// Do not go any further if DOMDocument is disabled on the server.
386
		if ( ! class_exists( 'DOMDocument' ) ) {
387
			return $images;
388
		}
389
390
		// Let's grab all image tags from the HTML.
391
		$dom_doc = new DOMDocument;
392
393
		// The @ is not enough to suppress errors when dealing with libxml,
394
		// we have to tell it directly how we want to handle errors.
395
		libxml_use_internal_errors( true );
396
		@$dom_doc->loadHTML( $html_info['html'] );
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...
397
		libxml_use_internal_errors( false );
398
399
		$image_tags = $dom_doc->getElementsByTagName( 'img' );
400
401
		// For each image Tag, make sure it can be added to the $images array, and add it.
402
		foreach ( $image_tags as $image_tag ) {
403
			$img_src = $image_tag->getAttribute( 'src' );
404
405
			if ( empty( $img_src ) ) {
406
				continue;
407
			}
408
409
			// Do not grab smiley images that were automatically created by WP when entering text smilies.
410
			if ( stripos( $img_src, '/smilies/' ) ) {
411
				continue;
412
			}
413
414
			$meta = array(
415
				'width'    => (int) $image_tag->getAttribute( 'width' ),
416
				'height'   => (int) $image_tag->getAttribute( 'height' ),
417
				'alt_text' => $image_tag->getAttribute( 'alt' ),
418
			);
419
420
			/**
421
			 * Filters the switch to ignore minimum image size requirements. Can be used
422
			 * to add custom logic to image dimensions, like only enforcing one of the dimensions,
423
			 * or disabling it entirely.
424
			 *
425
			 * @since 6.4.0
426
			 *
427
			 * @param bool $ignore Should the image dimensions be ignored?
428
			 * @param array $meta Array containing image dimensions parsed from the markup.
429
			 */
430
			$ignore_dimensions = apply_filters( 'jetpack_postimages_ignore_minimum_dimensions', false, $meta );
431
432
			// Must be larger than 200x200 (or user-specified).
433
			if (
434
				! $ignore_dimensions
435
				&& (
436
					empty( $meta['width'] )
437
					|| empty( $meta['height'] )
438
					|| $meta['width'] < $width
439
					|| $meta['height'] < $height
440
				)
441
			) {
442
				continue;
443
			}
444
445
			$images[] = array(
446
				'type'       => 'image',
447
				'from'       => 'html',
448
				'src'        => $img_src,
449
				'src_width'  => $meta['width'],
450
				'src_height' => $meta['height'],
451
				'href'       => $html_info['post_url'],
452
				'alt_text'   => $meta['alt_text'],
453
			);
454
		}
455
		return $images;
456
	}
457
458
	/**
459
	 * @param    int $post_id The post ID to check
460
	 * @param    int $size
461
	 * @return Array containing details of the image, or empty array if none.
462
	 */
463
	static function from_blavatar( $post_id, $size = 96 ) {
464
465
		$permalink = get_permalink( $post_id );
466
467
		if ( function_exists( 'blavatar_domain' ) && function_exists( 'blavatar_exists' ) && function_exists( 'blavatar_url' ) ) {
468
			$domain = blavatar_domain( $permalink );
469
470
			if ( ! blavatar_exists( $domain ) ) {
471
				return array();
472
			}
473
474
			$url = blavatar_url( $domain, 'img', $size );
475
		} else {
476
			$url = get_site_icon_url( $size );
477
			if ( ! $url ) {
478
				return array();
479
			}
480
		}
481
482
		return array( array(
483
			'type'       => 'image',
484
			'from'       => 'blavatar',
485
			'src'        => $url,
486
			'src_width'  => $size,
487
			'src_height' => $size,
488
			'href'       => $permalink,
489
			'alt_text'   => '',
490
		) );
491
	}
492
493
	/**
494
	 * Gets a post image from the author avatar.
495
	 *
496
	 * @param int    $post_id The post ID to check.
497
	 * @param int    $size The size of the avatar to get.
498
	 * @param string $default The default image to use.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $default not be false|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.

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

Loading history...
499
	 * @return Array containing details of the image, or empty array if none.
500
	 */
501
	static function from_gravatar( $post_id, $size = 96, $default = false ) {
502
		$post = get_post( $post_id );
503
		$permalink = get_permalink( $post_id );
504
505
		if ( function_exists( 'wpcom_get_avatar_url' ) ) {
506
			$url = wpcom_get_avatar_url( $post->post_author, $size, $default, true );
507
			if ( $url && is_array( $url ) ) {
508
				$url = $url[0];
509
			}
510
		} else {
511
			$url = get_avatar_url( $post->post_author, array(
512
				'size' => $size,
513
				'default' => $default,
514
			) );
515
		}
516
517
		return array(
518
			array(
519
				'type'       => 'image',
520
				'from'       => 'gravatar',
521
				'src'        => $url,
522
				'src_width'  => $size,
523
				'src_height' => $size,
524
				'href'       => $permalink,
525
				'alt_text'   => '',
526
			),
527
		);
528
	}
529
530
	/**
531
	 * Run through the different methods that we have available to try to find a single good
532
	 * display image for this post.
533
	 * @param  int $post_id
534
	 * @param array $args Other arguments (currently width and height required for images where possible to determine)
535
	 * @return Array containing details of the best image to be used
536
	 */
537
	static function get_image( $post_id, $args = array() ) {
538
		$image = '';
539
540
		/**
541
		 * Fires before we find a single good image for a specific post.
542
		 *
543
		 * @since 2.2.0
544
		 *
545
		 * @param int $post_id Post ID.
546
		 */
547
		do_action( 'jetpack_postimages_pre_get_image', $post_id );
548
		$media = self::get_images( $post_id, $args );
549
550
551
		if ( is_array( $media ) ) {
552
			foreach ( $media as $item ) {
553
				if ( 'image' == $item['type'] ) {
554
					$image = $item;
555
					break;
556
				}
557
			}
558
		}
559
560
		/**
561
		 * Fires after we find a single good image for a specific post.
562
		 *
563
		 * @since 2.2.0
564
		 *
565
		 * @param int $post_id Post ID.
566
		 */
567
		do_action( 'jetpack_postimages_post_get_image', $post_id );
568
569
		return $image;
570
	}
571
572
	/**
573
	 * Get an array containing a collection of possible images for this post, stopping once we hit a method
574
	 * that returns something useful.
575
	 * @param  int $post_id
576
	 * @param  array  $args Optional args, see defaults list for details
577
	 * @return Array containing images that would be good for representing this post
578
	 */
579
	static function get_images( $post_id, $args = array() ) {
580
		// Figure out which image to attach to this post.
581
		$media = false;
582
583
		/**
584
		 * Filters the array of images that would be good for a specific post.
585
		 * This filter is applied before options ($args) filter the original array.
586
		 *
587
		 * @since 2.0.0
588
		 *
589
		 * @param array $media Array of images that would be good for a specific post.
590
		 * @param int $post_id Post ID.
591
		 * @param array $args Array of options to get images.
592
		 */
593
		$media = apply_filters( 'jetpack_images_pre_get_images', $media, $post_id, $args );
594
		if ( $media )
595
			return $media;
596
597
		$defaults = array(
598
			'width'               => 200, // Required minimum width (if possible to determine)
599
			'height'              => 200, // Required minimum height (if possible to determine)
600
601
			'fallback_to_avatars' => false, // Optionally include Blavatar and Gravatar (in that order) in the image stack
602
			'avatar_size'         => 96, // Used for both Grav and Blav
603
			'gravatar_default'    => false, // Default image to use if we end up with no Gravatar
604
605
			'from_thumbnail'      => true, // Use these flags to specify which methods to use to find an image
606
			'from_slideshow'      => true,
607
			'from_gallery'        => true,
608
			'from_attachment'     => true,
609
			'from_blocks'         => true,
610
			'from_html'           => true,
611
612
			'html_content'        => '' // HTML string to pass to from_html()
613
		);
614
		$args = wp_parse_args( $args, $defaults );
615
616
		$media = false;
617
		if ( $args['from_thumbnail'] )
618
			$media = self::from_thumbnail( $post_id, $args['width'], $args['height'] );
619 View Code Duplication
		if ( !$media && $args['from_slideshow'] )
620
			$media = self::from_slideshow( $post_id, $args['width'], $args['height'] );
621
		if ( !$media && $args['from_gallery'] )
622
			$media = self::from_gallery( $post_id );
623 View Code Duplication
		if ( !$media && $args['from_attachment'] )
624
			$media = self::from_attachment( $post_id, $args['width'], $args['height'] );
625 View Code Duplication
		if ( ! $media && $args['from_blocks'] ) {
626
			if ( empty( $args['html_content'] ) )
627
				$media = self::from_blocks( $post_id, $args['width'], $args['height'] ); // Use the post_id, which will load the content
628
			else
629
				$media = self::from_blocks( $args['html_content'], $args['width'], $args['height'] ); // If html_content is provided, use that
630
		}
631 View Code Duplication
		if ( !$media && $args['from_html'] ) {
632
			if ( empty( $args['html_content'] ) )
633
				$media = self::from_html( $post_id, $args['width'], $args['height'] ); // Use the post_id, which will load the content
634
			else
635
				$media = self::from_html( $args['html_content'], $args['width'], $args['height'] ); // If html_content is provided, use that
636
		}
637
638
		if ( !$media && $args['fallback_to_avatars'] ) {
639
			$media = self::from_blavatar( $post_id, $args['avatar_size'] );
640
			if ( !$media )
0 ignored issues
show
Bug Best Practice introduced by
The expression $media 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...
641
				$media = self::from_gravatar( $post_id, $args['avatar_size'], $args['gravatar_default'] );
642
		}
643
644
		/**
645
		 * Filters the array of images that would be good for a specific post.
646
		 * This filter is applied after options ($args) filter the original array.
647
		 *
648
		 * @since 2.0.0
649
		 *
650
		 * @param array $media Array of images that would be good for a specific post.
651
		 * @param int $post_id Post ID.
652
		 * @param array $args Array of options to get images.
653
		 */
654
		return apply_filters( 'jetpack_images_get_images', $media, $post_id, $args );
655
	}
656
657
	/**
658
	 * Takes an image URL and pixel dimensions then returns a URL for the
659
	 * resized and cropped image.
660
	 *
661
	 * @param  string $src
662
	 * @param  int    $dimension
0 ignored issues
show
Bug introduced by
There is no parameter named $dimension. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
663
	 * @return string            Transformed image URL
664
	 */
665
	static function fit_image_url( $src, $width, $height ) {
666
		$width = (int) $width;
667
		$height = (int) $height;
668
669
		if ( $width < 1 || $height < 1 ) {
670
			return $src;
671
		}
672
673
		// See if we should bypass WordPress.com SaaS resizing
674
		if ( has_filter( 'jetpack_images_fit_image_url_override' ) ) {
675
			/**
676
			 * Filters the image URL used after dimensions are set by Photon.
677
			 *
678
			 * @since 3.3.0
679
			 *
680
			 * @param string $src Image URL.
681
			 * @param int $width Image width.
682
			 * @param int $width Image height.
683
			 */
684
			return apply_filters( 'jetpack_images_fit_image_url_override', $src, $width, $height );
685
		}
686
687
		// If WPCOM hosted image use native transformations
688
		$img_host = parse_url( $src, PHP_URL_HOST );
689
		if ( '.files.wordpress.com' == substr( $img_host, -20 ) ) {
690
			return add_query_arg( array( 'w' => $width, 'h' => $height, 'crop' => 1 ), set_url_scheme( $src ) );
691
		}
692
693
		// Use Photon magic
694
		if( function_exists( 'jetpack_photon_url' ) ) {
695
			return jetpack_photon_url( $src, array( 'resize' => "$width,$height" ) );
696
		}
697
698
		// Arg... no way to resize image using WordPress.com infrastructure!
699
		return $src;
700
	}
701
702
	/**
703
	 * Get HTML from given post content.
704
	 *
705
	 * @since 6.9.0
706
	 *
707
	 * @param mixed $html_or_id The HTML string to parse for images, or a post id.
708
	 *
709
	 * @return array $html_info {
710
	 * @type string $html     Post content.
711
	 * @type string $post_url Post URL.
712
	 * }
713
	 */
714
	static function get_post_html( $html_or_id ) {
715
		if ( is_numeric( $html_or_id ) ) {
716
			$post = get_post( $html_or_id );
717
718
			if ( empty( $post ) || ! empty( $post->post_password ) ) {
719
				return '';
720
			}
721
722
			$html_info = array(
723
				'html'     => $post->post_content, // DO NOT apply the_content filters here, it will cause loops.
724
				'post_url' => get_permalink( $post->ID ),
725
			);
726
		} else {
727
			$html_info = array(
728
				'html'     => $html_or_id,
729
				'post_url' => '',
730
			);
731
		}
732
		return $html_info;
733
	}
734
735
	/**
736
	 * Get info about a WordPress attachment.
737
	 *
738
	 * @since 6.9.0
739
	 *
740
	 * @param int    $attachment_id Attachment ID.
741
	 * @param string $post_url      URL of the post, if we have one.
742
	 * @param int    $width         Minimum Image width.
743
	 * @param int    $height        Minimum Image height.
744
	 * @return array|bool           Image data or false if unavailable.
745
	 */
746
	public static function get_attachment_data( $attachment_id, $post_url = '', $width, $height ) {
747
		if ( empty( $attachment_id ) ) {
748
			return false;
749
		}
750
751
		$meta = wp_get_attachment_metadata( $attachment_id );
752
753
		// The image must be larger than 200x200.
754
		if ( ! isset( $meta['width'] ) || $meta['width'] < $width ) {
755
			return false;
756
		}
757
		if ( ! isset( $meta['height'] ) || $meta['height'] < $height ) {
758
			return false;
759
		}
760
761
		$url = wp_get_attachment_url( $attachment_id );
762
763
		return array(
764
			'type'       => 'image',
765
			'from'       => 'attachment',
766
			'src'        => $url,
767
			'src_width'  => $meta['width'],
768
			'src_height' => $meta['height'],
769
			'href'       => $post_url,
770
			'alt_text'   => self::get_alt_text( $attachment_id ),
771
		);
772
	}
773
774
	/**
775
	 * Get the alt text for an image or other media from the Media Library.
776
	 *
777
	 * @since 7.1
778
	 *
779
	 * @param int $attachment_id The Post ID of the media.
780
	 * @return string The alt text value or an emptry string.
781
	 */
782
	public static function get_alt_text( $attachment_id ) {
783
		return get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
784
	}
785
786
	/**
787
	 * Get an image from a block.
788
	 *
789
	 * @since 7.8.0
790
	 *
791
	 * @param array $images    Images found.
792
	 * @param array $block     Block and its attributes.
793
	 * @param array $html_info Info about the post where the block is found.
794
	 * @param int   $width     Desired image width.
795
	 * @param int   $height    Desired image height.
796
	 */
797
	private static function get_images_from_block( $images, $block, $html_info, $width, $height ) {
798
		/**
799
		 * Parse content from Core Image blocks.
800
		 * If it is an image block for an image hosted on our site, it will have an ID.
801
		 * If it does not have an ID, let `from_html` parse that content later,
802
		 * and extract an image if it has size parameters.
803
		 */
804
		if (
805
			'core/image' === $block['blockName']
806
			&& ! empty( $block['attrs']['id'] )
807
		) {
808
			$images[] = self::get_attachment_data( $block['attrs']['id'], $html_info['post_url'], $width, $height );
809
		}
810
811
		/**
812
		 * Parse content from Core Gallery blocks as well from Jetpack's Tiled Gallery and Slideshow blocks.
813
		 * Gallery blocks include the ID of each one of the images in the gallery.
814
		 */
815
		if (
816
			in_array( $block['blockName'], array( 'core/gallery', 'jetpack/tiled-gallery', 'jetpack/slideshow' ), true )
817
			&& ! empty( $block['attrs']['ids'] )
818
		) {
819
			foreach ( $block['attrs']['ids'] as $img_id ) {
820
				$images[] = self::get_attachment_data( $img_id, $html_info['post_url'], $width, $height );
821
			}
822
		}
823
824
		return $images;
825
	}
826
827
	/**
828
	 * Check if a block has inner blocks.
829
	 *
830
	 * @since 7.8.0
831
	 *
832
	 * @param array $block Block and its attributes.
833
	 *
834
	 * @return bool
835
	 */
836
	private static function is_nested_block( $block ) {
837
		if ( ! empty( $block['innerBlocks'] ) ) {
838
			return true;
839
		}
840
841
		return false;
842
	}
843
}
844