1111 * children are never materialized into PHP arrays unless something actually
1212 * asks for them.
1313 *
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.
14+ * The hedge in those methods (`if ( $this->was_mutated() )`) is NOT a runtime
15+ * check for whether the native extension is loaded — if this class is in use,
16+ * the extension is loaded by definition. It checks whether THIS specific node
17+ * has been mutated from PHP. A node loses its native backing the first time
18+ * `append_child()` or `merge_fragment()` is called on it: those overrides
19+ * invoke `materialize_native_children()`, which copies the native children
20+ * into the inherited `$children` array and drops the native AST reference.
21+ * From that point on, the node is a plain PHP-backed `WP_Parser_Node` and the
22+ * read methods fall through to the parent implementation.
2423 *
2524 * Mutation from PHP is real and intentional — query rewriters in
2625 * `WP_PDO_MySQL_On_SQLite` (e.g. building synthetic `count(*)` expressions)
3130class WP_MySQL_Native_Parser_Node extends WP_Parser_Node {
3231 private $ native_ast = null ;
3332 private $ native_node_index = null ;
33+ private $ was_mutated = false ;
3434
3535 public function __construct ( $ rule_id , $ rule_name , $ native_ast = null , $ native_node_index = null ) {
3636 parent ::__construct ( $ rule_id , $ rule_name );
@@ -67,145 +67,145 @@ public function merge_fragment( $node ) {
6767
6868 /** @inheritDoc */
6969 public function has_child (): bool {
70- if ( $ this ->has_unmaterialized_native_ast () ) {
71- return wp_sqlite_mysql_native_ast_has_child ( $ this -> native_ast , $ this -> native_node_index );
70+ if ( $ this ->was_mutated () ) {
71+ return parent :: has_child ( );
7272 }
73- return parent :: has_child ( );
73+ return wp_sqlite_mysql_native_ast_has_child ( $ this -> native_ast , $ this -> native_node_index );
7474 }
7575
7676 /** @inheritDoc */
7777 public function has_child_node ( ?string $ rule_name = null ): bool {
78- if ( $ this ->has_unmaterialized_native_ast () ) {
79- return wp_sqlite_mysql_native_ast_has_child_node ( $ this -> native_ast , $ this -> native_node_index , $ rule_name );
78+ if ( $ this ->was_mutated () ) {
79+ return parent :: has_child_node ( $ rule_name );
8080 }
81- return parent :: has_child_node ( $ rule_name );
81+ return wp_sqlite_mysql_native_ast_has_child_node ( $ this -> native_ast , $ this -> native_node_index , $ rule_name );
8282 }
8383
8484 /** @inheritDoc */
8585 public function has_child_token ( ?int $ token_id = null ): bool {
86- if ( $ this ->has_unmaterialized_native_ast () ) {
87- return wp_sqlite_mysql_native_ast_has_child_token ( $ this -> native_ast , $ this -> native_node_index , $ token_id );
86+ if ( $ this ->was_mutated () ) {
87+ return parent :: has_child_token ( $ token_id );
8888 }
89- return parent :: has_child_token ( $ token_id );
89+ return wp_sqlite_mysql_native_ast_has_child_token ( $ this -> native_ast , $ this -> native_node_index , $ token_id );
9090 }
9191
9292 /** @inheritDoc */
9393 public function get_first_child () {
94- if ( $ this ->has_unmaterialized_native_ast () ) {
95- return wp_sqlite_mysql_native_ast_get_first_child ( $ this -> native_ast , $ this -> native_node_index );
94+ if ( $ this ->was_mutated () ) {
95+ return parent :: get_first_child ( );
9696 }
97- return parent :: get_first_child ( );
97+ return wp_sqlite_mysql_native_ast_get_first_child ( $ this -> native_ast , $ this -> native_node_index );
9898 }
9999
100100 /** @inheritDoc */
101101 public function get_first_child_node ( ?string $ rule_name = null ): ?WP_Parser_Node {
102- if ( $ this ->has_unmaterialized_native_ast () ) {
103- return wp_sqlite_mysql_native_ast_get_first_child_node ( $ this -> native_ast , $ this -> native_node_index , $ rule_name );
102+ if ( $ this ->was_mutated () ) {
103+ return parent :: get_first_child_node ( $ rule_name );
104104 }
105- return parent :: get_first_child_node ( $ rule_name );
105+ return wp_sqlite_mysql_native_ast_get_first_child_node ( $ this -> native_ast , $ this -> native_node_index , $ rule_name );
106106 }
107107
108108 /** @inheritDoc */
109109 public function get_first_child_token ( ?int $ token_id = null ): ?WP_Parser_Token {
110- if ( $ this ->has_unmaterialized_native_ast () ) {
111- return wp_sqlite_mysql_native_ast_get_first_child_token ( $ this -> native_ast , $ this -> native_node_index , $ token_id );
110+ if ( $ this ->was_mutated () ) {
111+ return parent :: get_first_child_token ( $ token_id );
112112 }
113- return parent :: get_first_child_token ( $ token_id );
113+ return wp_sqlite_mysql_native_ast_get_first_child_token ( $ this -> native_ast , $ this -> native_node_index , $ token_id );
114114 }
115115
116116 /** @inheritDoc */
117117 public function get_first_descendant_node ( ?string $ rule_name = null ): ?WP_Parser_Node {
118- if ( $ this ->has_unmaterialized_native_ast () ) {
119- return wp_sqlite_mysql_native_ast_get_first_descendant_node ( $ this -> native_ast , $ this -> native_node_index , $ rule_name );
118+ if ( $ this ->was_mutated () ) {
119+ return parent :: get_first_descendant_node ( $ rule_name );
120120 }
121- return parent :: get_first_descendant_node ( $ rule_name );
121+ return wp_sqlite_mysql_native_ast_get_first_descendant_node ( $ this -> native_ast , $ this -> native_node_index , $ rule_name );
122122 }
123123
124124 /** @inheritDoc */
125125 public function get_first_descendant_token ( ?int $ token_id = null ): ?WP_Parser_Token {
126- if ( $ this ->has_unmaterialized_native_ast () ) {
127- return wp_sqlite_mysql_native_ast_get_first_descendant_token ( $ this -> native_ast , $ this -> native_node_index , $ token_id );
126+ if ( $ this ->was_mutated () ) {
127+ return parent :: get_first_descendant_token ( $ token_id );
128128 }
129- return parent :: get_first_descendant_token ( $ token_id );
129+ return wp_sqlite_mysql_native_ast_get_first_descendant_token ( $ this -> native_ast , $ this -> native_node_index , $ token_id );
130130 }
131131
132132 /** @inheritDoc */
133133 public function get_children (): array {
134- if ( $ this ->has_unmaterialized_native_ast () ) {
135- return wp_sqlite_mysql_native_ast_get_children ( $ this -> native_ast , $ this -> native_node_index );
134+ if ( $ this ->was_mutated () ) {
135+ return parent :: get_children ( );
136136 }
137- return parent :: get_children ( );
137+ return wp_sqlite_mysql_native_ast_get_children ( $ this -> native_ast , $ this -> native_node_index );
138138 }
139139
140140 /** @inheritDoc */
141141 public function get_child_nodes ( ?string $ rule_name = null ): array {
142- if ( $ this ->has_unmaterialized_native_ast () ) {
143- return wp_sqlite_mysql_native_ast_get_child_nodes ( $ this -> native_ast , $ this -> native_node_index , $ rule_name );
142+ if ( $ this ->was_mutated () ) {
143+ return parent :: get_child_nodes ( $ rule_name );
144144 }
145- return parent :: get_child_nodes ( $ rule_name );
145+ return wp_sqlite_mysql_native_ast_get_child_nodes ( $ this -> native_ast , $ this -> native_node_index , $ rule_name );
146146 }
147147
148148 /** @inheritDoc */
149149 public function get_child_tokens ( ?int $ token_id = null ): array {
150- if ( $ this ->has_unmaterialized_native_ast () ) {
151- return wp_sqlite_mysql_native_ast_get_child_tokens ( $ this -> native_ast , $ this -> native_node_index , $ token_id );
150+ if ( $ this ->was_mutated () ) {
151+ return parent :: get_child_tokens ( $ token_id );
152152 }
153- return parent :: get_child_tokens ( $ token_id );
153+ return wp_sqlite_mysql_native_ast_get_child_tokens ( $ this -> native_ast , $ this -> native_node_index , $ token_id );
154154 }
155155
156156 /** @inheritDoc */
157157 public function get_descendants (): array {
158- if ( $ this ->has_unmaterialized_native_ast () ) {
159- return wp_sqlite_mysql_native_ast_get_descendants ( $ this -> native_ast , $ this -> native_node_index );
158+ if ( $ this ->was_mutated () ) {
159+ return parent :: get_descendants ( );
160160 }
161- return parent :: get_descendants ( );
161+ return wp_sqlite_mysql_native_ast_get_descendants ( $ this -> native_ast , $ this -> native_node_index );
162162 }
163163
164164 /** @inheritDoc */
165165 public function get_descendant_nodes ( ?string $ rule_name = null ): array {
166- if ( $ this ->has_unmaterialized_native_ast () ) {
167- return wp_sqlite_mysql_native_ast_get_descendant_nodes ( $ this -> native_ast , $ this -> native_node_index , $ rule_name );
166+ if ( $ this ->was_mutated () ) {
167+ return parent :: get_descendant_nodes ( $ rule_name );
168168 }
169- return parent :: get_descendant_nodes ( $ rule_name );
169+ return wp_sqlite_mysql_native_ast_get_descendant_nodes ( $ this -> native_ast , $ this -> native_node_index , $ rule_name );
170170 }
171171
172172 /** @inheritDoc */
173173 public function get_descendant_tokens ( ?int $ token_id = null ): array {
174- if ( $ this ->has_unmaterialized_native_ast () ) {
175- return wp_sqlite_mysql_native_ast_get_descendant_tokens ( $ this -> native_ast , $ this -> native_node_index , $ token_id );
174+ if ( $ this ->was_mutated () ) {
175+ return parent :: get_descendant_tokens ( $ token_id );
176176 }
177- return parent :: get_descendant_tokens ( $ token_id );
177+ return wp_sqlite_mysql_native_ast_get_descendant_tokens ( $ this -> native_ast , $ this -> native_node_index , $ token_id );
178178 }
179179
180180 /** @inheritDoc */
181181 public function get_start (): int {
182- if ( $ this ->has_unmaterialized_native_ast () ) {
183- return wp_sqlite_mysql_native_ast_get_start ( $ this -> native_ast , $ this -> native_node_index );
182+ if ( $ this ->was_mutated () ) {
183+ return parent :: get_start ( );
184184 }
185- return parent :: get_start ( );
185+ return wp_sqlite_mysql_native_ast_get_start ( $ this -> native_ast , $ this -> native_node_index );
186186 }
187187
188188 /** @inheritDoc */
189189 public function get_length (): int {
190- if ( $ this ->has_unmaterialized_native_ast () ) {
191- return wp_sqlite_mysql_native_ast_get_length ( $ this -> native_ast , $ this -> native_node_index );
190+ if ( $ this ->was_mutated () ) {
191+ return parent :: get_length ( );
192192 }
193- return parent :: get_length ( );
193+ return wp_sqlite_mysql_native_ast_get_length ( $ this -> native_ast , $ this -> native_node_index );
194194 }
195195
196196 /**
197- * Indicates whether this node still has an unmaterialized native AST .
197+ * Indicates whether this node has been mutated from PHP .
198198 *
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()) .
199+ * Returns false for freshly-parsed nodes whose children still live in the
200+ * Rust-owned AST buffer; returns true once `append_child()` or
201+ * `merge_fragment()` has copied the children into the inherited
202+ * `$children` array and dropped the native AST reference .
203203 *
204204 * This is a per-instance state check, not a check for whether the native
205205 * extension is loaded.
206206 */
207- private function has_unmaterialized_native_ast (): bool {
208- return null !== $ this ->native_ast ;
207+ private function was_mutated (): bool {
208+ return $ this ->was_mutated ;
209209 }
210210
211211 /**
@@ -214,16 +214,17 @@ private function has_unmaterialized_native_ast(): bool {
214214 *
215215 * Called before any mutation (append_child, merge_fragment) so the node's
216216 * 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.
217+ * was_mutated () returns true and read methods fall through to the parent
218+ * WP_Parser_Node implementation.
219219 */
220220 private function materialize_native_children (): void {
221- if ( ! $ this ->has_unmaterialized_native_ast () ) {
221+ if ( $ this ->was_mutated ) {
222222 return ;
223223 }
224224
225225 $ this ->children = wp_sqlite_mysql_native_ast_get_children ( $ this ->native_ast , $ this ->native_node_index );
226226 $ this ->native_ast = null ;
227227 $ this ->native_node_index = null ;
228+ $ this ->was_mutated = true ;
228229 }
229230}
0 commit comments