Completed
Push — renovate/history-4.x ( 8706da...6c1ea7 )
by
unknown
17:57 queued 11:18
created

_inc/lib/class.media-summary.php (1 issue)

Severity

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
 * Class Jetpack_Media_Summary
4
 *
5
 * embed [video] > gallery > image > text
6
 */
7
class Jetpack_Media_Summary {
8
9
	private static $cache = array();
10
11
	static function get( $post_id, $blog_id = 0, $args = array() ) {
12
13
		$defaults = array(
14
			'max_words' => 16,
15
			'max_chars' => 256,
16
		);
17
		$args = wp_parse_args( $args, $defaults );
0 ignored issues
show
$defaults is of type array<string,integer,{"m..."max_chars":"integer"}>, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
18
19
		$switched = false;
20
		if ( !empty( $blog_id ) && $blog_id != get_current_blog_id() && function_exists( 'switch_to_blog' ) ) {
21
			switch_to_blog( $blog_id );
22
			$switched = true;
23
		} else {
24
			$blog_id = get_current_blog_id();
25
		}
26
27
		$cache_key = "{$blog_id}_{$post_id}_{$args['max_words']}_{$args['max_chars']}";
28
		if ( isset( self::$cache[ $cache_key ] ) ) {
29
			return self::$cache[ $cache_key ];
30
		}
31
32
		if ( ! class_exists( 'Jetpack_Media_Meta_Extractor' ) ) {
33
			if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
34
				jetpack_require_lib( 'class.wpcom-media-meta-extractor' );
35
			} else {
36
				jetpack_require_lib( 'class.media-extractor' );
37
			}
38
		}
39
40
		$post      = get_post( $post_id );
41
		$permalink = get_permalink( $post_id );
42
43
		$return = array(
44
			'type'       => 'standard',
45
			'permalink'  => $permalink,
46
			'image'      => '',
47
			'excerpt'    => '',
48
			'word_count' => 0,
49
			'secure'     => array(
50
				'image'  => '',
51
			),
52
			'count'       => array(
53
				'image' => 0,
54
				'video' => 0,
55
				'word'  => 0,
56
				'link'  => 0,
57
			),
58
		);
59
60
		if ( empty( $post->post_password ) ) {
61
			$return['excerpt']       = self::get_excerpt( $post->post_content, $post->post_excerpt, $args['max_words'], $args['max_chars'] , $post);
62
			$return['count']['word'] = self::get_word_count( $post->post_content );
63
			$return['count']['word_remaining'] = self::get_word_remaining_count( $post->post_content, $return['excerpt'] );
64
			$return['count']['link'] = self::get_link_count( $post->post_content );
65
		}
66
67
		$extract = Jetpack_Media_Meta_Extractor::extract( $blog_id, $post_id, Jetpack_Media_Meta_Extractor::ALL );
68
69
		if ( empty( $extract['has'] ) )
70
			return $return;
71
72
		// Prioritize [some] video embeds
73
		if ( !empty( $extract['has']['shortcode'] ) ) {
74
			foreach ( $extract['shortcode'] as $type => $data ) {
75
				switch ( $type ) {
76
					case 'videopress':
77
					case 'wpvideo':
78
						if ( 0 == $return['count']['video'] ) {
79
							// If there is no id on the video, then let's just skip this
80
							if ( ! isset ( $data['id'][0] ) ) {
81
								break;
82
							}
83
84
							$guid = $data['id'][0];
85
							$video_info = videopress_get_video_details( $guid );
86
87
							// Only add the video tags if the guid returns a valid videopress object.
88
							if ( $video_info instanceof stdClass ) {
89
								// Continue early if we can't find a Video slug.
90
								if ( empty( $video_info->files->std->mp4 ) ) {
91
									break;
92
								}
93
94
								$url = sprintf(
95
									'https://videos.files.wordpress.com/%1$s/%2$s',
96
									$guid,
97
									$video_info->files->std->mp4
98
								);
99
100
								$thumbnail = $video_info->poster;
101
								if ( ! empty( $thumbnail ) ) {
102
									$return['image'] = $thumbnail;
103
									$return['secure']['image'] = $thumbnail;
104
								}
105
106
								$return['type'] = 'video';
107
								$return['video'] = esc_url_raw( $url );
108
								$return['video_type'] = 'video/mp4';
109
								$return['secure']['video'] = $return['video'];
110
							}
111
112
						}
113
						$return['count']['video']++;
114
						break;
115
					case 'youtube':
116
						if ( 0 == $return['count']['video'] ) {
117
							$return['type'] = 'video';
118
							$return['video'] = esc_url_raw( 'http://www.youtube.com/watch?feature=player_embedded&v=' . $extract['shortcode']['youtube']['id'][0] );
119
							$return['image'] = self::get_video_poster( 'youtube', $extract['shortcode']['youtube']['id'][0] );
120
							$return['secure']['video'] = self::https( $return['video'] );
121
							$return['secure']['image'] = self::https( $return['image'] );
122
						}
123
						$return['count']['video']++;
124
						break;
125
					case 'vimeo':
126
						if ( 0 == $return['count']['video'] ) {
127
							$return['type'] = 'video';
128
							$return['video'] = esc_url_raw( 'http://vimeo.com/' . $extract['shortcode']['vimeo']['id'][0] );
129
							$return['secure']['video'] = self::https( $return['video'] );
130
131
							$poster_image = get_post_meta( $post_id, 'vimeo_poster_image', true );
132 View Code Duplication
							if ( !empty( $poster_image ) ) {
133
								$return['image'] = $poster_image;
134
								$poster_url_parts = wp_parse_url( $poster_image );
135
								$return['secure']['image'] = 'https://secure-a.vimeocdn.com' . $poster_url_parts['path'];
136
							}
137
						}
138
						$return['count']['video']++;
139
						break;
140
				}
141
			}
142
143
		}
144
145
		if ( !empty( $extract['has']['embed'] ) ) {
146
			foreach( $extract['embed']['url'] as $embed ) {
147
				if ( preg_match( '/((youtube|vimeo|dailymotion)\.com|youtu.be)/', $embed ) ) {
148
					if ( 0 == $return['count']['video'] ) {
149
						$return['type']   = 'video';
150
						$return['video']  = 'http://' .  $embed;
151
						$return['secure']['video'] = self::https( $return['video'] );
152
						if ( false !== strpos( $embed, 'youtube' ) ) {
153
							$return['image'] = self::get_video_poster( 'youtube', jetpack_get_youtube_id( $return['video'] ) );
154
							$return['secure']['image'] = self::https( $return['image'] );
155
						} else if ( false !== strpos( $embed, 'youtu.be' ) ) {
156
							$youtube_id = jetpack_get_youtube_id( $return['video'] );
157
							$return['video'] = 'http://youtube.com/watch?v=' . $youtube_id . '&feature=youtu.be';
158
							$return['secure']['video'] = self::https( $return['video'] );
159
							$return['image'] = self::get_video_poster( 'youtube', jetpack_get_youtube_id( $return['video'] ) );
160
							$return['secure']['image'] = self::https( $return['image'] );
161
						} else if ( false !== strpos( $embed, 'vimeo' ) ) {
162
							$poster_image = get_post_meta( $post_id, 'vimeo_poster_image', true );
163 View Code Duplication
							if ( !empty( $poster_image ) ) {
164
								$return['image'] = $poster_image;
165
								$poster_url_parts = wp_parse_url( $poster_image );
166
								$return['secure']['image'] = 'https://secure-a.vimeocdn.com' . $poster_url_parts['path'];
167
							}
168
						} else if ( false !== strpos( $embed, 'dailymotion' ) ) {
169
							$return['image'] = str_replace( 'dailymotion.com/video/','dailymotion.com/thumbnail/video/', $embed );
170
							$return['image'] = wp_parse_url( $return['image'], PHP_URL_SCHEME ) === null ? 'http://' . $return['image'] : $return['image'];
171
							$return['secure']['image'] = self::https( $return['image'] );
172
						}
173
174
					}
175
					$return['count']['video']++;
176
				}
177
			}
178
		}
179
180
		// Do we really want to make the video the primary focus of the post?
181
		if ( 'video' == $return['type'] ) {
182
			$content = wpautop( strip_tags( $post->post_content ) );
183
			$paragraphs = explode( '</p>', $content );
184
			$number_of_paragraphs = 0;
185
186
			foreach ( $paragraphs as $i => $paragraph ) {
187
				// Don't include blank lines as a paragraph
188
				if ( '' == trim( $paragraph ) ) {
189
					unset( $paragraphs[$i] );
190
					continue;
191
				}
192
				$number_of_paragraphs++;
193
			}
194
195
			$number_of_paragraphs = $number_of_paragraphs - $return['count']['video']; // subtract amount for videos..
196
197
			// More than 2 paragraph? The video is not the primary focus so we can do some more analysis
198
			if ( $number_of_paragraphs > 2 )
199
				$return['type'] = 'standard';
200
		}
201
202
		// If we don't have any prioritized embed...
203
		if ( 'standard' == $return['type'] ) {
204
			if ( ( ! empty( $extract['has']['gallery'] ) || ! empty( $extract['shortcode']['gallery']['count'] ) ) && ! empty( $extract['image'] ) ) {
205
				//... Then we prioritize galleries first (multiple images returned)
206
				$return['type']   = 'gallery';
207
				$return['images'] = $extract['image'];
208
				foreach ( $return['images'] as $image ) {
209
					$return['secure']['images'][] = array( 'url' => self::ssl_img( $image['url'] ) );
210
					$return['count']['image']++;
211
				}
212
			} else if ( ! empty( $extract['has']['image'] ) ) {
213
				// ... Or we try and select a single image that would make sense
214
				$content = wpautop( strip_tags( $post->post_content ) );
215
				$paragraphs = explode( '</p>', $content );
216
				$number_of_paragraphs = 0;
217
218
				foreach ( $paragraphs as $i => $paragraph ) {
219
					// Don't include 'actual' captions as a paragraph
220
					if ( false !== strpos( $paragraph, '[caption' ) ) {
221
						unset( $paragraphs[$i] );
222
						continue;
223
					}
224
					// Don't include blank lines as a paragraph
225
					if ( '' == trim( $paragraph ) ) {
226
						unset( $paragraphs[$i] );
227
						continue;
228
					}
229
					$number_of_paragraphs++;
230
				}
231
232
				$return['image'] = $extract['image'][0]['url'];
233
				$return['secure']['image'] = self::ssl_img( $return['image'] );
234
				$return['count']['image']++;
235
236
				if ( $number_of_paragraphs <= 2 && 1 == count( $extract['image'] ) ) {
237
					// If we have lots of text or images, let's not treat it as an image post, but return its first image
238
					$return['type']  = 'image';
239
				}
240
			}
241
		}
242
243
		if ( $switched ) {
244
			restore_current_blog();
245
		}
246
247
		/**
248
		 * Allow a theme or plugin to inspect and ultimately change the media summary.
249
		 *
250
		 * @since 4.4.0
251
		 *
252
		 * @param array $data The calculated media summary data.
253
		 * @param int $post_id The id of the post this data applies to.
254
		 */
255
		$return = apply_filters( 'jetpack_media_summary_output', $return, $post_id );
256
257
		self::$cache[ $cache_key ] = $return;
258
259
		return $return;
260
	}
261
262
	static function https( $str ) {
263
		return str_replace( 'http://', 'https://', $str );
264
	}
265
266
	static function ssl_img( $url ) {
267
		if ( false !== strpos( $url, 'files.wordpress.com' ) ) {
268
			return self::https( $url );
269
		} else {
270
			return self::https( jetpack_photon_url( $url ) );
271
		}
272
	}
273
274
	static function get_video_poster( $type, $id ) {
275
		if ( 'videopress' == $type ) {
276
			if ( function_exists( 'video_get_highest_resolution_image_url' ) ) {
277
				return video_get_highest_resolution_image_url( $id );
278
			} else if ( class_exists( 'VideoPress_Video' ) ) {
279
				$video = new VideoPress_Video( $id );
280
				return $video->poster_frame_uri;
281
			}
282
		} else if ( 'youtube' == $type ) {
283
			return  'http://img.youtube.com/vi/'.$id.'/0.jpg';
284
		}
285
	}
286
287
	static function clean_text( $text ) {
288
		return trim(
289
			preg_replace(
290
				'/[\s]+/',
291
				' ',
292
				preg_replace(
293
					'@https?://[\S]+@',
294
					'',
295
					strip_shortcodes(
296
						strip_tags(
297
							$text
298
						)
299
					)
300
				)
301
			)
302
		);
303
	}
304
305
	/**
306
	 * Retrieve an excerpt for the post summary.
307
	 *
308
	 * This function works around a suspected problem with Core. If resolved, this function should be simplified.
309
	 * @link https://github.com/Automattic/jetpack/pull/8510
310
	 * @link https://core.trac.wordpress.org/ticket/42814
311
	 *
312
	 * @param  string  $post_content The post's content.
313
	 * @param  string  $post_excerpt The post's excerpt. Empty if none was explicitly set.
314
	 * @param  int     $max_words Maximum number of words for the excerpt. Used on wp.com. Default 16.
315
	 * @param  int     $max_chars Maximum characters in the excerpt. Used on wp.com. Default 256.
316
	 * @param  WP_Post $requested_post The post object.
317
	 * @return string Post excerpt.
318
	 **/
319
	static function get_excerpt( $post_content, $post_excerpt, $max_words = 16, $max_chars = 256, $requested_post = null ) {
320
		global $post;
321
		$original_post = $post; // Saving the global for later use.
322
		if ( function_exists( 'wpcom_enhanced_excerpt_extract_excerpt' ) ) {
323
			return self::clean_text( wpcom_enhanced_excerpt_extract_excerpt( array(
324
				'text'                => $post_content,
325
				'excerpt_only'        => true,
326
				'show_read_more'      => false,
327
				'max_words'           => $max_words,
328
				'max_chars'           => $max_chars,
329
				'read_more_threshold' => 25,
330
			) ) );
331
		} elseif ( $requested_post instanceof WP_Post ) {
332
			$post = $requested_post; // setup_postdata does not set the global.
333
			setup_postdata( $post );
334
			/** This filter is documented in core/src/wp-includes/post-template.php */
335
			$post_excerpt = apply_filters( 'get_the_excerpt', $post_excerpt, $post );
336
			$post         = $original_post; // wp_reset_postdata uses the $post global.
337
			wp_reset_postdata();
338
			return self::clean_text( $post_excerpt );
339
		}
340
		return '';
341
	}
342
343
	/**
344
	 * Split a string into an array of words.
345
	 *
346
	 * @param string $text Post content or excerpt.
347
	 */
348
	static function split_content_in_words( $text ) {
349
		$words = preg_split( '/[\s!?;,.]+/', $text, null, PREG_SPLIT_NO_EMPTY );
350
351
		// Return an empty array if the split above fails.
352
		return $words ? $words : array();
353
	}
354
355
	static function get_word_count( $post_content ) {
356
		return (int) count( self::split_content_in_words( self::clean_text( $post_content ) ) );
357
	}
358
359
	static function get_word_remaining_count( $post_content, $excerpt_content ) {
360
		$content_word_count = count( self::split_content_in_words( self::clean_text( $post_content ) ) );
361
		$excerpt_word_count = count( self::split_content_in_words( self::clean_text( $excerpt_content ) ) );
362
363
		return (int) $content_word_count - $excerpt_word_count;
364
	}
365
366
	static function get_link_count( $post_content ) {
367
		return preg_match_all( '/\<a[\> ]/', $post_content, $matches );
368
	}
369
}
370