11<?php
22
33/**
4- * Parser node backed by a native (Rust) AST .
4+ * Native- backed parser node .
55 *
6- * Instances of this class are constructed exclusively by the native MySQL
7- * parser extension: when the extension parses a query, it produces a tree of
8- * `WP_MySQL_Native_Parser_Node` objects whose `$native_ast` and
9- * `$native_node_index` fields point into a Rust-owned AST buffer. Read methods
10- * (`get_start`, `has_child`, `get_children`, ...) delegate to the extension so
11- * children are never materialized into PHP arrays unless something actually
12- * asks for them.
13- *
14- * The hedge in those methods (`if ( $this->has_unmaterialized_native_ast() )`)
15- * is NOT a runtime check for whether the native extension is loaded — if this
16- * class is in use, the extension is loaded by definition. It checks whether
17- * THIS specific node still has an authoritative native AST behind it. A node
18- * loses its native backing the first time it is mutated from PHP via
19- * `append_child()` or `merge_fragment()`: those overrides call
20- * `materialize_native_children()`, which copies the native children into the
21- * inherited `$children` array and then drops the native AST reference. From
22- * that point on, the node is a plain PHP-backed `WP_Parser_Node` and the read
23- * methods fall through to the parent implementation.
24- *
25- * Mutation from PHP is real and intentional — query rewriters in
26- * `WP_PDO_MySQL_On_SQLite` (e.g. building synthetic `count(*)` expressions)
27- * call `append_child()` on parsed nodes. The lazy-then-materialize design
28- * keeps the fast path (read-only traversal) cheap while still allowing
29- * mutation when callers need it.
6+ * This subclass keeps the regular WP_Parser_Node API while delegating lazy AST
7+ * reads to the optional native MySQL parser extension. The base node remains a
8+ * plain PHP tree node for the polyfill parser.
309 */
3110class WP_MySQL_Native_Parser_Node extends WP_Parser_Node {
3211 private $ native_ast = null ;
@@ -39,24 +18,13 @@ public function __construct( $rule_id, $rule_name, $native_ast = null, $native_n
3918 $ this ->native_node_index = $ native_node_index ;
4019 }
4120
42- /**
43- * Materializes any native children before mutating, then appends.
44- *
45- * Once a node is mutated, its native AST is no longer authoritative, so we
46- * copy the native children into PHP storage first and drop the native
47- * reference. Subsequent reads use the parent's PHP implementation.
48- */
21+ /** @inheritDoc */
4922 public function append_child ( $ node ) {
5023 $ this ->materialize_native_children ();
5124 parent ::append_child ( $ node );
5225 }
5326
54- /**
55- * Materializes any native children on both nodes before merging.
56- *
57- * @see self::append_child() for why materialization is required before
58- * mutation.
59- */
27+ /** @inheritDoc */
6028 public function merge_fragment ( $ node ) {
6129 $ this ->materialize_native_children ();
6230 if ( $ node instanceof self ) {
@@ -67,158 +35,138 @@ public function merge_fragment( $node ) {
6735
6836 /** @inheritDoc */
6937 public function has_child (): bool {
70- if ( $ this ->has_unmaterialized_native_ast () ) {
38+ if ( $ this ->has_native_ast () ) {
7139 return wp_sqlite_mysql_native_ast_has_child ( $ this ->native_ast , $ this ->native_node_index );
7240 }
7341 return parent ::has_child ();
7442 }
7543
7644 /** @inheritDoc */
7745 public function has_child_node ( ?string $ rule_name = null ): bool {
78- if ( $ this ->has_unmaterialized_native_ast () ) {
46+ if ( $ this ->has_native_ast () ) {
7947 return wp_sqlite_mysql_native_ast_has_child_node ( $ this ->native_ast , $ this ->native_node_index , $ rule_name );
8048 }
8149 return parent ::has_child_node ( $ rule_name );
8250 }
8351
8452 /** @inheritDoc */
8553 public function has_child_token ( ?int $ token_id = null ): bool {
86- if ( $ this ->has_unmaterialized_native_ast () ) {
54+ if ( $ this ->has_native_ast () ) {
8755 return wp_sqlite_mysql_native_ast_has_child_token ( $ this ->native_ast , $ this ->native_node_index , $ token_id );
8856 }
8957 return parent ::has_child_token ( $ token_id );
9058 }
9159
9260 /** @inheritDoc */
9361 public function get_first_child () {
94- if ( $ this ->has_unmaterialized_native_ast () ) {
62+ if ( $ this ->has_native_ast () ) {
9563 return wp_sqlite_mysql_native_ast_get_first_child ( $ this ->native_ast , $ this ->native_node_index );
9664 }
9765 return parent ::get_first_child ();
9866 }
9967
10068 /** @inheritDoc */
10169 public function get_first_child_node ( ?string $ rule_name = null ): ?WP_Parser_Node {
102- if ( $ this ->has_unmaterialized_native_ast () ) {
70+ if ( $ this ->has_native_ast () ) {
10371 return wp_sqlite_mysql_native_ast_get_first_child_node ( $ this ->native_ast , $ this ->native_node_index , $ rule_name );
10472 }
10573 return parent ::get_first_child_node ( $ rule_name );
10674 }
10775
10876 /** @inheritDoc */
10977 public function get_first_child_token ( ?int $ token_id = null ): ?WP_Parser_Token {
110- if ( $ this ->has_unmaterialized_native_ast () ) {
78+ if ( $ this ->has_native_ast () ) {
11179 return wp_sqlite_mysql_native_ast_get_first_child_token ( $ this ->native_ast , $ this ->native_node_index , $ token_id );
11280 }
11381 return parent ::get_first_child_token ( $ token_id );
11482 }
11583
11684 /** @inheritDoc */
11785 public function get_first_descendant_node ( ?string $ rule_name = null ): ?WP_Parser_Node {
118- if ( $ this ->has_unmaterialized_native_ast () ) {
86+ if ( $ this ->has_native_ast () ) {
11987 return wp_sqlite_mysql_native_ast_get_first_descendant_node ( $ this ->native_ast , $ this ->native_node_index , $ rule_name );
12088 }
12189 return parent ::get_first_descendant_node ( $ rule_name );
12290 }
12391
12492 /** @inheritDoc */
12593 public function get_first_descendant_token ( ?int $ token_id = null ): ?WP_Parser_Token {
126- if ( $ this ->has_unmaterialized_native_ast () ) {
94+ if ( $ this ->has_native_ast () ) {
12795 return wp_sqlite_mysql_native_ast_get_first_descendant_token ( $ this ->native_ast , $ this ->native_node_index , $ token_id );
12896 }
12997 return parent ::get_first_descendant_token ( $ token_id );
13098 }
13199
132100 /** @inheritDoc */
133101 public function get_children (): array {
134- if ( $ this ->has_unmaterialized_native_ast () ) {
102+ if ( $ this ->has_native_ast () ) {
135103 return wp_sqlite_mysql_native_ast_get_children ( $ this ->native_ast , $ this ->native_node_index );
136104 }
137105 return parent ::get_children ();
138106 }
139107
140108 /** @inheritDoc */
141109 public function get_child_nodes ( ?string $ rule_name = null ): array {
142- if ( $ this ->has_unmaterialized_native_ast () ) {
110+ if ( $ this ->has_native_ast () ) {
143111 return wp_sqlite_mysql_native_ast_get_child_nodes ( $ this ->native_ast , $ this ->native_node_index , $ rule_name );
144112 }
145113 return parent ::get_child_nodes ( $ rule_name );
146114 }
147115
148116 /** @inheritDoc */
149117 public function get_child_tokens ( ?int $ token_id = null ): array {
150- if ( $ this ->has_unmaterialized_native_ast () ) {
118+ if ( $ this ->has_native_ast () ) {
151119 return wp_sqlite_mysql_native_ast_get_child_tokens ( $ this ->native_ast , $ this ->native_node_index , $ token_id );
152120 }
153121 return parent ::get_child_tokens ( $ token_id );
154122 }
155123
156124 /** @inheritDoc */
157125 public function get_descendants (): array {
158- if ( $ this ->has_unmaterialized_native_ast () ) {
126+ if ( $ this ->has_native_ast () ) {
159127 return wp_sqlite_mysql_native_ast_get_descendants ( $ this ->native_ast , $ this ->native_node_index );
160128 }
161129 return parent ::get_descendants ();
162130 }
163131
164132 /** @inheritDoc */
165133 public function get_descendant_nodes ( ?string $ rule_name = null ): array {
166- if ( $ this ->has_unmaterialized_native_ast () ) {
134+ if ( $ this ->has_native_ast () ) {
167135 return wp_sqlite_mysql_native_ast_get_descendant_nodes ( $ this ->native_ast , $ this ->native_node_index , $ rule_name );
168136 }
169137 return parent ::get_descendant_nodes ( $ rule_name );
170138 }
171139
172140 /** @inheritDoc */
173141 public function get_descendant_tokens ( ?int $ token_id = null ): array {
174- if ( $ this ->has_unmaterialized_native_ast () ) {
142+ if ( $ this ->has_native_ast () ) {
175143 return wp_sqlite_mysql_native_ast_get_descendant_tokens ( $ this ->native_ast , $ this ->native_node_index , $ token_id );
176144 }
177145 return parent ::get_descendant_tokens ( $ token_id );
178146 }
179147
180148 /** @inheritDoc */
181149 public function get_start (): int {
182- if ( $ this ->has_unmaterialized_native_ast () ) {
150+ if ( $ this ->has_native_ast () ) {
183151 return wp_sqlite_mysql_native_ast_get_start ( $ this ->native_ast , $ this ->native_node_index );
184152 }
185153 return parent ::get_start ();
186154 }
187155
188156 /** @inheritDoc */
189157 public function get_length (): int {
190- if ( $ this ->has_unmaterialized_native_ast () ) {
158+ if ( $ this ->has_native_ast () ) {
191159 return wp_sqlite_mysql_native_ast_get_length ( $ this ->native_ast , $ this ->native_node_index );
192160 }
193161 return parent ::get_length ();
194162 }
195163
196- /**
197- * Indicates whether this node still has an unmaterialized native AST.
198- *
199- * Returns true for freshly-parsed nodes whose children live in the
200- * Rust-owned AST buffer; returns false once the node has been mutated and
201- * its children copied into the inherited `$children` array (see
202- * self::materialize_native_children()).
203- *
204- * This is a per-instance state check, not a check for whether the native
205- * extension is loaded.
206- */
207- private function has_unmaterialized_native_ast (): bool {
164+ private function has_native_ast (): bool {
208165 return null !== $ this ->native_ast ;
209166 }
210167
211- /**
212- * Copies native children into the inherited PHP $children array and drops
213- * the native AST reference for this node.
214- *
215- * Called before any mutation (append_child, merge_fragment) so the node's
216- * authoritative state lives in PHP from that point on. After this runs,
217- * has_unmaterialized_native_ast() returns false and read methods fall
218- * through to the parent WP_Parser_Node implementation.
219- */
220168 private function materialize_native_children (): void {
221- if ( ! $ this ->has_unmaterialized_native_ast () ) {
169+ if ( ! $ this ->has_native_ast () ) {
222170 return ;
223171 }
224172
0 commit comments