PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.0-beta
16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / _inc / lib / class.media-summary.php
jetpack / _inc / lib Last commit date
admin-pages 2 weeks ago core-api 2 days ago debugger 1 month ago markdown 7 months ago class-jetpack-ai-helper.php 4 months ago class-jetpack-application-password-extras.php 3 months ago class-jetpack-blog-stats-helper.php 7 months ago class-jetpack-currencies.php 1 year ago class-jetpack-instagram-gallery-helper.php 1 month ago class-jetpack-mapbox-helper.php 3 years ago class-jetpack-newsletter-category-helper.php 7 months ago class-jetpack-podcast-feed-locator.php 1 month ago class-jetpack-podcast-helper.php 5 months ago class-jetpack-recommendations.php 1 month ago class-jetpack-spinner.php 2 months ago class-jetpack-top-posts-helper.php 2 weeks ago class.color.php 1 year ago class.core-rest-api-endpoints.php 2 weeks ago class.jetpack-automatic-install-skin.php 2 years ago class.jetpack-iframe-embed.php 3 years ago class.jetpack-password-checker.php 7 months ago class.jetpack-search-performance-logger.php 4 years ago class.media-extractor.php 1 month ago class.media-summary.php 2 weeks ago class.media.php 7 months ago components.php 7 months ago debugger.php 1 month ago icalendar-reader.php 1 month ago markdown.php 7 months ago plans.php 4 years ago plugins.php 4 years ago tonesque.php 7 months ago widgets.php 3 months ago
class.media-summary.php
477 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Provides media summary of a post.
4 *
5 * @package automattic/jetpack
6 */
7
8 use Automattic\Jetpack\Image_CDN\Image_CDN_Core;
9
10 /**
11 * Class Jetpack_Media_Summary
12 *
13 * Priority: embed [video] > gallery > image > text
14 */
15 class Jetpack_Media_Summary {
16
17 /**
18 * Media cache.
19 *
20 * @var array
21 */
22 private static $cache = array();
23
24 /**
25 * Get media summary for a post.
26 *
27 * @param ?int $post_id Post ID.
28 * @param int $blog_id Blog ID, if applicable.
29 * @param array $args {
30 * Optional. An array of arguments.
31 * @type int $max_words Maximum number of words.
32 * @type int $max_chars Maximum number of characters.
33 * @type bool $include_excerpt Whether to compute the excerpt and return it. Default true.
34 * @type bool $include_counts Whether to compute word/link counts. Default true.
35 * }
36 *
37 * @return array|mixed|void
38 */
39 public static function get( ?int $post_id, int $blog_id = 0, array $args = array() ) {
40 $post_id = (int) $post_id;
41
42 $defaults = array(
43 'max_words' => 16,
44 'max_chars' => 256,
45 'include_excerpt' => true,
46 'include_counts' => true,
47 );
48 $args = wp_parse_args( $args, $defaults );
49
50 $switched = false;
51 if ( ! empty( $blog_id ) && get_current_blog_id() !== $blog_id && function_exists( 'switch_to_blog' ) ) {
52 switch_to_blog( $blog_id );
53 $switched = true;
54 } else {
55 $blog_id = get_current_blog_id();
56 }
57
58 $cache_key = "{$blog_id}_{$post_id}_{$args['max_words']}_{$args['max_chars']}_"
59 . (int) $args['include_excerpt'] . '_' . (int) $args['include_counts'];
60 if ( isset( self::$cache[ $cache_key ] ) ) {
61 if ( $switched ) {
62 restore_current_blog();
63 }
64 return self::$cache[ $cache_key ];
65 }
66
67 if ( ! class_exists( 'Jetpack_Media_Meta_Extractor' ) ) {
68 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.media-extractor.php';
69 }
70
71 $post = get_post( $post_id );
72 $permalink = get_permalink( $post_id );
73
74 $return = array(
75 'type' => 'standard',
76 'permalink' => $permalink,
77 'image' => '',
78 'excerpt' => '',
79 'word_count' => 0,
80 'secure' => array(
81 'image' => '',
82 ),
83 'count' => array(
84 'image' => 0,
85 'video' => 0,
86 'word' => 0,
87 'word_remaining' => 0,
88 'link' => 0,
89 ),
90 );
91
92 if ( $post instanceof WP_Post && empty( $post->post_password ) ) {
93 if ( $args['include_excerpt'] ) {
94 $return['excerpt'] = self::get_excerpt( $post->post_content, $post->post_excerpt, $args['max_words'], $args['max_chars'], $post );
95 }
96 if ( $args['include_counts'] ) {
97 $return['count']['word'] = self::get_word_count( $post->post_content );
98 $return['count']['link'] = self::get_link_count( $post->post_content );
99 // Only compute word_remaining if we have an excerpt. If not, leave the default of 0.
100 if ( $args['include_excerpt'] && '' !== $return['excerpt'] ) {
101 $return['count']['word_remaining'] = self::get_word_remaining_count( $post->post_content, $return['excerpt'] );
102 }
103 }
104 }
105
106 $extract = Jetpack_Media_Meta_Extractor::extract( $blog_id, $post_id, Jetpack_Media_Meta_Extractor::ALL );
107
108 if ( empty( $extract['has'] ) ) {
109 if ( $switched ) {
110 restore_current_blog();
111 }
112 self::$cache[ $cache_key ] = $return;
113 return $return;
114 }
115
116 // Prioritize [some] video embeds.
117 if ( ! empty( $extract['has']['shortcode'] ) ) {
118 foreach ( $extract['shortcode'] as $type => $data ) {
119 switch ( $type ) {
120 case 'videopress':
121 case 'wpvideo':
122 if ( 0 === $return['count']['video'] ) {
123 // If there is no id on the video, then let's just skip this.
124 if ( ! isset( $data['id'][0] ) ) {
125 break;
126 }
127
128 $guid = $data['id'][0];
129 $video_info = videopress_get_video_details( $guid );
130
131 // Only add the video tags if the guid returns a valid videopress object.
132 if ( $video_info instanceof stdClass ) {
133 // Continue early if we can't find a Video slug.
134 if ( empty( $video_info->files->std->mp4 ) ) {
135 break;
136 }
137
138 $url = sprintf(
139 'https://videos.files.wordpress.com/%1$s/%2$s',
140 $guid,
141 $video_info->files->std->mp4
142 );
143
144 $thumbnail = $video_info->poster;
145 if ( ! empty( $thumbnail ) ) {
146 $return['image'] = $thumbnail;
147 $return['secure']['image'] = $thumbnail;
148 }
149
150 $return['type'] = 'video';
151 $return['video'] = esc_url_raw( $url );
152 $return['video_type'] = 'video/mp4';
153 $return['secure']['video'] = $return['video'];
154 }
155 }
156 ++$return['count']['video'];
157 break;
158 case 'youtube':
159 if ( 0 === $return['count']['video'] ) {
160 if ( ! isset( $extract['shortcode']['youtube']['id'][0] ) ) {
161 break;
162 }
163 $return['type'] = 'video';
164 $return['video'] = esc_url_raw( 'http://www.youtube.com/watch?feature=player_embedded&v=' . $extract['shortcode']['youtube']['id'][0] );
165 $return['image'] = self::get_video_poster( 'youtube', $extract['shortcode']['youtube']['id'][0] );
166 $return['secure']['video'] = self::https( $return['video'] );
167 $return['secure']['image'] = self::https( $return['image'] );
168 }
169 ++$return['count']['video'];
170 break;
171 case 'vimeo':
172 if ( 0 === $return['count']['video'] ) {
173 if ( ! isset( $extract['shortcode']['vimeo']['id'][0] ) ) {
174 break;
175 }
176 $return['type'] = 'video';
177 $return['video'] = esc_url_raw( 'http://vimeo.com/' . $extract['shortcode']['vimeo']['id'][0] );
178 $return['secure']['video'] = self::https( $return['video'] );
179
180 $poster_image = get_post_meta( $post_id, 'vimeo_poster_image', true );
181 if ( ! empty( $poster_image ) ) {
182 $return['image'] = $poster_image;
183 $poster_url_parts = wp_parse_url( $poster_image );
184 $return['secure']['image'] = 'https://secure-a.vimeocdn.com' . $poster_url_parts['path'];
185 }
186 }
187 ++$return['count']['video'];
188 break;
189 }
190 }
191 }
192
193 if ( ! empty( $extract['has']['embed'] ) ) {
194 foreach ( $extract['embed']['url'] as $embed ) {
195 if ( preg_match( '/((youtube|vimeo|dailymotion)\.com|youtu.be)/', $embed ) ) {
196 if ( 0 === $return['count']['video'] ) {
197 $return['type'] = 'video';
198 $return['video'] = 'http://' . $embed;
199 $return['secure']['video'] = self::https( $return['video'] );
200 if ( str_contains( $embed, 'youtube' ) ) {
201 $return['image'] = self::get_video_poster( 'youtube', jetpack_get_youtube_id( $return['video'] ) );
202 $return['secure']['image'] = self::https( $return['image'] );
203 } elseif ( str_contains( $embed, 'youtu.be' ) ) {
204 $youtube_id = jetpack_get_youtube_id( $return['video'] );
205 $return['video'] = 'http://youtube.com/watch?v=' . $youtube_id . '&feature=youtu.be';
206 $return['secure']['video'] = self::https( $return['video'] );
207 $return['image'] = self::get_video_poster( 'youtube', jetpack_get_youtube_id( $return['video'] ) );
208 $return['secure']['image'] = self::https( $return['image'] );
209 } elseif ( str_contains( $embed, 'vimeo' ) ) {
210 $poster_image = get_post_meta( $post_id, 'vimeo_poster_image', true );
211 if ( ! empty( $poster_image ) ) {
212 $return['image'] = $poster_image;
213 $poster_url_parts = wp_parse_url( $poster_image );
214 $return['secure']['image'] = 'https://secure-a.vimeocdn.com' . $poster_url_parts['path'];
215 }
216 } elseif ( str_contains( $embed, 'dailymotion' ) ) {
217 $return['image'] = str_replace( 'dailymotion.com/video/', 'dailymotion.com/thumbnail/video/', $embed );
218 $return['image'] = wp_parse_url( $return['image'], PHP_URL_SCHEME ) === null ? 'http://' . $return['image'] : $return['image'];
219 $return['secure']['image'] = self::https( $return['image'] );
220 }
221 }
222 ++$return['count']['video'];
223 }
224 }
225 }
226
227 // Do we really want to make the video the primary focus of the post?
228 if ( 'video' === $return['type'] ) {
229 $content = wpautop( wp_strip_all_tags( $post->post_content ) );
230 $paragraphs = explode( '</p>', $content );
231 $number_of_paragraphs = 0;
232
233 foreach ( $paragraphs as $i => $paragraph ) {
234 // Don't include blank lines as a paragraph.
235 if ( '' === trim( $paragraph ) ) {
236 unset( $paragraphs[ $i ] );
237 continue;
238 }
239 ++$number_of_paragraphs;
240 }
241
242 $number_of_paragraphs -= $return['count']['video']; // subtract amount for videos.
243
244 // More than 2 paragraph? The video is not the primary focus so we can do some more analysis.
245 if ( $number_of_paragraphs > 2 ) {
246 $return['type'] = 'standard';
247 }
248 }
249
250 // If we don't have any prioritized embed...
251 if ( 'standard' === $return['type'] ) {
252 if ( ( ! empty( $extract['has']['gallery'] ) || ! empty( $extract['shortcode']['gallery']['count'] ) ) && ! empty( $extract['image'] ) ) {
253 // ... Then we prioritize galleries first (multiple images returned)
254 $return['type'] = 'gallery';
255 $return['images'] = $extract['image'];
256 foreach ( $return['images'] as $image ) {
257 $return['secure']['images'][] = array( 'url' => self::ssl_img( $image['url'] ) );
258 ++$return['count']['image'];
259 }
260 } elseif ( ! empty( $extract['has']['image'] ) ) {
261 // ... Or we try and select a single image that would make sense.
262 $content = wpautop( wp_strip_all_tags( $post->post_content ) );
263 $paragraphs = explode( '</p>', $content );
264 $number_of_paragraphs = 0;
265
266 foreach ( $paragraphs as $i => $paragraph ) {
267 // Don't include 'actual' captions as a paragraph.
268 if ( str_contains( $paragraph, '[caption' ) ) {
269 unset( $paragraphs[ $i ] );
270 continue;
271 }
272 // Don't include blank lines as a paragraph.
273 if ( '' === trim( $paragraph ) ) {
274 unset( $paragraphs[ $i ] );
275 continue;
276 }
277 ++$number_of_paragraphs;
278 }
279
280 // @phan-suppress-next-line PhanTypeMismatchDimFetch -- Phan is understandably confused, as $extract has many forms, including this one.
281 if ( ! empty( $extract['image'][0]['url'] ) ) {
282 $return['image'] = $extract['image'][0]['url'];
283 $return['secure']['image'] = self::ssl_img( $return['image'] );
284 ++$return['count']['image'];
285 }
286
287 if ( $number_of_paragraphs <= 2 && is_countable( $extract['image'] ) && 1 === count( $extract['image'] ) ) {
288 // If we have lots of text or images, let's not treat it as an image post, but return its first image.
289 $return['type'] = 'image';
290 }
291 }
292 }
293
294 if ( $switched ) {
295 restore_current_blog();
296 }
297
298 /**
299 * Allow a theme or plugin to inspect and ultimately change the media summary.
300 *
301 * @since 4.4.0
302 *
303 * @param array $data The calculated media summary data.
304 * @param int $post_id The id of the post this data applies to.
305 */
306 $return = apply_filters( 'jetpack_media_summary_output', $return, $post_id );
307
308 self::$cache[ $cache_key ] = $return;
309
310 return $return;
311 }
312
313 /**
314 * Converts http to https://
315 *
316 * @param string $str URL.
317 *
318 * @return string URL.
319 */
320 public static function https( $str ) {
321 return str_replace( 'http://', 'https://', $str );
322 }
323
324 /**
325 * Returns a Photonized version of the URL.
326 *
327 * @param string $url URL.
328 *
329 * @return string URL.
330 */
331 public static function ssl_img( $url ) {
332 if ( str_contains( $url, 'files.wordpress.com' ) ) {
333 return self::https( $url );
334 } else {
335 return self::https( Image_CDN_Core::cdn_url( $url ) );
336 }
337 }
338
339 /**
340 * Get the video poster.
341 *
342 * @param string $type Video service.
343 * @param string $id Video ID for the service.
344 *
345 * @return string URL of image thumbnail for the video.
346 */
347 public static function get_video_poster( $type, $id ) {
348 if ( 'videopress' === $type ) {
349 if ( function_exists( 'video_get_highest_resolution_image_url' ) ) {
350 return video_get_highest_resolution_image_url( $id );
351 } elseif ( class_exists( 'VideoPress_Video' ) ) {
352 $video = new VideoPress_Video( $id );
353 return $video->poster_frame_uri;
354 }
355 } elseif ( 'youtube' === $type ) {
356 return 'http://img.youtube.com/vi/' . $id . '/0.jpg';
357 }
358 }
359
360 /**
361 * Clean text of shortcodes and tags.
362 *
363 * @param string $text Dirty text.
364 * @param bool $preserve_urls When true, keep http(s) URLs in the text instead of stripping them. Default false.
365 *
366 * @return string Clean text.
367 */
368 public static function clean_text( $text, $preserve_urls = false ) {
369 $text = strip_shortcodes( wp_strip_all_tags( $text ) );
370
371 if ( ! $preserve_urls ) {
372 $text = preg_replace( '@https?://[\S]+@', '', $text );
373 }
374
375 return trim( preg_replace( '/[\s]+/', ' ', $text ) );
376 }
377
378 /**
379 * Retrieve an excerpt for the post summary.
380 *
381 * This function works around a suspected problem with Core. If resolved, this function should be simplified.
382 *
383 * @link https://github.com/Automattic/jetpack/pull/8510
384 * @link https://core.trac.wordpress.org/ticket/42814
385 *
386 * @param string $post_content The post's content.
387 * @param string $post_excerpt The post's excerpt. Empty if none was explicitly set.
388 * @param int $max_words Maximum number of words for the excerpt. Used on wp.com. Default 16.
389 * @param int $max_chars Maximum characters in the excerpt. Used on wp.com. Default 256.
390 * @param WP_Post $requested_post The post object.
391 * @param bool $preserve_urls When true, keep http(s) URLs in the excerpt instead of stripping them. Default false.
392 * @return string Post excerpt.
393 **/
394 public static function get_excerpt( $post_content, $post_excerpt, $max_words = 16, $max_chars = 256, $requested_post = null, $preserve_urls = false ) {
395 global $post;
396 $original_post = $post; // Saving the global for later use.
397 if ( empty( $post_excerpt ) && function_exists( 'wpcom_enhanced_excerpt_extract_excerpt' ) ) {
398 return self::clean_text(
399 wpcom_enhanced_excerpt_extract_excerpt(
400 array(
401 'text' => $post_content,
402 'excerpt_only' => true,
403 'show_read_more' => false,
404 'max_words' => $max_words,
405 'max_chars' => $max_chars,
406 'read_more_threshold' => 25,
407 )
408 ),
409 $preserve_urls
410 );
411 } elseif ( $requested_post instanceof WP_Post ) {
412 // @todo Refactor to not need to override the global.
413 // phpcs:ignore: WordPress.WP.GlobalVariablesOverride.Prohibited
414 $post = $requested_post; // setup_postdata does not set the global.
415 setup_postdata( $post );
416 /** This filter is documented in core/src/wp-includes/post-template.php */
417 $post_excerpt = apply_filters( 'get_the_excerpt', $post_excerpt, $post );
418 // phpcs:ignore: WordPress.WP.GlobalVariablesOverride.Prohibited
419 $post = $original_post; // wp_reset_postdata uses the $post global.
420 wp_reset_postdata();
421 return self::clean_text( $post_excerpt, $preserve_urls );
422 }
423 return '';
424 }
425
426 /**
427 * Split a string into an array of words.
428 *
429 * @param string $text Post content or excerpt.
430 *
431 * @return array Array of words.
432 */
433 public static function split_content_in_words( $text ) {
434 $words = preg_split( '/[\s!?;,.]+/', $text, -1, PREG_SPLIT_NO_EMPTY );
435
436 // Return an empty array if the split above fails.
437 return $words ? $words : array();
438 }
439
440 /**
441 * Get the word count.
442 *
443 * @param string $post_content Post content.
444 *
445 * @return int Word count.
446 */
447 public static function get_word_count( $post_content ) {
448 return count( self::split_content_in_words( self::clean_text( $post_content ) ) );
449 }
450
451 /**
452 * Get remainder word count (after the excerpt).
453 *
454 * @param string $post_content Post content.
455 * @param string $excerpt_content Excerpt content.
456 *
457 * @return int Number of words after the excerpt.
458 */
459 public static function get_word_remaining_count( $post_content, $excerpt_content ) {
460 $content_word_count = count( self::split_content_in_words( self::clean_text( $post_content ) ) );
461 $excerpt_word_count = count( self::split_content_in_words( self::clean_text( $excerpt_content ) ) );
462
463 return $content_word_count - $excerpt_word_count;
464 }
465
466 /**
467 * Counts the number of links in a post.
468 *
469 * @param string $post_content Post content.
470 *
471 * @return false|int Number of links.
472 */
473 public static function get_link_count( $post_content ) {
474 return preg_match_all( '/\<a[\> ]/', $post_content, $matches );
475 }
476 }
477