diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-07 20:51:59 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-07 20:51:59 +0900 |
| commit | 18a37a098157a98edbc8473e9c0d8dff3e8a88fa (patch) | |
| tree | 471682a4493fc8ddc1577a6f1e3c3d9c203ebf2c /docs | |
| parent | cd9e4a2b67cdea258e1daa2d9d830b7643bc19bb (diff) | |
| download | php-shirabe-18a37a098157a98edbc8473e9c0d8dff3e8a88fa.tar.gz php-shirabe-18a37a098157a98edbc8473e9c0d8dff3e8a88fa.tar.zst php-shirabe-18a37a098157a98edbc8473e9c0d8dff3e8a88fa.zip | |
fix(plugin): make proxy stub property access an explicit error
A proxy stub declares none of the real class's instance properties, and the
`__get`/`__set` forwarders were emitted only for classes that declare a public
one. Every other property access therefore got PHP's own answer for an
undeclared property — null on a read, a dynamic property on a write — so plugin
code reading state the entity holds ran on with null and failed somewhere else
entirely, or not at all.
Emit the forwarders on every root stub, add `__isset`/`__unset` alongside them
so `isset()` cannot answer false silently either, and serve all four from one
dispatcher that answers the state the Rust-side entity exposes and rejects every
other name.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/dev/plugin-stub-generation.md | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/docs/dev/plugin-stub-generation.md b/docs/dev/plugin-stub-generation.md index 27835921..9743534a 100644 --- a/docs/dev/plugin-stub-generation.md +++ b/docs/dev/plugin-stub-generation.md @@ -75,9 +75,12 @@ the generator's vendor directory or the classifier report is unavailable. materialized verbatim from the real source (they read no instance state and run locally in the worker), together with any non-public static helpers the methods call. -* **Public instance properties** are not declared on the stub; `__get`/`__set` - forwarders carry every access (including dynamic-property writes) to the - Rust side, where an unsupported name is an explicit error. +* **Instance properties** are not declared on the stub, whatever their + visibility: they are entity state. Every root stub instead carries + `__get`/`__set`/`__isset`/`__unset` forwarders, so each access reaches the + Rust side, where an unsupported name is an explicit error. They are emitted + unconditionally because PHP's own answer for an undeclared property — null on + a read, a dynamic property on a write, false on `isset()` — is silent. * **`__toString`** is forwarded like any other method. **`__clone`** is part of the boilerplate on every stub, whether or not the real class declares one: PHP has already copied the stub by the time it runs, so the copy asks the |
