Skip to content

Commit cba475a

Browse files
authored
Clean up native parser follow-ups (#394)
## What it does Cleans up the native parser follow-up from #381 so the merged code reads as permanent code, not review scaffolding. It replaces duplicated inline verifier logic with one `tests/tools/verify-native-parser-extension.php` entry point for `mysql-on-sqlite`. The parser-extension workflow and PHPUnit bootstrap both call the same verifier: ```bash php -d extension=../php-ext-wp-mysql-parser/target/debug/libwp_mysql_parser.so \ tests/tools/verify-native-parser-extension.php ``` It also collapses `WP_PDO_MySQL_On_SQLite::create_parser()` to one token selection and one parser reset/create return, and rewrites native parser test comments to describe behavior instead of PR review history. ## Rationale #381 landed functional native parser support, but a few follow-up surfaces still carried review-era wording and copied verifier blocks. That makes future changes harder to read and easier to drift: native parser routing, Rust AST handle storage, wrapper identity, and materialized child behavior were being checked in multiple places. The verifier now pins that runtime contract from one script: extension loaded, `WP_MySQL_Lexer` resolves native, `WP_MySQL_Parser` delegates to `WP_MySQL_Native_Parser`, the SQLite driver returns a native-backed AST, native wrapper handle properties are absent, child identity is stable, and materialized child mutations survive. ## Implementation Added `wp_sqlite_verify_native_parser_extension()` with a shared delegate check: ```php function wp_sqlite_assert_native_parser_delegate( WP_MySQL_Parser $parser, string $context ): void { $reflection = new ReflectionObject( $parser ); if ( ! $reflection->hasProperty( 'native' ) ) { wp_sqlite_native_parser_verification_fail( $context ); } $native_property = $reflection->getProperty( 'native' ); $native_property->setAccessible( true ); if ( ! ( $native_property->getValue( $parser ) instanceof WP_MySQL_Native_Parser ) ) { wp_sqlite_native_parser_verification_fail( $context ); } } ``` `WP_SQLITE_REQUIRE_NATIVE_PARSER_EXTENSION=1` in the PHPUnit bootstrap now loads that verifier instead of inlining the same checks. `create_parser()` now selects tokens once: ```php $tokens = $lexer instanceof WP_MySQL_Native_Lexer ? $lexer->native_token_stream() : $lexer->remaining_tokens(); return $this->reset_or_create_parser( $tokens ); ``` The WordPress PHPUnit extension setup keeps its container-specific verifier, but factors the repeated reflection checks into the same small helper shape. ## Testing instructions ```bash cargo fmt --check bash -n .github/workflows/wp-tests-phpunit-native-extension-setup.sh node --check .github/workflows/wp-tests-phpunit-run.js php -l packages/mysql-on-sqlite/tests/tools/verify-native-parser-extension.php php -l packages/mysql-on-sqlite/tests/bootstrap.php php ./vendor/bin/phpcs .github/workflows/wp-tests-phpunit-native-extension-setup.sh packages/mysql-on-sqlite/tests/bootstrap.php packages/mysql-on-sqlite/tests/tools/verify-native-parser-extension.php packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php packages/mysql-on-sqlite/tests/mysql/native/WP_MySQL_Native_Parser_Node_Identity_Tests.php packages/mysql-on-sqlite/tests/mysql/native/WP_MySQL_Parser_Instanceof_Tests.php cd packages/mysql-on-sqlite php -d extension=../php-ext-wp-mysql-parser/target/debug/libwp_mysql_parser.so tests/tools/verify-native-parser-extension.php php ./vendor/bin/phpunit -c ./phpunit.xml.dist tests/mysql/native/WP_MySQL_Parser_Instanceof_Tests.php tests/mysql/native/WP_MySQL_Native_Parser_Node_Identity_Tests.php tests/mysql/native/WP_MySQL_Native_Parser_Node_Cycle_Tests.php WP_SQLITE_REQUIRE_NATIVE_PARSER_EXTENSION=1 php -d extension=../php-ext-wp-mysql-parser/target/debug/libwp_mysql_parser.so ./vendor/bin/phpunit -c ./phpunit.xml.dist --filter 'WP_MySQL_(Native_Parser_Node_(Identity|Cycle)|Parser_Instanceof)_Tests' php ./vendor/bin/phpunit -c ./phpunit.xml.dist tests/WP_SQLite_Driver_Query_Tests.php WP_SQLITE_REQUIRE_NATIVE_PARSER_EXTENSION=1 php -d extension=../php-ext-wp-mysql-parser/target/debug/libwp_mysql_parser.so ./vendor/bin/phpunit -c ./phpunit.xml.dist tests/WP_SQLite_Driver_Query_Tests.php ``` CI is passing on `3f4153f`, including the PHP 8.0-8.5 Rust-extension matrix and `WordPress PHPUnit Tests / Rust extension`.
1 parent c43113d commit cba475a

9 files changed

Lines changed: 152 additions & 222 deletions

.github/workflows/mysql-parser-extension-tests.yml

Lines changed: 2 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -165,91 +165,11 @@ jobs:
165165
run: cargo build
166166
working-directory: packages/php-ext-wp-mysql-parser
167167

168-
- name: Run native parser smoke tests
168+
- name: Verify native parser extension
169169
if: matrix.native
170-
run: |
171-
php -d extension="$GITHUB_WORKSPACE/packages/php-ext-wp-mysql-parser/target/debug/libwp_mysql_parser.so" -r '
172-
require "src/load.php";
173-
$lexer = new WP_MySQL_Lexer( "SELECT ID, post_title FROM wp_posts WHERE ID IN (1, 2, 3)" );
174-
if ( ! ( $lexer instanceof WP_MySQL_Native_Lexer ) ) {
175-
fwrite( STDERR, "Native lexer is not available.\n" );
176-
exit( 1 );
177-
}
178-
$tokens = $lexer->native_token_stream();
179-
$rules = include "src/mysql/mysql-grammar.php";
180-
$grammar = new WP_Parser_Grammar( $rules );
181-
$parser = new WP_MySQL_Parser( $grammar, $tokens );
182-
$parser_reflection = new ReflectionObject( $parser );
183-
if ( ! $parser_reflection->hasProperty( "native" ) ) {
184-
fwrite( STDERR, "WP_MySQL_Parser did not select the native parser delegate.\n" );
185-
exit( 1 );
186-
}
187-
$native_property = $parser_reflection->getProperty( "native" );
188-
$native_property->setAccessible( true );
189-
if ( ! ( $native_property->getValue( $parser ) instanceof WP_MySQL_Native_Parser ) ) {
190-
fwrite( STDERR, "WP_MySQL_Parser did not select the native parser delegate.\n" );
191-
exit( 1 );
192-
}
193-
$ast = $parser->parse();
194-
if ( ! $ast instanceof WP_MySQL_Native_Parser_Node || "query" !== $ast->rule_name ) {
195-
fwrite( STDERR, "Native parser did not produce the expected query AST.\n" );
196-
exit( 1 );
197-
}
198-
'
170+
run: php -d extension="$GITHUB_WORKSPACE/packages/php-ext-wp-mysql-parser/target/debug/libwp_mysql_parser.so" tests/tools/verify-native-parser-extension.php
199171
working-directory: packages/mysql-on-sqlite
200172

201-
- name: Verify SQLite driver selects the native parser path
202-
if: matrix.native
203-
run: |
204-
php -d extension="$GITHUB_WORKSPACE/packages/php-ext-wp-mysql-parser/target/debug/libwp_mysql_parser.so" -r '
205-
require "packages/mysql-on-sqlite/src/load.php";
206-
$lexer = new WP_MySQL_Lexer( "SELECT 1" );
207-
if ( ! ( $lexer instanceof WP_MySQL_Native_Lexer ) ) {
208-
fwrite( STDERR, "Native lexer is not available.\n" );
209-
exit( 1 );
210-
}
211-
$driver = new WP_PDO_MySQL_On_SQLite( "mysql-on-sqlite:path=:memory:;dbname=wp;" );
212-
$parser = $driver->create_parser( "SELECT 1" );
213-
$parser_reflection = new ReflectionObject( $parser );
214-
if ( ! $parser_reflection->hasProperty( "native" ) ) {
215-
fwrite( STDERR, "SQLite driver did not create a native parser delegate.\n" );
216-
exit( 1 );
217-
}
218-
$native_property = $parser_reflection->getProperty( "native" );
219-
$native_property->setAccessible( true );
220-
if ( ! ( $native_property->getValue( $parser ) instanceof WP_MySQL_Native_Parser ) ) {
221-
fwrite( STDERR, "SQLite driver did not create a native parser delegate.\n" );
222-
exit( 1 );
223-
}
224-
$parser->next_query();
225-
$ast = $parser->get_query_ast();
226-
if ( ! ( $ast instanceof WP_MySQL_Native_Parser_Node ) ) {
227-
fwrite( STDERR, "SQLite driver did not return a native-backed AST.\n" );
228-
exit( 1 );
229-
}
230-
$reflection = new ReflectionObject( $ast );
231-
if ( $reflection->hasProperty( "native_ast" ) || $reflection->hasProperty( "native_node_index" ) ) {
232-
fwrite( STDERR, "Native wrapper still stores Rust AST handle properties.\n" );
233-
exit( 1 );
234-
}
235-
$first = $ast->get_first_child_node();
236-
if ( ! ( $first instanceof WP_MySQL_Native_Parser_Node ) ) {
237-
fwrite( STDERR, "Native wrapper did not return a native-backed child node.\n" );
238-
exit( 1 );
239-
}
240-
if ( $first !== $ast->get_first_child_node() ) {
241-
fwrite( STDERR, "Native wrapper identity is not stable across reads.\n" );
242-
exit( 1 );
243-
}
244-
$synthetic = new WP_Parser_Node( 0, "synthetic" );
245-
$first->append_child( $synthetic );
246-
$same_first = $ast->get_first_child_node();
247-
if ( $same_first !== $first || ! in_array( $synthetic, $same_first->get_children(), true ) ) {
248-
fwrite( STDERR, "Materialized native wrapper was lost from the parent cache.\n" );
249-
exit( 1 );
250-
}
251-
'
252-
253173
- name: Run full PHPUnit suite with parser extension
254174
if: matrix.native
255175
env:

.github/workflows/wp-tests-phpunit-native-extension-setup.sh

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -115,78 +115,69 @@ cat > "$WP_DIR/native-verify-extension.php" <<'EOF'
115115
<?php
116116
require_once '/var/www/src/wp-content/plugins/sqlite-database-integration/wp-includes/database/load.php';
117117
118+
function wp_sqlite_native_parser_verification_fail( string $message ): void {
119+
fwrite( STDERR, $message . "\n" );
120+
exit( 1 );
121+
}
122+
123+
function wp_sqlite_assert_native_parser_delegate( WP_MySQL_Parser $parser, string $message ): void {
124+
$parser_reflection = new ReflectionObject( $parser );
125+
if ( ! $parser_reflection->hasProperty( 'native' ) ) {
126+
wp_sqlite_native_parser_verification_fail( $message );
127+
}
128+
129+
$native_property = $parser_reflection->getProperty( 'native' );
130+
$native_property->setAccessible( true );
131+
if ( ! ( $native_property->getValue( $parser ) instanceof WP_MySQL_Native_Parser ) ) {
132+
wp_sqlite_native_parser_verification_fail( $message );
133+
}
134+
}
135+
118136
$lexer = new WP_MySQL_Lexer( 'SELECT 1' );
119137
if ( ! ( $lexer instanceof WP_MySQL_Native_Lexer ) ) {
120-
fwrite( STDERR, "Native lexer is not available in the WordPress PHP test container.\n" );
121-
exit( 1 );
138+
wp_sqlite_native_parser_verification_fail( 'Native lexer is not available in the WordPress PHP test container.' );
122139
}
123140
124141
$tokens = $lexer->native_token_stream();
125142
$rules = include '/var/www/src/wp-content/plugins/sqlite-database-integration/wp-includes/database/mysql/mysql-grammar.php';
126143
$grammar = new WP_Parser_Grammar( $rules );
127144
$parser = new WP_MySQL_Parser( $grammar, $tokens );
128-
$parser_reflection = new ReflectionObject( $parser );
129-
if ( ! $parser_reflection->hasProperty( 'native' ) ) {
130-
fwrite( STDERR, "WordPress PHP test container did not select the native parser delegate.\n" );
131-
exit( 1 );
132-
}
133-
$native_property = $parser_reflection->getProperty( 'native' );
134-
$native_property->setAccessible( true );
135-
if ( ! ( $native_property->getValue( $parser ) instanceof WP_MySQL_Native_Parser ) ) {
136-
fwrite( STDERR, "WordPress PHP test container did not select the native parser delegate.\n" );
137-
exit( 1 );
138-
}
145+
wp_sqlite_assert_native_parser_delegate( $parser, 'WordPress PHP test container did not select the native parser delegate.' );
139146
140147
$parser_ast = $parser->parse();
141148
if ( ! ( $parser_ast instanceof WP_MySQL_Native_Parser_Node ) ) {
142-
fwrite( STDERR, "Native parser did not produce a native-backed AST in the WordPress PHP test container.\n" );
143-
exit( 1 );
149+
wp_sqlite_native_parser_verification_fail( 'Native parser did not produce a native-backed AST in the WordPress PHP test container.' );
144150
}
145151
146152
$driver = new WP_PDO_MySQL_On_SQLite( 'mysql-on-sqlite:path=:memory:;dbname=wp;' );
147153
$parser = $driver->create_parser( 'SELECT 1' );
148-
$parser_reflection = new ReflectionObject( $parser );
149-
if ( ! $parser_reflection->hasProperty( 'native' ) ) {
150-
fwrite( STDERR, "WordPress PHP test container SQLite driver did not create a native parser delegate.\n" );
151-
exit( 1 );
152-
}
153-
$native_property = $parser_reflection->getProperty( 'native' );
154-
$native_property->setAccessible( true );
155-
if ( ! ( $native_property->getValue( $parser ) instanceof WP_MySQL_Native_Parser ) ) {
156-
fwrite( STDERR, "WordPress PHP test container SQLite driver did not create a native parser delegate.\n" );
157-
exit( 1 );
158-
}
154+
wp_sqlite_assert_native_parser_delegate( $parser, 'WordPress PHP test container SQLite driver did not create a native parser delegate.' );
159155
$parser->next_query();
160156
$ast = $parser->get_query_ast();
161157
162158
if ( ! ( $ast instanceof WP_MySQL_Native_Parser_Node ) ) {
163-
fwrite( STDERR, "WordPress PHP test container did not select the native-backed AST.\n" );
164-
exit( 1 );
159+
wp_sqlite_native_parser_verification_fail( 'WordPress PHP test container did not select the native-backed AST.' );
165160
}
166161
167162
$reflection = new ReflectionObject( $ast );
168163
if ( $reflection->hasProperty( 'native_ast' ) || $reflection->hasProperty( 'native_node_index' ) ) {
169-
fwrite( STDERR, "Native wrapper still stores Rust AST handle properties.\n" );
170-
exit( 1 );
164+
wp_sqlite_native_parser_verification_fail( 'Native wrapper still stores Rust AST handle properties.' );
171165
}
172166
173167
$first = $ast->get_first_child_node();
174168
if ( ! ( $first instanceof WP_MySQL_Native_Parser_Node ) ) {
175-
fwrite( STDERR, "Native wrapper did not return a native-backed child node.\n" );
176-
exit( 1 );
169+
wp_sqlite_native_parser_verification_fail( 'Native wrapper did not return a native-backed child node.' );
177170
}
178171
179172
if ( $first !== $ast->get_first_child_node() ) {
180-
fwrite( STDERR, "Native wrapper identity is not stable across reads.\n" );
181-
exit( 1 );
173+
wp_sqlite_native_parser_verification_fail( 'Native wrapper identity is not stable across reads.' );
182174
}
183175
184176
$synthetic = new WP_Parser_Node( 0, 'synthetic' );
185177
$first->append_child( $synthetic );
186178
$same_first = $ast->get_first_child_node();
187179
if ( $same_first !== $first || ! in_array( $synthetic, $same_first->get_children(), true ) ) {
188-
fwrite( STDERR, "Materialized native wrapper was lost from the parent cache.\n" );
189-
exit( 1 );
180+
wp_sqlite_native_parser_verification_fail( 'Materialized native wrapper was lost from the parent cache.' );
190181
}
191182
EOF
192183

@@ -197,7 +188,7 @@ const file = process.argv[2];
197188
const marker = "require_once ABSPATH . 'wp-settings.php';";
198189
const guard = [
199190
'/*',
200-
' * Native parser CI guard. This file is generated by the SQLite integration workflow.',
191+
' * Native parser extension guard. This file is generated by the SQLite integration workflow.',
201192
' */',
202193
"require_once dirname( __DIR__, 3 ) . '/native-verify-extension.php';",
203194
].join( '\n' );

.github/workflows/wp-tests-phpunit-run.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
* Wrap the "composer run wp-tests-php" command to process tests
33
* that are expected to error and fail at the moment.
44
*
5-
* This makes sure that the CI job passes, while explicitly tracking
6-
* the issues that need to be addressed. Ideally, over time this script
7-
* will become obsolete when all errors and failures are resolved.
5+
* Unexpected errors/failures still fail the workflow. Expected failures that
6+
* stop happening are reported so this allowlist can be reduced over time.
87
*/
98
const { execSync } = require( 'child_process' );
109
const fs = require( 'fs' );

packages/mysql-on-sqlite/src/sqlite/class-wp-pdo-mysql-on-sqlite.php

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1160,17 +1160,14 @@ public function get_insert_id() {
11601160
* @return WP_MySQL_Parser A parser initialized for the MySQL query.
11611161
*/
11621162
public function create_parser( string $query ): WP_MySQL_Parser {
1163-
$lexer = new WP_MySQL_Lexer(
1163+
$lexer = new WP_MySQL_Lexer(
11641164
$query,
11651165
80038,
11661166
$this->active_sql_modes
11671167
);
1168-
if ( $lexer instanceof WP_MySQL_Native_Lexer ) {
1169-
$tokens = $lexer->native_token_stream();
1170-
return $this->reset_or_create_parser( $tokens );
1171-
}
1172-
1173-
$tokens = $lexer->remaining_tokens();
1168+
$tokens = $lexer instanceof WP_MySQL_Native_Lexer
1169+
? $lexer->native_token_stream()
1170+
: $lexer->remaining_tokens();
11741171
return $this->reset_or_create_parser( $tokens );
11751172
}
11761173

packages/mysql-on-sqlite/tests/bootstrap.php

Lines changed: 1 addition & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -10,82 +10,7 @@
1010
}
1111

1212
if ( '1' === getenv( 'WP_SQLITE_REQUIRE_NATIVE_PARSER_EXTENSION' ) ) {
13-
if ( ! class_exists( 'WP_MySQL_Native_Lexer', false ) || ! class_exists( 'WP_MySQL_Native_Parser', false ) ) {
14-
fwrite( STDERR, "Native MySQL parser extension is required for this PHPUnit run.\n" );
15-
exit( 1 );
16-
}
17-
18-
$native_parser_lexer = new WP_MySQL_Lexer( 'SELECT 1' );
19-
if ( ! ( $native_parser_lexer instanceof WP_MySQL_Native_Lexer ) ) {
20-
fwrite( STDERR, "WP_MySQL_Lexer did not resolve to the native implementation.\n" );
21-
exit( 1 );
22-
}
23-
24-
$native_parser_tokens = $native_parser_lexer->native_token_stream();
25-
$native_parser_rules = include __DIR__ . '/../src/mysql/mysql-grammar.php';
26-
$native_parser_grammar = new WP_Parser_Grammar( $native_parser_rules );
27-
$native_parser = new WP_MySQL_Parser( $native_parser_grammar, $native_parser_tokens );
28-
$native_parser_reflection = new ReflectionObject( $native_parser );
29-
if ( ! $native_parser_reflection->hasProperty( 'native' ) ) {
30-
fwrite( STDERR, "WP_MySQL_Parser did not create a native parser delegate.\n" );
31-
exit( 1 );
32-
}
33-
$native_parser_property = $native_parser_reflection->getProperty( 'native' );
34-
$native_parser_property->setAccessible( true );
35-
if ( ! ( $native_parser_property->getValue( $native_parser ) instanceof WP_MySQL_Native_Parser ) ) {
36-
fwrite( STDERR, "WP_MySQL_Parser did not create a native parser delegate.\n" );
37-
exit( 1 );
38-
}
39-
40-
$native_parser_ast = $native_parser->parse();
41-
if ( ! ( $native_parser_ast instanceof WP_MySQL_Native_Parser_Node ) ) {
42-
fwrite( STDERR, "Native parser did not produce a native-backed AST.\n" );
43-
exit( 1 );
44-
}
45-
46-
$native_parser_driver = new WP_PDO_MySQL_On_SQLite( 'mysql-on-sqlite:path=:memory:;dbname=wp;' );
47-
$native_parser_driver_parser = $native_parser_driver->create_parser( 'SELECT 1' );
48-
$native_parser_driver_reflection = new ReflectionObject( $native_parser_driver_parser );
49-
if ( ! $native_parser_driver_reflection->hasProperty( 'native' ) ) {
50-
fwrite( STDERR, "WP_PDO_MySQL_On_SQLite did not create a native parser delegate.\n" );
51-
exit( 1 );
52-
}
53-
$native_parser_driver_property = $native_parser_driver_reflection->getProperty( 'native' );
54-
$native_parser_driver_property->setAccessible( true );
55-
if ( ! ( $native_parser_driver_property->getValue( $native_parser_driver_parser ) instanceof WP_MySQL_Native_Parser ) ) {
56-
fwrite( STDERR, "WP_PDO_MySQL_On_SQLite did not create a native parser delegate.\n" );
57-
exit( 1 );
58-
}
59-
60-
$native_parser_driver_parser->next_query();
61-
$native_parser_driver_ast = $native_parser_driver_parser->get_query_ast();
62-
if ( ! ( $native_parser_driver_ast instanceof WP_MySQL_Native_Parser_Node ) ) {
63-
fwrite( STDERR, "WP_PDO_MySQL_On_SQLite did not produce a native-backed AST.\n" );
64-
exit( 1 );
65-
}
66-
67-
$native_parser_driver_child = $native_parser_driver_ast->get_first_child_node();
68-
if ( ! ( $native_parser_driver_child instanceof WP_MySQL_Native_Parser_Node ) ) {
69-
fwrite( STDERR, "WP_PDO_MySQL_On_SQLite did not produce native-backed child AST nodes.\n" );
70-
exit( 1 );
71-
}
72-
73-
unset(
74-
$native_parser_ast,
75-
$native_parser,
76-
$native_parser_grammar,
77-
$native_parser_rules,
78-
$native_parser_tokens,
79-
$native_parser_lexer,
80-
$native_parser_driver,
81-
$native_parser_driver_parser,
82-
$native_parser_reflection,
83-
$native_parser_property,
84-
$native_parser_driver_reflection,
85-
$native_parser_driver_property,
86-
$native_parser_driver_ast,
87-
$native_parser_driver_child
88-
);
13+
require_once __DIR__ . '/tools/verify-native-parser-extension.php';
8914
}
9015

9116
// Configure the test environment.

packages/mysql-on-sqlite/tests/mysql/native/WP_MySQL_Native_Parser_Node_Identity_Tests.php

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
/**
66
* Regression tests for the per-AST identity map on native parser nodes.
77
*
8-
* The native extension constructs a fresh PHP wrapper for every accessor
9-
* call. Without interning, two reads of the same logical node would yield
10-
* distinct objects, and a mutation made through a still-live wrapper would be
11-
* invisible through the second. WP_Parser_Node exposes public mutators and
12-
* stable child identity, so the native wrapper must preserve both.
8+
* The native extension materializes PHP wrappers from Rust-owned arena nodes.
9+
* Without interning, two reads of the same logical node would yield distinct
10+
* objects, and a mutation made through a still-live wrapper would be invisible
11+
* through the second. WP_Parser_Node exposes public mutators and stable child
12+
* identity, so the native wrapper must preserve both.
1313
*
14-
* Skipped when the native extension is not loaded the pure-PHP code
14+
* Skipped when the native extension is not loaded; the pure-PHP code
1515
* path already has stable identity by construction.
1616
*/
1717
class WP_MySQL_Native_Parser_Node_Identity_Tests extends TestCase {
@@ -96,11 +96,9 @@ public function test_mutation_on_child_survives_re_read(): void {
9696
$child = $tree->get_first_child_node();
9797
$this->assertNotNull( $child );
9898

99-
// Mutate via the public WP_Parser_Node API — this is exactly the
100-
// kind of state the reviewer worried would be lost when accessors
101-
// hand back fresh wrappers. rule_name is a declared public property
102-
// that the parser itself sets, so PHP 8.2's dynamic-property
103-
// deprecation does not apply here.
99+
// Mutate via the public WP_Parser_Node API. This catches regressions
100+
// where accessors hand back fresh wrappers and lose state written
101+
// through a previously returned child.
104102
$child->rule_name = 'mutated-rule';
105103

106104
$same_child = $tree->get_first_child_node();

packages/mysql-on-sqlite/tests/mysql/native/WP_MySQL_Parser_Instanceof_Tests.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
/**
66
* `WP_MySQL_Parser instanceof WP_Parser` must hold in both modes.
77
*
8-
* Pre-PR, the native-mode `WP_MySQL_Parser` was `extends WP_MySQL_Native_Parser`
9-
* (a Rust-registered class with no `WP_Parser` in its chain), so existing
10-
* downstream code doing `if ($parser instanceof WP_Parser)` silently
11-
* skipped the parser when the extension was loaded. This test pins the
12-
* contract for both modes.
8+
* The native-mode `WP_MySQL_Parser` must not expose the Rust-registered
9+
* parser directly. Existing downstream code may rely on
10+
* `if ($parser instanceof WP_Parser)`, so this test pins the contract for
11+
* both modes.
1312
*/
1413
class WP_MySQL_Parser_Instanceof_Tests extends TestCase {
1514

0 commit comments

Comments
 (0)