PluginProbe
Gutenberg / 16.5.1
Gutenberg v16.5.1
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 7.4.0 All 402 releases
gutenberg / lib / experimental / interactivity-api / directive-processing.php

directive-processing.php in Gutenberg 16.5.1, at lib/experimental/interactivity-api/directive-processing.php

171 lines 5.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Functions and hooks to process the server side rendering of the Interactivity
4 * API directives.
5 *
6 * @package Gutenberg
7 * @subpackage Interactivity API
8 */
9
10 /**
11 * Process directives in each block.
12 *
13 * @param string $block_content The block content.
14 * @param array $block The full block.
15 *
16 * @return string Filtered block content.
17 */
18 function gutenberg_interactivity_process_directives_in_root_blocks( $block_content, $block ) {
19 // Don't process inner blocks or root blocks that don't contain directives.
20 if ( ! WP_Directive_Processor::is_root_block( $block ) || strpos( $block_content, 'data-wp-' ) === false ) {
21 return $block_content;
22 }
23
24 // TODO: Add some directive/components registration mechanism.
25 $directives = array(
26 'data-wp-bind' => 'gutenberg_interactivity_process_wp_bind',
27 'data-wp-context' => 'gutenberg_interactivity_process_wp_context',
28 'data-wp-class' => 'gutenberg_interactivity_process_wp_class',
29 'data-wp-style' => 'gutenberg_interactivity_process_wp_style',
30 'data-wp-text' => 'gutenberg_interactivity_process_wp_text',
31 );
32
33 $tags = new WP_Directive_Processor( $block_content );
34 $tags = gutenberg_interactivity_process_directives( $tags, 'data-wp-', $directives );
35 return $tags->get_updated_html();
36 }
37 add_filter( 'render_block', 'gutenberg_interactivity_process_directives_in_root_blocks', 10, 2 );
38
39 /**
40 * Mark the inner blocks with a temporary property so we can discard them later,
41 * and process only the root blocks.
42 *
43 * @param array $parsed_block The parsed block.
44 * @param array $source_block The source block.
45 * @param array $parent_block The parent block.
46 *
47 * @return array The parsed block.
48 */
49 function gutenberg_interactivity_mark_inner_blocks( $parsed_block, $source_block, $parent_block ) {
50 if ( ! isset( $parent_block ) ) {
51 WP_Directive_Processor::add_root_block( $parsed_block );
52 }
53 return $parsed_block;
54 }
55 add_filter( 'render_block_data', 'gutenberg_interactivity_mark_inner_blocks', 10, 3 );
56
57 /**
58 * Process directives.
59 *
60 * @param WP_Directive_Processor $tags An instance of the WP_Directive_Processor.
61 * @param string $prefix Attribute prefix.
62 * @param string[] $directives Directives.
63 *
64 * @return WP_Directive_Processor The modified instance of the
65 * WP_Directive_Processor.
66 */
67 function gutenberg_interactivity_process_directives( $tags, $prefix, $directives ) {
68 $context = new WP_Directive_Context;
69 $tag_stack = array();
70
71 while ( $tags->next_tag( array( 'tag_closers' => 'visit' ) ) ) {
72 $tag_name = $tags->get_tag();
73
74 // Is this a tag that closes the latest opening tag?
75 if ( $tags->is_tag_closer() ) {
76 if ( 0 === count( $tag_stack ) ) {
77 continue;
78 }
79
80 list( $latest_opening_tag_name, $attributes ) = end( $tag_stack );
81 if ( $latest_opening_tag_name === $tag_name ) {
82 array_pop( $tag_stack );
83
84 // If the matching opening tag didn't have any directives, we move on.
85 if ( 0 === count( $attributes ) ) {
86 continue;
87 }
88 }
89 } else {
90 $attributes = array();
91 foreach ( $tags->get_attribute_names_with_prefix( $prefix ) as $name ) {
92 /*
93 * Removes the part after the double hyphen before looking for
94 * the directive processor inside `$directives`, e.g., "wp-bind"
95 * from "wp-bind--src" and "wp-context" from "wp-context" etc...
96 */
97 list( $type ) = WP_Directive_Processor::parse_attribute_name( $name );
98 if ( array_key_exists( $type, $directives ) ) {
99 $attributes[] = $type;
100 }
101 }
102
103 /*
104 * If this is an open tag, and if it either has directives, or if
105 * we're inside a tag that does, take note of this tag and its
106 * directives so we can call its directive processor once we
107 * encounter the matching closing tag.
108 */
109 if (
110 ! WP_Directive_Processor::is_html_void_element( $tags->get_tag() ) &&
111 ( 0 !== count( $attributes ) || 0 !== count( $tag_stack ) )
112 ) {
113 $tag_stack[] = array( $tag_name, $attributes );
114 }
115 }
116
117 foreach ( $attributes as $attribute ) {
118 call_user_func( $directives[ $attribute ], $tags, $context );
119 }
120 }
121
122 return $tags;
123 }
124
125 /**
126 * Resolve the reference using the store and the context from the provided path.
127 *
128 * @param string $path Path.
129 * @param array $context Context data.
130 * @return mixed
131 */
132 function gutenberg_interactivity_evaluate_reference( $path, array $context = array() ) {
133 $store = array_merge(
134 WP_Interactivity_Store::get_data(),
135 array( 'context' => $context )
136 );
137
138 /*
139 * Check first if the directive path is preceded by a negator operator (!),
140 * indicating that the value obtained from the Interactivity Store (or the
141 * passed context) using the subsequent path should be negated.
142 */
143 $should_negate_value = '!' === $path[0];
144
145 $path = $should_negate_value ? substr( $path, 1 ) : $path;
146 $path_segments = explode( '.', $path );
147 $current = $store;
148 foreach ( $path_segments as $p ) {
149 if ( isset( $current[ $p ] ) ) {
150 $current = $current[ $p ];
151 } else {
152 return null;
153 }
154 }
155
156 /*
157 * Check if $current is an anonymous function or an arrow function, and if
158 * so, call it passing the store. Other types of callables are ignored on
159 * purpose, as arbitrary strings or arrays could be wrongly evaluated as
160 * "callables".
161 *
162 * E.g., "file" is an string and a "callable" (the "file" function exists).
163 */
164 if ( $current instanceof Closure ) {
165 $current = call_user_func( $current, $store );
166 }
167
168 // Return the opposite if it has a negator operator (!).
169 return $should_negate_value ? ! $current : $current;
170 }
171