Make WordPress Core

Ticket #64093: interactivity-api-patch.diff

File interactivity-api-patch.diff, 7.6 KB (added by michelleeby, 10 months ago)
Line 
1107a108,115
2> /**
3> * Cache for data-wp-each template blueprints.
4> *
5> * @since 6.7.0
6> * @var array
7> */
8> private static $template_blueprints = array();
9>
101155,1174c1163,1172
11< /**
12< * Processes the `data-wp-each` directive.
13< *
14< * This directive gets an array passed as reference and iterates over it
15< * generating new content for each item based on the inner markup of the
16< * `template` tag.
17< *
18< * @since 6.5.0
19< *
20< * @param WP_Interactivity_API_Directives_Processor $p The directives processor instance.
21< * @param string $mode Whether the processing is entering or exiting the tag.
22< * @param array $tag_stack The reference to the tag stack.
23< */
24< private function data_wp_each_processor( WP_Interactivity_API_Directives_Processor $p, string $mode, array &$tag_stack ) {
25< if ( 'enter' === $mode && 'TEMPLATE' === $p->get_tag() ) {
26< $attribute_name = $p->get_attribute_names_with_prefix( 'data-wp-each' )[0];
27< $extracted_suffix = $this->extract_prefix_and_suffix( $attribute_name );
28< $item_name = isset( $extracted_suffix[1] ) ? $this->kebab_to_camel_case( $extracted_suffix[1] ) : 'item';
29< $attribute_value = $p->get_attribute( $attribute_name );
30< $result = $this->evaluate( $attribute_value );
31---
32> /**
33> * Processes the data-wp-each directive using a pre-computation and caching strategy.
34> *
35> * @param WP_HTML_Tag_Processor $p The WP_HTML_Tag_Processor instance.
36> * @param array $context The context.
37> */
38> private function data_wp_each_processor( WP_HTML_Tag_Processor $p, array $context ) {
39> if ( 'TEMPLATE' !== $p->get_tag() ) {
40> return;
41> }
421176,1177c1174,1187
43< // Gets the content between the template tags and leaves the cursor in the closer tag.
44< $inner_content = $p->get_content_between_balanced_template_tags();
45---
46> $directive_value = $p->get_attribute( 'data-wp-each' );
47> list( $item_name, $array_name ) = explode( ' in ', $directive_value );
48> $item_name = trim( $item_name );
49> $array_name = trim( $array_name );
50>
51> $template_html = '';
52> if ( $p->next_tag(
53> array(
54> 'tag_closers' => 'visit',
55> 'tag_name' => 'TEMPLATE',
56> )
57> ) ) {
58> $template_html = $p->get_content_between_balanced_tags();
59> }
601179,1211c1189,1194
61< // Checks if there is a manual server-side directive processing.
62< $template_end = 'data-wp-each: template end';
63< $p->set_bookmark( $template_end );
64< $p->next_tag();
65< $manual_sdp = $p->get_attribute( 'data-wp-each-child' );
66< $p->seek( $template_end ); // Rewinds to the template closer tag.
67< $p->release_bookmark( $template_end );
68<
69< /*
70< * It doesn't process in these situations:
71< * - Manual server-side directive processing.
72< * - Empty or non-array values.
73< * - Associative arrays because those are deserialized as objects in JS.
74< * - Templates that contain top-level texts because those texts can't be
75< * identified and removed in the client.
76< */
77< if (
78< $manual_sdp ||
79< empty( $result ) ||
80< ! is_array( $result ) ||
81< ! array_is_list( $result ) ||
82< ! str_starts_with( trim( $inner_content ), '<' ) ||
83< ! str_ends_with( trim( $inner_content ), '>' )
84< ) {
85< array_pop( $tag_stack );
86< return;
87< }
88<
89< // Extracts the namespace from the directive attribute value.
90< $namespace_value = end( $this->namespace_stack );
91< list( $namespace_value ) = is_string( $attribute_value ) && ! empty( $attribute_value )
92< ? $this->extract_directive_value( $attribute_value, $namespace_value )
93< : array( $namespace_value, null );
94---
95> $p->next_tag(
96> array(
97> 'tag_name' => 'TEMPLATE',
98> 'tag_closers' => 'visit',
99> )
100> );
1011213,1220c1196
102< // Processes the inner content for each item of the array.
103< $processed_content = '';
104< foreach ( $result as $item ) {
105< // Creates a new context that includes the current item of the array.
106< $this->context_stack[] = array_replace_recursive(
107< end( $this->context_stack ) !== false ? end( $this->context_stack ) : array(),
108< array( $namespace_value => array( $item_name => $item ) )
109< );
110---
111> $cache_key = md5( $template_html );
1121222,1223c1198,1202
113< // Processes the inner content with the new context.
114< $processed_item = $this->_process_directives( $inner_content );
115---
116> // 1. Pre-computation: Parse the template once and create a "blueprint" of bookmarks.
117> if ( ! isset( self::$template_blueprints[ $cache_key ] ) ) {
118> self::$template_blueprints[ $cache_key ] = array();
119> $blueprint_p = new WP_HTML_Tag_Processor( $template_html );
120> $bookmark_counter = 0;
1211225,1229c1204,1212
122< if ( null === $processed_item ) {
123< // If the HTML is unbalanced, stop processing it.
124< array_pop( $this->context_stack );
125< return;
126< }
127---
128> while ( $blueprint_p->next_tag() ) {
129> // Find any tag that has one or more `data-wp-` directives.
130> if ( ! empty( $blueprint_p->get_attribute_names_with_prefix( 'data-wp-' ) ) ) {
131> $bookmark_name = 'directive_tag_' . $bookmark_counter++;
132> $blueprint_p->set_bookmark( $bookmark_name );
133> self::$template_blueprints[ $cache_key ][] = $bookmark_name;
134> }
135> }
136> }
1371231,1237c1214,1215
138< // Adds the `data-wp-each-child` to each top-level tag.
139< $i = new WP_Interactivity_API_Directives_Processor( $processed_item );
140< while ( $i->next_tag() ) {
141< $i->set_attribute( 'data-wp-each-child', true );
142< $i->next_balanced_tag_closer_tag();
143< }
144< $processed_content .= $i->get_updated_html();
145---
146> $bookmarks = self::$template_blueprints[ $cache_key ];
147> $html = '';
1481239,1241c1217,1219
149< // Removes the current context from the stack.
150< array_pop( $this->context_stack );
151< }
152---
153> // 2. Optimized Loop: Jump directly to bookmarked tags and process them.
154> foreach ( $this->get_context_value( $array_name, $context ) as $item_context ) {
155> $item_p = new WP_HTML_Tag_Processor( $template_html );
1561243,1244c1221,1226
157< // Appends the processed content after the tag closer of the template.
158< $p->append_content_after_template_tag_closer( $processed_content );
159---
160> // Recreate the same inner context as the original function.
161> $inner_context = array(
162> $item_name => $item_context,
163> 'context' => $item_context,
164> );
165> $current_context = array_merge( $context, $inner_context );
1661246,1249c1228,1239
167< // Pops the last tag because it skipped the closing tag of the template tag.
168< array_pop( $tag_stack );
169< }
170< }
171---
172> // Instead of scanning all tags, iterate through our pre-computed bookmarks.
173> foreach ( $bookmarks as $bookmark_name ) {
174> $item_p->seek( $bookmark_name );
175> // Call the original, robust `process_directives` function on the targeted tag.
176> $this->process_directives( $item_p, $current_context );
177> }
178>
179> $html .= $item_p->get_updated_html();
180> }
181>
182> $p->insert_before_closing_tag( $html );
183> }