33/**
44 * Parser node backed by a native (Rust) AST.
55 *
6- * Constructed by the native MySQL parser extension. Read methods delegate
7- * into the Rust-owned AST so children are never copied into PHP unless a
8- * caller actually walks the tree. On the first mutation (append_child or
9- * merge_fragment), the node materializes its children into the inherited
10- * `$children` array and behaves like a plain WP_Parser_Node from then on.
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->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.
23+ *
24+ * Mutation from PHP is real and intentional — query rewriters in
25+ * `WP_PDO_MySQL_On_SQLite` (e.g. building synthetic `count(*)` expressions)
26+ * call `append_child()` on parsed nodes. The lazy-then-materialize design
27+ * keeps the fast path (read-only traversal) cheap while still allowing
28+ * mutation when callers need it.
1129 */
1230class WP_MySQL_Native_Parser_Node extends WP_Parser_Node {
1331 private $ native_ast = null ;
@@ -21,13 +39,24 @@ public function __construct( $rule_id, $rule_name, $native_ast = null, $native_n
2139 $ this ->native_node_index = $ native_node_index ;
2240 }
2341
24- /** @inheritDoc */
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+ */
2549 public function append_child ( $ node ) {
2650 $ this ->materialize_native_children ();
2751 parent ::append_child ( $ node );
2852 }
2953
30- /** @inheritDoc */
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+ */
3160 public function merge_fragment ( $ node ) {
3261 $ this ->materialize_native_children ();
3362 if ( $ node instanceof self ) {
@@ -164,10 +193,30 @@ public function get_length(): int {
164193 return wp_sqlite_mysql_native_ast_get_length ( $ this ->native_ast , $ this ->native_node_index );
165194 }
166195
196+ /**
197+ * Indicates whether this node has been mutated from PHP.
198+ *
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.
203+ *
204+ * This is a per-instance state check, not a check for whether the native
205+ * extension is loaded.
206+ */
167207 private function was_mutated (): bool {
168208 return $ this ->was_mutated ;
169209 }
170210
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+ * was_mutated() returns true and read methods fall through to the parent
218+ * WP_Parser_Node implementation.
219+ */
171220 private function materialize_native_children (): void {
172221 if ( $ this ->was_mutated ) {
173222 return ;
0 commit comments