PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 15.1.2
Jetpack – WP Security, Backup, Speed, & Growth v15.1.2
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 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 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 1 year ago core-api 10 months ago debugger 11 months ago markdown 11 months ago class-jetpack-ai-helper.php 1 year ago class-jetpack-blog-stats-helper.php 1 year ago class-jetpack-currencies.php 2 years ago class-jetpack-instagram-gallery-helper.php 2 years ago class-jetpack-mapbox-helper.php 3 years ago class-jetpack-newsletter-category-helper.php 1 year ago class-jetpack-podcast-feed-locator.php 1 year ago class-jetpack-podcast-helper.php 1 year ago class-jetpack-recommendations.php 2 years ago class-jetpack-top-posts-helper.php 1 year ago class.color.php 2 years ago class.core-rest-api-endpoints.php 1 year ago class.jetpack-automatic-install-skin.php 2 years ago class.jetpack-iframe-embed.php 3 years ago class.jetpack-password-checker.php 11 months ago class.jetpack-search-performance-logger.php 4 years ago class.media-extractor.php 1 year ago class.media-summary.php 11 months ago class.media.php 1 year ago components.php 1 year ago debugger.php 1 year ago icalendar-reader.php 1 year ago markdown.php 1 year ago plans.php 4 years ago plugins.php 4 years ago tonesque.php 11 months ago widgets.php 1 year ago
class.media-summary.php
469 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 * }
34 *
35 * @return array|mixed|void
36 */
37 public static function get( ?int $post_id, int $blog_id = 0, array $args = array() ) {
38 $post_id = (int) $post_id;
39 $blog_id = (int) $blog_id;
40
41 $defaults = array(
42 'max_words' => 16,
43 'max_chars' => 256,
44 );
45 $args = wp_parse_args( $args, $defaults );
46
47 $switched = false;
48 if ( ! empty( $blog_id ) && get_current_blog_id() !== $blog_id && function_exists( 'switch_to_blog' ) ) {
49 switch_to_blog( $blog_id );
50 $switched = true;
51 } else {
52 $blog_id = get_current_blog_id();
53 }
54
55 $cache_key = "{$blog_id}_{$post_id}_{$args['max_words']}_{$args['max_chars']}";
56 if ( isset( self::$cache[ $cache_key ] ) ) {
57 if ( $switched ) {
58 restore_current_blog();
59 }
60 return self::$cache[ $cache_key ];
61 }
62
63 if ( ! class_exists( 'Jetpack_Media_Meta_Extractor' ) ) {
64 require_once JETPACK__PLUGIN_DIR . '_inc/lib/class.media-extractor.php';
65 }
66
67 $post = get_post( $post_id );
68 $permalink = get_permalink( $post_id );
69
70 $return = array(
71 'type' => 'standard',
72 'permalink' => $permalink,
73 'image' => '',
74 'excerpt' => '',
75 'word_count' => 0,
76 'secure' => array(
77 'image' => '',
78 ),
79 'count' => array(
80 'image' => 0,
81 'video' => 0,
82 'word' => 0,
83 'link' => 0,
84 ),
85 );
86
87 if ( $post instanceof WP_Post && empty( $post->post_password ) ) {
88 $return['excerpt'] = self::get_excerpt( $post->post_content, $post->post_excerpt, $args['max_words'], $args['max_chars'], $post );
89 $return['count']['word'] = self::get_word_count( $post->post_content );
90 $return['count']['word_remaining'] = self::get_word_remaining_count( $post->post_content, $return['excerpt'] );
91 $return['count']['link'] = self::get_link_count( $post->post_content );
92 }
93
94 $extract = Jetpack_Media_Meta_Extractor::extract( $blog_id, $post_id, Jetpack_Media_Meta_Extractor::ALL );
95
96 if ( empty( $extract['has'] ) ) {
97 if ( $switched ) {
98 restore_current_blog();
99 }
100 self::$cache[ $cache_key ] = $return;
101 return $return;
102 }
103
104 // Prioritize [some] video embeds.
105 if ( ! empty( $extract['has']['shortcode'] ) ) {
106 foreach ( $extract['shortcode'] as $type => $data ) {
107 switch ( $type ) {
108 case 'videopress':
109 case 'wpvideo':
110 if ( 0 === $return['count']['video'] ) {
111 // If there is no id on the video, then let's just skip this.
112 if ( ! isset( $data['id'][0] ) ) {
113 break;
114 }
115
116 $guid = $data['id'][0];
117 $video_info = videopress_get_video_details( $guid );
118
119 // Only add the video tags if the guid returns a valid videopress object.
120 if ( $video_info instanceof stdClass ) {
121 // Continue early if we can't find a Video slug.
122 if ( empty( $video_info->files->std->mp4 ) ) {
123 break;
124 }
125
126 $url = sprintf(
127 'https://videos.files.wordpress.com/%1$s/%2$s',
128 $guid,
129 $video_info->files->std->mp4
130 );
131
132 $thumbnail = $video_info->poster;
133 if ( ! empty( $thumbnail ) ) {
134 $return['image'] = $thumbnail;
135 $return['secure']['image'] = $thumbnail;
136 }
137
138 $return['type'] = 'video';
139 $return['video'] = esc_url_raw( $url );
140 $return['video_type'] = 'video/mp4';
141 $return['secure']['video'] = $return['video'];
142 }
143 }
144 ++$return['count']['video'];
145 break;
146 case 'youtube':
147 if ( 0 === $return['count']['video'] ) {
148 if ( ! isset( $extract['shortcode']['youtube']['id'][0] ) ) {
149 break;
150 }
151 $return['type'] = 'video';
152 $return['video'] = esc_url_raw( 'http://www.youtube.com/watch?feature=player_embedded&v=' . $extract['shortcode']['youtube']['id'][0] );
153 $return['image'] = self::get_video_poster( 'youtube', $extract['shortcode']['youtube']['id'][0] );
154 $return['secure']['video'] = self::https( $return['video'] );
155 $return['secure']['image'] = self::https( $return['image'] );
156 }
157 ++$return['count']['video'];
158 break;
159 case 'vimeo':
160 if ( 0 === $return['count']['video'] ) {
161 if ( ! isset( $extract['shortcode']['vimeo']['id'][0] ) ) {
162 break;
163 }
164 $return['type'] = 'video';
165 $return['video'] = esc_url_raw( 'http://vimeo.com/' . $extract['shortcode']['vimeo']['id'][0] );
166 $return['secure']['video'] = self::https( $return['video'] );
167
168 $poster_image = get_post_meta( $post_id, 'vimeo_poster_image', true );
169 if ( ! empty( $poster_image ) ) {
170 $return['image'] = $poster_image;
171 $poster_url_parts = wp_parse_url( $poster_image );
172 $return['secure']['image'] = 'https://secure-a.vimeocdn.com' . $poster_url_parts['path'];
173 }
174 }
175 ++$return['count']['video'];
176 break;
177 }
178 }
179 }
180
181 if ( ! empty( $extract['has']['embed'] ) ) {
182 foreach ( $extract['embed']['url'] as $embed ) {
183 if ( preg_match( '/((youtube|vimeo|dailymotion)\.com|youtu.be)/', $embed ) ) {
184 if ( 0 === $return['count']['video'] ) {
185 $return['type'] = 'video';
186 $return['video'] = 'http://' . $embed;
187 $return['secure']['video'] = self::https( $return['video'] );
188 if ( str_contains( $embed, 'youtube' ) ) {
189 $return['image'] = self::get_video_poster( 'youtube', jetpack_get_youtube_id( $return['video'] ) );
190 $return['secure']['image'] = self::https( $return['image'] );
191 } elseif ( str_contains( $embed, 'youtu.be' ) ) {
192 $youtube_id = jetpack_get_youtube_id( $return['video'] );
193 $return['video'] = 'http://youtube.com/watch?v=' . $youtube_id . '&feature=youtu.be';
194 $return['secure']['video'] = self::https( $return['video'] );
195 $return['image'] = self::get_video_poster( 'youtube', jetpack_get_youtube_id( $return['video'] ) );
196 $return['secure']['image'] = self::https( $return['image'] );
197 } elseif ( str_contains( $embed, 'vimeo' ) ) {
198 $poster_image = get_post_meta( $post_id, 'vimeo_poster_image', true );
199 if ( ! empty( $poster_image ) ) {
200 $return['image'] = $poster_image;
201 $poster_url_parts = wp_parse_url( $poster_image );
202 $return['secure']['image'] = 'https://secure-a.vimeocdn.com' . $poster_url_parts['path'];
203 }
204 } elseif ( str_contains( $embed, 'dailymotion' ) ) {
205 $return['image'] = str_replace( 'dailymotion.com/video/', 'dailymotion.com/thumbnail/video/', $embed );
206 $return['image'] = wp_parse_url( $return['image'], PHP_URL_SCHEME ) === null ? 'http://' . $return['image'] : $return['image'];
207 $return['secure']['image'] = self::https( $return['image'] );
208 }
209 }
210 ++$return['count']['video'];
211 }
212 }
213 }
214
215 // Do we really want to make the video the primary focus of the post?
216 if ( 'video' === $return['type'] ) {
217 $content = wpautop( wp_strip_all_tags( $post->post_content ) );
218 $paragraphs = explode( '</p>', $content );
219 $number_of_paragraphs = 0;
220
221 foreach ( $paragraphs as $i => $paragraph ) {
222 // Don't include blank lines as a paragraph.
223 if ( '' === trim( $paragraph ) ) {
224 unset( $paragraphs[ $i ] );
225 continue;
226 }
227 ++$number_of_paragraphs;
228 }
229
230 $number_of_paragraphs = $number_of_paragraphs - $return['count']['video']; // subtract amount for videos.
231
232 // More than 2 paragraph? The video is not the primary focus so we can do some more analysis.
233 if ( $number_of_paragraphs > 2 ) {
234 $return['type'] = 'standard';
235 }
236 }
237
238 // If we don't have any prioritized embed...
239 if ( 'standard' === $return['type'] ) {
240 if ( ( ! empty( $extract['has']['gallery'] ) || ! empty( $extract['shortcode']['gallery']['count'] ) ) && ! empty( $extract['image'] ) ) {
241 // ... Then we prioritize galleries first (multiple images returned)
242 $return['type'] = 'gallery';
243 $return['images'] = $extract['image'];
244 foreach ( $return['images'] as $image ) {
245 $return['secure']['images'][] = array( 'url' => self::ssl_img( $image['url'] ) );
246 ++$return['count']['image'];
247 }
248 } elseif ( ! empty( $extract['has']['image'] ) ) {
249 // ... Or we try and select a single image that would make sense.
250 $content = wpautop( wp_strip_all_tags( $post->post_content ) );
251 $paragraphs = explode( '</p>', $content );
252 $number_of_paragraphs = 0;
253
254 foreach ( $paragraphs as $i => $paragraph ) {
255 // Don't include 'actual' captions as a paragraph.
256 if ( str_contains( $paragraph, '[caption' ) ) {
257 unset( $paragraphs[ $i ] );
258 continue;
259 }
260 // Don't include blank lines as a paragraph.
261 if ( '' === trim( $paragraph ) ) {
262 unset( $paragraphs[ $i ] );
263 continue;
264 }
265 ++$number_of_paragraphs;
266 }
267
268 if ( isset( $extract['image'][0]['url'] ) ) {
269 $return['image'] = $extract['image'][0]['url'];
270 $return['secure']['image'] = self::ssl_img( $return['image'] );
271 ++$return['count']['image'];
272 }
273
274 if ( $number_of_paragraphs <= 2 && is_countable( $extract['image'] ) && 1 === count( $extract['image'] ) ) {
275 // If we have lots of text or images, let's not treat it as an image post, but return its first image.
276 $return['type'] = 'image';
277 }
278 }
279 }
280
281 if ( $switched ) {
282 restore_current_blog();
283 }
284
285 /**
286 * Allow a theme or plugin to inspect and ultimately change the media summary.
287 *
288 * @since 4.4.0
289 *
290 * @param array $data The calculated media summary data.
291 * @param int $post_id The id of the post this data applies to.
292 */
293 $return = apply_filters( 'jetpack_media_summary_output', $return, $post_id );
294
295 self::$cache[ $cache_key ] = $return;
296
297 return $return;
298 }
299
300 /**
301 * Converts http to https://
302 *
303 * @param string $str URL.
304 *
305 * @return string URL.
306 */
307 public static function https( $str ) {
308 return str_replace( 'http://', 'https://', $str );
309 }
310
311 /**
312 * Returns a Photonized version of the URL.
313 *
314 * @param string $url URL.
315 *
316 * @return string URL.
317 */
318 public static function ssl_img( $url ) {
319 if ( str_contains( $url, 'files.wordpress.com' ) ) {
320 return self::https( $url );
321 } else {
322 return self::https( Image_CDN_Core::cdn_url( $url ) );
323 }
324 }
325
326 /**
327 * Get the video poster.
328 *
329 * @param string $type Video service.
330 * @param string $id Video ID for the service.
331 *
332 * @return string URL of image thumbnail for the video.
333 */
334 public static function get_video_poster( $type, $id ) {
335 if ( 'videopress' === $type ) {
336 if ( function_exists( 'video_get_highest_resolution_image_url' ) ) {
337 return video_get_highest_resolution_image_url( $id );
338 } elseif ( class_exists( 'VideoPress_Video' ) ) {
339 $video = new VideoPress_Video( $id );
340 return $video->poster_frame_uri;
341 }
342 } elseif ( 'youtube' === $type ) {
343 return 'http://img.youtube.com/vi/' . $id . '/0.jpg';
344 }
345 }
346
347 /**
348 * Clean text of shortcodes and tags.
349 *
350 * @param string $text Dirty text.
351 *
352 * @return string Clean text.
353 */
354 public static function clean_text( $text ) {
355 return trim(
356 preg_replace(
357 '/[\s]+/',
358 ' ',
359 preg_replace(
360 '@https?://[\S]+@',
361 '',
362 strip_shortcodes(
363 wp_strip_all_tags(
364 $text
365 )
366 )
367 )
368 )
369 );
370 }
371
372 /**
373 * Retrieve an excerpt for the post summary.
374 *
375 * This function works around a suspected problem with Core. If resolved, this function should be simplified.
376 *
377 * @link https://github.com/Automattic/jetpack/pull/8510
378 * @link https://core.trac.wordpress.org/ticket/42814
379 *
380 * @param string $post_content The post's content.
381 * @param string $post_excerpt The post's excerpt. Empty if none was explicitly set.
382 * @param int $max_words Maximum number of words for the excerpt. Used on wp.com. Default 16.
383 * @param int $max_chars Maximum characters in the excerpt. Used on wp.com. Default 256.
384 * @param WP_Post $requested_post The post object.
385 * @return string Post excerpt.
386 **/
387 public static function get_excerpt( $post_content, $post_excerpt, $max_words = 16, $max_chars = 256, $requested_post = null ) {
388 global $post;
389 $original_post = $post; // Saving the global for later use.
390 if ( empty( $post_excerpt ) && function_exists( 'wpcom_enhanced_excerpt_extract_excerpt' ) ) {
391 return self::clean_text(
392 wpcom_enhanced_excerpt_extract_excerpt(
393 array(
394 'text' => $post_content,
395 'excerpt_only' => true,
396 'show_read_more' => false,
397 'max_words' => $max_words,
398 'max_chars' => $max_chars,
399 'read_more_threshold' => 25,
400 )
401 )
402 );
403 } elseif ( $requested_post instanceof WP_Post ) {
404 // @todo Refactor to not need to override the global.
405 // phpcs:ignore: WordPress.WP.GlobalVariablesOverride.Prohibited
406 $post = $requested_post; // setup_postdata does not set the global.
407 setup_postdata( $post );
408 /** This filter is documented in core/src/wp-includes/post-template.php */
409 $post_excerpt = apply_filters( 'get_the_excerpt', $post_excerpt, $post );
410 // phpcs:ignore: WordPress.WP.GlobalVariablesOverride.Prohibited
411 $post = $original_post; // wp_reset_postdata uses the $post global.
412 wp_reset_postdata();
413 return self::clean_text( $post_excerpt );
414 }
415 return '';
416 }
417
418 /**
419 * Split a string into an array of words.
420 *
421 * @param string $text Post content or excerpt.
422 *
423 * @return array Array of words.
424 */
425 public static function split_content_in_words( $text ) {
426 $words = preg_split( '/[\s!?;,.]+/', $text, -1, PREG_SPLIT_NO_EMPTY );
427
428 // Return an empty array if the split above fails.
429 return $words ? $words : array();
430 }
431
432 /**
433 * Get the word count.
434 *
435 * @param string $post_content Post content.
436 *
437 * @return int Word count.
438 */
439 public static function get_word_count( $post_content ) {
440 return (int) count( self::split_content_in_words( self::clean_text( $post_content ) ) );
441 }
442
443 /**
444 * Get remainder word count (after the excerpt).
445 *
446 * @param string $post_content Post content.
447 * @param string $excerpt_content Excerpt content.
448 *
449 * @return int Number of words after the excerpt.
450 */
451 public static function get_word_remaining_count( $post_content, $excerpt_content ) {
452 $content_word_count = count( self::split_content_in_words( self::clean_text( $post_content ) ) );
453 $excerpt_word_count = count( self::split_content_in_words( self::clean_text( $excerpt_content ) ) );
454
455 return (int) $content_word_count - $excerpt_word_count;
456 }
457
458 /**
459 * Counts the number of links in a post.
460 *
461 * @param string $post_content Post content.
462 *
463 * @return false|int Number of links.
464 */
465 public static function get_link_count( $post_content ) {
466 return preg_match_all( '/\<a[\> ]/', $post_content, $matches );
467 }
468 }
469