PluginProbe
Gutenberg / 16.2.1
Gutenberg v16.2.1
24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 All 403 releases
gutenberg / lib / experimental / interactivity-api / class-wp-directive-processor.php

class-wp-directive-processor.php in Gutenberg 16.2.1, at lib/experimental/interactivity-api/class-wp-directive-processor.php

225 lines 5.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WP_Directive_Processor class
4 *
5 * @package Gutenberg
6 * @subpackage Interactivity API
7 */
8
9 /**
10 * This processor is built on top of the HTML Tag Processor and augments its
11 * capabilities to process the Interactivity API directives.
12 *
13 * IMPORTANT DISCLAIMER: This code is highly experimental and its only purpose
14 * is to provide a way to test the server-side rendering of the Interactivity
15 * API. Most of this code will be discarded once the HTML Processor is
16 * available. Please restrain from investing unnecessary time and effort trying
17 * to improve this code.
18 */
19 class WP_Directive_Processor extends WP_HTML_Tag_Processor {
20
21 /**
22 * An array of root blocks.
23 *
24 * @var array
25 */
26 static $root_blocks = array();
27
28 /**
29 * Add a root block to the list.
30 *
31 * @param array $block The block to add.
32 *
33 * @return void
34 */
35 public static function add_root_block( $block ) {
36 self::$root_blocks[] = md5( serialize( $block ) );
37 }
38
39 /**
40 * Check if block is a root block.
41 *
42 * @param array $block The block to check.
43 *
44 * @return bool True if block is a root block, false otherwise.
45 */
46 public static function is_root_block( $block ) {
47 return in_array( md5( serialize( $block ) ), self::$root_blocks, true );
48 }
49
50
51 /**
52 * Find the matching closing tag for an opening tag.
53 *
54 * When called while on an open tag, traverse the HTML until we find the
55 * matching closing tag, respecting any in-between content, including nested
56 * tags of the same name. Return false when called on a closing or void tag,
57 * or if no matching closing tag was found.
58 *
59 * @return bool Whether a matching closing tag was found.
60 */
61 public function next_balanced_closer() {
62 $depth = 0;
63
64 $tag_name = $this->get_tag();
65
66 if ( self::is_html_void_element( $tag_name ) ) {
67 return false;
68 }
69
70 while ( $this->next_tag(
71 array(
72 'tag_name' => $tag_name,
73 'tag_closers' => 'visit',
74 )
75 ) ) {
76 if ( ! $this->is_tag_closer() ) {
77 $depth++;
78 continue;
79 }
80
81 if ( 0 === $depth ) {
82 return true;
83 }
84
85 $depth--;
86 }
87
88 return false;
89 }
90
91 /**
92 * Return the content between two balanced tags.
93 *
94 * When called on an opening tag, return the HTML content found between that
95 * opening tag and its matching closing tag.
96 *
97 * @return string The content between the current opening and its matching
98 * closing tag.
99 */
100 public function get_inner_html() {
101 $bookmarks = $this->get_balanced_tag_bookmarks();
102 if ( ! $bookmarks ) {
103 return false;
104 }
105 list( $start_name, $end_name ) = $bookmarks;
106
107 $start = $this->bookmarks[ $start_name ]->end + 1;
108 $end = $this->bookmarks[ $end_name ]->start;
109
110 $this->seek( $start_name ); // Return to original position.
111 $this->release_bookmark( $start_name );
112 $this->release_bookmark( $end_name );
113
114 return substr( $this->html, $start, $end - $start );
115 }
116
117 /**
118 * Set the content between two balanced tags.
119 *
120 * When called on an opening tag, set the HTML content found between that
121 * opening tag and its matching closing tag.
122 *
123 * @param string $new_html The string to replace the content between the
124 * matching tags with.
125 *
126 * @return bool Whether the content was successfully replaced.
127 */
128 public function set_inner_html( $new_html ) {
129 $this->get_updated_html(); // Apply potential previous updates.
130
131 $bookmarks = $this->get_balanced_tag_bookmarks();
132 if ( ! $bookmarks ) {
133 return false;
134 }
135 list( $start_name, $end_name ) = $bookmarks;
136
137 $start = $this->bookmarks[ $start_name ]->end + 1;
138 $end = $this->bookmarks[ $end_name ]->start;
139
140 $this->seek( $start_name ); // Return to original position.
141 $this->release_bookmark( $start_name );
142 $this->release_bookmark( $end_name );
143
144 $this->lexical_updates[] = new WP_HTML_Text_Replacement( $start, $end, $new_html );
145 return true;
146 }
147
148 /**
149 * Return a pair of bookmarks for the current opening tag and the matching
150 * closing tag.
151 *
152 * @return array|false A pair of bookmarks, or false if there's no matching
153 * closing tag.
154 */
155 public function get_balanced_tag_bookmarks() {
156 $i = 0;
157 while ( array_key_exists( 'start' . $i, $this->bookmarks ) ) {
158 ++$i;
159 }
160 $start_name = 'start' . $i;
161
162 $this->set_bookmark( $start_name );
163 if ( ! $this->next_balanced_closer() ) {
164 $this->release_bookmark( $start_name );
165 return false;
166 }
167
168 $i = 0;
169 while ( array_key_exists( 'end' . $i, $this->bookmarks ) ) {
170 ++$i;
171 }
172 $end_name = 'end' . $i;
173 $this->set_bookmark( $end_name );
174
175 return array( $start_name, $end_name );
176 }
177
178 /**
179 * Whether a given HTML element is void (e.g. <br>).
180 *
181 * @param string $tag_name The element in question.
182 * @return bool True if the element is void.
183 *
184 * @see https://html.spec.whatwg.org/#elements-2
185 */
186 public static function is_html_void_element( $tag_name ) {
187 switch ( $tag_name ) {
188 case 'AREA':
189 case 'BASE':
190 case 'BR':
191 case 'COL':
192 case 'EMBED':
193 case 'HR':
194 case 'IMG':
195 case 'INPUT':
196 case 'LINK':
197 case 'META':
198 case 'SOURCE':
199 case 'TRACK':
200 case 'WBR':
201 return true;
202
203 default:
204 return false;
205 }
206 }
207
208 /**
209 * Extract and return the directive type and the the part after the double
210 * hyphen from an attribute name (if present), in an array format.
211 *
212 * Examples:
213 *
214 * 'wp-island' => array( 'wp-island', null )
215 * 'wp-bind--src' => array( 'wp-bind', 'src' )
216 * 'wp-thing--and--thang' => array( 'wp-thing', 'and--thang' )
217 *
218 * @param string $name The attribute name.
219 * @return array The resulting array
220 */
221 public static function parse_attribute_name( $name ) {
222 return explode( '--', $name, 2 );
223 }
224 }
225