aboutsummaryrefslogtreecommitdiffhomepage
path: root/scripts/plugin-stub-generator/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-07 20:51:59 +0900
committernsfisis <nsfisis@gmail.com>2026-08-07 20:51:59 +0900
commit18a37a098157a98edbc8473e9c0d8dff3e8a88fa (patch)
tree471682a4493fc8ddc1577a6f1e3c3d9c203ebf2c /scripts/plugin-stub-generator/src
parentcd9e4a2b67cdea258e1daa2d9d830b7643bc19bb (diff)
downloadphp-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 'scripts/plugin-stub-generator/src')
-rw-r--r--scripts/plugin-stub-generator/src/Generator.php29
1 files changed, 14 insertions, 15 deletions
diff --git a/scripts/plugin-stub-generator/src/Generator.php b/scripts/plugin-stub-generator/src/Generator.php
index 395155dd..12967787 100644
--- a/scripts/plugin-stub-generator/src/Generator.php
+++ b/scripts/plugin-stub-generator/src/Generator.php
@@ -12,8 +12,7 @@ use PhpParser\Node\Stmt\Interface_;
/**
* Emits the proxy stub files deterministically from the Composer sources and the classifier
* report. Anything the emitter cannot faithfully proxy (by-ref or variadic parameters,
- * magic methods, public properties, non-public constants) fails generation instead of
- * degrading silently.
+ * magic methods, non-public constants) fails generation instead of degrading silently.
*/
final class Generator
{
@@ -59,7 +58,6 @@ final class Generator
PHP;
private const PROPERTY_FORWARDERS = <<<'PHP'
- /** The real class declares public properties; every access forwards to the entity. */
public function __get($name)
{
return \ShirabeRpcRuntime::callRust($this->__rhandle, '__get', [$name]);
@@ -69,6 +67,16 @@ final class Generator
{
\ShirabeRpcRuntime::callRust($this->__rhandle, '__set', [$name, $value]);
}
+
+ public function __isset($name): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, '__isset', [$name]);
+ }
+
+ public function __unset($name): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, '__unset', [$name]);
+ }
PHP;
private Project $project;
@@ -202,20 +210,13 @@ final class Generator
$isRoot = true;
}
- $hasPublicInstanceProperties = false;
+ // Instance properties are entity state, so the stub declares none of them.
$staticProperties = [];
foreach ($class->getProperties() as $property) {
- if (!$property->isPublic()) {
- continue;
- }
- if ($property->isStatic()) {
+ if ($property->isPublic() && $property->isStatic()) {
// A public static property reads no instance state; its real declaration is
// materialized so it lives locally in the worker, like static methods.
$staticProperties[] = $file->verbatim($property->getStartLine(), $property->getEndLine());
- } else {
- // Instance properties are entity state: the stub declares none and lets the
- // __get/__set forwarders below carry every access to the Rust side.
- $hasPublicInstanceProperties = true;
}
}
@@ -331,6 +332,7 @@ final class Generator
$members = [];
if ($isRoot) {
$members[] = self::BOILERPLATE;
+ $members[] = self::PROPERTY_FORWARDERS;
}
if ($isRoot || $constructor !== null) {
$members[] = $this->renderConstructor($fqcn, $constructor, $file);
@@ -341,9 +343,6 @@ final class Generator
if ($staticProperties !== []) {
$members[] = implode("\n", $staticProperties);
}
- if ($hasPublicInstanceProperties) {
- $members[] = self::PROPERTY_FORWARDERS;
- }
$members = array_merge($members, $staticMethods, $methodTexts);
$body = implode("\n\n", $members);