PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.1-beta.3
Jetpack – WP Security, Backup, Speed, & Growth v16.1-beta.3
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 / vendor / wp-php-toolkit / data-liberation / class-dataliberationhtmlprocessor.php
jetpack / vendor / wp-php-toolkit / data-liberation Last commit date
BlockMarkup 6 days ago CSS 6 days ago DataFormatConsumer 6 days ago DataFormatProducer 6 days ago EntityReader 6 days ago EntityWriter 6 days ago Importer 6 days ago URL 6 days ago vendor-patched 6 days ago LICENSE.md 6 days ago README.md 6 days ago class-dataliberationexception.php 6 days ago class-dataliberationhtmlprocessor.php 6 days ago class-importentity.php 6 days ago composer.json 6 days ago rector.php 6 days ago
class-dataliberationhtmlprocessor.php
266 lines
1 <?php
2
3 namespace WordPress\DataLiberation;
4
5 use WP_HTML_Processor;
6 use WP_HTML_Tag_Processor;
7
8 /**
9 * HTML Processor helpers used by the Data Liberation import pipeline.
10 *
11 * This class intentionally keeps the same no-burden progressive upgrade model
12 * as the public HTML API. When the native extension is loaded, inherited cursor
13 * operations such as `next_tag()`, `next_token()`, `get_attribute()`, and
14 * `get_modifiable_text()` use the native processor automatically. Consumers do
15 * not need to opt in or choose a separate class.
16 *
17 * The Data Liberation-only helpers below expose source-string offsets and
18 * substrings:
19 *
20 * $p = DataLiberationHTMLProcessor::create_fragment( '<h1>Title</h1><p>Body</p>' );
21 * $p->next_tag( 'H1' );
22 * $inner_html = $p->get_inner_html(); // "Title".
23 *
24 * $p->skip_to_closer();
25 * $after_h1 = substr( $html, $p->get_string_index_after_current_token() );
26 *
27 * Those offsets are an implementation detail of the PHP parser; native HTML
28 * bookmarks are opaque and do not expose byte spans. To keep native processing
29 * available for normal scanning, these helpers replay the current cursor
30 * position on a PHP-backed mirror and read the offsets from that mirror.
31 */
32 class DataLiberationHTMLProcessor extends WP_HTML_Processor {
33
34 /**
35 * Number of public tokens consumed from this processor.
36 *
37 * This is enough to replay the cursor onto a PHP-backed mirror for the
38 * Data Liberation helpers that need PHP bookmark spans.
39 *
40 * @var int
41 */
42 private $tokens_parsed_for_php_replay = 0;
43
44 /**
45 * Token counts captured for public bookmarks.
46 *
47 * Native bookmarks are intentionally opaque. This map lets a later `seek()`
48 * restore the replay counter when the bookmark was created through this
49 * class, so the PHP mirror can land on the same token.
50 *
51 * @var array<string,int>
52 */
53 private $php_replay_bookmarks = array();
54
55 /**
56 * Whether the current cursor can be replayed onto the PHP parser.
57 *
58 * @var bool
59 */
60 private $can_replay_php_cursor = true;
61
62 /**
63 * Whether this instance exists only as a PHP replay mirror.
64 *
65 * @var bool
66 */
67 private $is_php_replay = false;
68
69 public function next_token(): bool {
70 $matched = parent::next_token();
71 if ( $matched ) {
72 ++$this->tokens_parsed_for_php_replay;
73 }
74
75 return $matched;
76 }
77
78 public function set_bookmark( $name ): bool {
79 $set = parent::set_bookmark( $name );
80 if ( $set ) {
81 $this->php_replay_bookmarks[ $name ] = $this->tokens_parsed_for_php_replay;
82 }
83
84 return $set;
85 }
86
87 public function release_bookmark( $name ): bool {
88 $released = parent::release_bookmark( $name );
89 if ( $released ) {
90 unset( $this->php_replay_bookmarks[ $name ] );
91 }
92
93 return $released;
94 }
95
96 public function seek( $bookmark_name ): bool {
97 $found = parent::seek( $bookmark_name );
98 if ( ! $found ) {
99 return false;
100 }
101
102 if ( isset( $this->php_replay_bookmarks[ $bookmark_name ] ) ) {
103 $this->tokens_parsed_for_php_replay = $this->php_replay_bookmarks[ $bookmark_name ];
104 } else {
105 $this->can_replay_php_cursor = false;
106 }
107
108 return true;
109 }
110
111 public function get_inner_html() {
112 if ( $this->has_active_native_processor() ) {
113 $php_processor = $this->create_php_replay_at_current_token();
114
115 return $php_processor instanceof self ? $php_processor->get_inner_html() : false;
116 }
117
118 if ( '#tag' !== $this->get_token_type() ) {
119 return false;
120 }
121
122 if ( $this->is_tag_closer() ) {
123 return false;
124 }
125
126 if ( false === $this->set_bookmark( 'tag-start' ) ) {
127 return false;
128 }
129
130 $this->skip_to_closer();
131
132 if ( false === $this->set_bookmark( 'tag-end' ) ) {
133 $this->release_bookmark( 'tag-start' );
134
135 return false;
136 }
137
138 $tag_start = $this->get_bookmark_span( 'tag-start' );
139 $tag_end = $this->get_bookmark_span( 'tag-end' );
140 $inner_html_start = $tag_start->start + $tag_start->length;
141 $inner_html_end = $tag_end->start - $inner_html_start;
142
143 if ( ! $this->is_php_replay ) {
144 $this->seek( 'tag-start' );
145 $this->release_bookmark( 'tag-start' );
146 $this->release_bookmark( 'tag-end' );
147 }
148
149 return substr(
150 $this->html,
151 $inner_html_start,
152 $inner_html_end
153 );
154 }
155
156 public function skip_to_closer() {
157 $starting_depth = $this->get_current_depth();
158 while ( $this->next_token() ) {
159 if (
160 '#tag' === $this->get_token_type() &&
161 $this->is_tag_closer() &&
162 $this->get_current_depth() === $starting_depth - 1
163 ) {
164 return true;
165 }
166 }
167
168 return false;
169 }
170
171 public function get_string_index_after_current_token() {
172 if ( $this->has_active_native_processor() ) {
173 $php_processor = $this->create_php_replay_at_current_token();
174
175 return $php_processor instanceof self ? $php_processor->get_string_index_after_current_token() : false;
176 }
177
178 $name = 'current_token';
179 $this->set_bookmark( $name );
180 $bookmark = $this->get_bookmark_span( '_' . $name );
181 $this->release_bookmark( $name );
182
183 return $bookmark->start + $bookmark->length;
184 }
185
186 /**
187 * Returns a PHP bookmark span from this object or its PHP delegate.
188 *
189 * @param string $name Bookmark name.
190 * @return WP_HTML_Span Bookmark span.
191 */
192 private function get_bookmark_span( $name ) {
193 if ( isset( $this->bookmarks[ $name ] ) ) {
194 return $this->bookmarks[ $name ];
195 }
196 if ( isset( $this->bookmarks[ '_' . $name ] ) ) {
197 return $this->bookmarks[ '_' . $name ];
198 }
199
200 if ( property_exists( $this, 'php_processor' ) ) {
201 $property = new \ReflectionProperty( 'WP_HTML_PHP_Tag_Processor', 'bookmarks' );
202 $property->setAccessible( true );
203 $bookmarks = $property->getValue( $this->php_processor );
204 if ( isset( $bookmarks[ $name ] ) ) {
205 return $bookmarks[ $name ];
206 }
207 if ( isset( $bookmarks[ '_' . $name ] ) ) {
208 return $bookmarks[ '_' . $name ];
209 }
210 }
211
212 return null;
213 }
214
215 /**
216 * Checks whether this processor has a native delegate.
217 *
218 * The pure PHP HTML Processor loaded on older runtimes does not define the
219 * native-wrapper helper method, so callers must guard the method before
220 * invoking it.
221 *
222 * @return bool Whether a native processor is active.
223 */
224 private function has_active_native_processor() {
225 return method_exists( $this, 'has_native_processor' ) && $this->has_native_processor();
226 }
227
228 /**
229 * Creates a PHP-backed processor at the same public cursor token.
230 *
231 * Native cursor operations are faster for scanning, but native bookmarks do
232 * not expose byte offsets. For offset-only helpers this method replays the
233 * already-consumed public token count on a mirror with native delegation
234 * disabled. Example:
235 *
236 * $native = DataLiberationHTMLProcessor::create_fragment( '<h1>Title</h1><p>Body</p>' );
237 * $native->next_tag( 'H1' );
238 *
239 * // The mirror runs the PHP parser through the same first token, so its
240 * // bookmark spans point to the original `<h1>` source bytes.
241 * $php = $native->create_php_replay_at_current_token();
242 *
243 * @return self|null PHP-backed processor at the current token, or null.
244 */
245 private function create_php_replay_at_current_token() {
246 if ( ! $this->can_replay_php_cursor ) {
247 return null;
248 }
249
250 $processor = static::create_fragment( $this->html );
251 if ( ! $processor instanceof self ) {
252 return null;
253 }
254
255 $processor->set_native_processor( null );
256 $processor->is_php_replay = true;
257 for ( $i = 0; $i < $this->tokens_parsed_for_php_replay; ++$i ) {
258 if ( ! $processor->next_token() ) {
259 return null;
260 }
261 }
262
263 return $processor;
264 }
265 }
266