PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.5
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / src / images / Application / image-content-extractor.php

image-content-extractor.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.5, at src/images/Application/image-content-extractor.php

178 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Yoast\WP\SEO\Images\Application;
4
5 use DOMDocument;
6 use WP_HTML_Tag_Processor;
7
8 /**
9 * The image content extractor.
10 */
11 class Image_Content_Extractor {
12
13 /**
14 * Gathers all images from content.
15 *
16 * @param string $content The content.
17 *
18 * @return int[] An associated array of image IDs, keyed by their URLs.
19 */
20 public function gather_images( $content ) {
21
22 /**
23 * Filter 'wpseo_force_creating_and_using_attachment_indexables' - Filters if we should use attachment indexables to find all content images. Instead of scanning the content.
24 *
25 * The default value is false.
26 *
27 * @since 21.1
28 */
29 $should_not_parse_content = \apply_filters( 'wpseo_force_creating_and_using_attachment_indexables', false );
30 /**
31 * Filter 'wpseo_force_skip_image_content_parsing' - Filters if we should force skip scanning the content to parse images.
32 * This filter can be used if the regex gives a faster result than scanning the code.
33 *
34 * The default value is false.
35 *
36 * @since 21.1
37 */
38 $should_not_parse_content = \apply_filters( 'wpseo_force_skip_image_content_parsing', $should_not_parse_content );
39
40 if ( ! $should_not_parse_content && \class_exists( WP_HTML_Tag_Processor::class ) ) {
41 return $this->gather_images_wp( $content );
42 }
43
44 if ( ! $should_not_parse_content && \class_exists( DOMDocument::class ) ) {
45
46 return $this->gather_images_DOMDocument( $content );
47 }
48
49 if ( \strpos( $content, 'src' ) === false ) {
50 // Nothing to do.
51 return [];
52 }
53
54 $images = [];
55 $regexp = '<img\s[^>]*src=("??)([^" >]*?)\\1[^>]*>';
56 // Used modifiers iU to match case insensitive and make greedy quantifiers lazy.
57 if ( \preg_match_all( "/$regexp/iU", $content, $matches, \PREG_SET_ORDER ) ) {
58 foreach ( $matches as $match ) {
59 $images[ $match[2] ] = 0;
60 }
61 }
62
63 return $images;
64 }
65
66 /**
67 * Gathers all images from content with WP's WP_HTML_Tag_Processor() and returns them along with their IDs, if
68 * possible.
69 *
70 * @param string $content The content.
71 *
72 * @return int[] An associated array of image IDs, keyed by their URL.
73 */
74 protected function gather_images_wp( $content ) {
75 $processor = new WP_HTML_Tag_Processor( $content );
76 $images = [];
77
78 $query = [
79 'tag_name' => 'img',
80 ];
81
82 /**
83 * Filter 'wpseo_image_attribute_containing_id' - Allows filtering what attribute will be used to extract image IDs from.
84 *
85 * Defaults to "class", which is where WP natively stores the image IDs, in a `wp-image-<ID>` format.
86 *
87 * @api string The attribute to be used to extract image IDs from.
88 */
89 $attribute = \apply_filters( 'wpseo_image_attribute_containing_id', 'class' );
90 while ( $processor->next_tag( $query ) ) {
91 $src_raw = $processor->get_attribute( 'src' );
92 if ( ! $src_raw ) {
93 continue;
94 }
95
96 $src = \htmlentities( $src_raw, ( \ENT_QUOTES | \ENT_SUBSTITUTE | \ENT_HTML401 ), \get_bloginfo( 'charset' ) );
97 $classes = $processor->get_attribute( $attribute );
98 $id = $this->extract_id_of_classes( $classes );
99
100 $images[ $src ] = $id;
101 }
102
103 return $images;
104 }
105
106 /**
107 * Gathers all images from content with DOMDocument() and returns them along with their IDs, if possible.
108 *
109 * @param string $content The content.
110 *
111 * @return int[] An associated array of image IDs, keyed by their URL.
112 */
113 protected function gather_images_domdocument( $content ) {
114 $images = [];
115 $charset = \get_bloginfo( 'charset' );
116
117 /**
118 * Filter 'wpseo_image_attribute_containing_id' - Allows filtering what attribute will be used to extract image IDs from.
119 *
120 * Defaults to "class", which is where WP natively stores the image IDs, in a `wp-image-<ID>` format.
121 *
122 * @api string The attribute to be used to extract image IDs from.
123 */
124 $attribute = \apply_filters( 'wpseo_image_attribute_containing_id', 'class' );
125
126 \libxml_use_internal_errors( true );
127 $post_dom = new DOMDocument();
128 $post_dom->loadHTML( '<?xml encoding="' . $charset . '">' . $content );
129 \libxml_clear_errors();
130
131 foreach ( $post_dom->getElementsByTagName( 'img' ) as $img ) {
132 $src = \htmlentities( $img->getAttribute( 'src' ), ( \ENT_QUOTES | \ENT_SUBSTITUTE | \ENT_HTML401 ), $charset );
133 $classes = $img->getAttribute( $attribute );
134
135 $id = $this->extract_id_of_classes( $classes );
136
137 $images[ $src ] = $id;
138 }
139
140 return $images;
141 }
142
143 /**
144 * Extracts image ID out of the image's classes.
145 *
146 * @param string $classes The classes assigned to the image.
147 *
148 * @return int The ID that's extracted from the classes.
149 */
150 protected function extract_id_of_classes( $classes ) {
151 if ( ! $classes ) {
152 return 0;
153 }
154
155 /**
156 * Filter 'wpseo_extract_id_pattern' - Allows filtering the regex patern to be used to extract image IDs from class/attribute names.
157 *
158 * Defaults to the pattern that extracts image IDs from core's `wp-image-<ID>` native format in image classes.
159 *
160 * @api string The regex pattern to be used to extract image IDs from class names. Empty string if the whole class/attribute should be returned.
161 */
162 $pattern = \apply_filters( 'wpseo_extract_id_pattern', '/(?<!\S)wp-image-(\d+)(?!\S)/i' );
163
164 if ( $pattern === '' ) {
165 return (int) $classes;
166 }
167
168 $matches = [];
169
170 if ( \preg_match( $pattern, $classes, $matches ) ) {
171
172 return (int) $matches[1];
173 }
174
175 return 0;
176 }
177 }
178