Completed
Push — fix/9129-allow-analytics-js-wi... ( 41fabb...20f3a6 )
by
unknown
27:44 queued 11:52
created

_inc/lib/class.media-summary.php (2 issues)

Upgrade to new PHP Analysis Engine

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

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

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...
313
	 * @return string Post excerpt.
314
	 **/
315
	static function get_excerpt( $post_content, $post_excerpt, $max_words = 16, $max_chars = 256, $requested_post = null ) {
316
		global $post;
317
		$original_post = $post; // Saving the global for later use.
318
		if ( function_exists( 'wpcom_enhanced_excerpt_extract_excerpt' ) ) {
319
			return self::clean_text( wpcom_enhanced_excerpt_extract_excerpt( array(
320
				'text'                => $post_content,
321
				'excerpt_only'        => true,
322
				'show_read_more'      => false,
323
				'max_words'           => $max_words,
324
				'max_chars'           => $max_chars,
325
				'read_more_threshold' => 25,
326
			) ) );
327
		} elseif ( $requested_post instanceof WP_Post ) {
0 ignored issues
show
The class WP_Post does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
328
			$post = $requested_post; // setup_postdata does not set the global.
329
			setup_postdata( $post );
330
			/** This filter is documented in core/src/wp-includes/post-template.php */
331
			$post_excerpt = apply_filters( 'get_the_excerpt', $post_excerpt, $post );
332
			$post         = $original_post; // wp_reset_postdata uses the $post global.
333
			wp_reset_postdata();
334
			return self::clean_text( $post_excerpt );
335
		}
336
		return '';
337
	}
338
339
	static function get_word_count( $post_content ) {
340
		return str_word_count( self::clean_text( $post_content ) );
341
	}
342
343
	static function get_word_remaining_count( $post_content, $excerpt_content ) {
344
		return str_word_count( self::clean_text( $post_content ) ) - str_word_count( self::clean_text( $excerpt_content ) );
345
	}
346
347
	static function get_link_count( $post_content ) {
348
		return preg_match_all( '/\<a[\> ]/', $post_content, $matches );
349
	}
350
}
351