From 765b7749d31675d3cbf541b146ead1aafd6d6ae9 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Tue, 21 Jul 2026 02:54:18 +0900 Subject: feat(plugin-class-classifier): add deterministic plugin-boundary classifier Decides, for every composer/composer class, how it is treated at the plugin boundary (rust-proxy / rust-snapshot / contract / two-world / php-native / unsupported) so that upstream updates re-classify new or rewritten classes without re-deriving the design by hand. Rules and category definitions live in docs/dev/plugin-class-classification.md; the tool (PHP + nikic/PHP-Parser) implements them as a reachability closure with direction marks, per-method pure/mutator analysis, and a leaf-first fixed point for unreachable classes, with three small versioned exception lists. Co-Authored-By: Claude Fable 5 --- scripts/plugin-class-classifier/src/MethodInfo.php | 71 ++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 scripts/plugin-class-classifier/src/MethodInfo.php (limited to 'scripts/plugin-class-classifier/src/MethodInfo.php') diff --git a/scripts/plugin-class-classifier/src/MethodInfo.php b/scripts/plugin-class-classifier/src/MethodInfo.php new file mode 100644 index 00000000..a03c2126 --- /dev/null +++ b/scripts/plugin-class-classifier/src/MethodInfo.php @@ -0,0 +1,71 @@ +m(), self::m(), + * static::m(), parent::m()) with a literal name. Purity propagates + * through these; thisArgs lists 0-based argument positions holding + * $this-rooted expressions (checked against by-ref parameters). + * + * @var list}> + */ + public array $selfCalls = []; + + /** + * Calls on receivers whose class could be resolved statically (typed + * property or typed parameter) that pass $this-rooted expressions. + * Checked against the callee's by-ref parameter positions once the whole + * symbol table is available. + * + * @var list}> + */ + public array $externalCalls = []; + + /** + * True when the body contains a call whose signature cannot be resolved + * statically (dynamic method name, variable function, call_user_func, + * closure) with a $this-rooted expression (or $this itself) as argument, + * or passes a $this-rooted expression into a by-ref parameter position. + */ + public bool $thisEscapesUnresolved = false; + + public function __construct( + public readonly string $name, + /** 'public' | 'protected' | 'private' */ + public readonly string $visibility, + public readonly bool $static, + public readonly bool $abstract, + /** @var list */ + public readonly array $params, + /** Class-like FQCNs in the native return type. @var list */ + public readonly array $returnClassTypes, + /** Native return type is array/iterable/mixed/object or absent. */ + public readonly bool $returnExpandable, + /** Class-like FQCNs from the `@return` docblock. @var list */ + public readonly array $docblockReturnTypes, + /** Class-like FQCNs from `@throws` docblocks. @var list */ + public readonly array $docblockThrowsTypes, + ) { + } + + /** @return list positions of by-ref parameters */ + public function byRefParamPositions(): array + { + $positions = []; + foreach ($this->params as $i => $param) { + if ($param->byRef) { + $positions[] = $i; + } + } + + return $positions; + } +} -- cgit v1.3.1