aboutsummaryrefslogtreecommitdiffhomepage
path: root/scripts/plugin-stub-generator/src/Project.php
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-04 03:36:10 +0900
committernsfisis <nsfisis@gmail.com>2026-08-04 05:43:22 +0900
commit261516d5ce6f8b0d69cf9e3da7dd2f0ef1cdc36a (patch)
treef635a416745065b52ed0bb0ebb18154b9ef89f3b /scripts/plugin-stub-generator/src/Project.php
parent6cb1849473792bd73dbfb6265d363f149f687572 (diff)
downloadphp-shirabe-261516d5ce6f8b0d69cf9e3da7dd2f0ef1cdc36a.tar.gz
php-shirabe-261516d5ce6f8b0d69cf9e3da7dd2f0ef1cdc36a.tar.zst
php-shirabe-261516d5ce6f8b0d69cf9e3da7dd2f0ef1cdc36a.zip
feat(plugin): generate the worker proxy stubs from the Composer sources
Replace the hand-written proxy stubs under crates/shirabe-php-rpc/php/stubs with output of scripts/plugin-stub-generator, a deterministic emitter that derives every stub from the Composer checkout and the classifier report. Anything it cannot faithfully proxy (by-ref/variadic parameters, magic methods, public properties, diverging omitted overrides, stale stub files, a STUB_FILES entry missing in lib.rs) fails generation instead of degrading silently, so future Composer releases surface new members as explicit errors rather than silent gaps. Regenerating the stubs also normalizes the hand-written inconsistencies (uniform guarded constructors, import-based type spellings) and fixes real gaps the review of the generated diff uncovered: the BaseIO authentication methods now carry the real class's untyped signatures, and the previously missing ConsoleIO::sanitize is materialized together with its private static helper. A cargo test runs generate-stubs --check to keep the committed stubs, the generator and the embedded list from drifting apart. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'scripts/plugin-stub-generator/src/Project.php')
-rw-r--r--scripts/plugin-stub-generator/src/Project.php59
1 files changed, 59 insertions, 0 deletions
diff --git a/scripts/plugin-stub-generator/src/Project.php b/scripts/plugin-stub-generator/src/Project.php
new file mode 100644
index 00000000..13f1067d
--- /dev/null
+++ b/scripts/plugin-stub-generator/src/Project.php
@@ -0,0 +1,59 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Shirabe\PluginStubGenerator;
+
+use PhpParser\Parser;
+use PhpParser\ParserFactory;
+
+/**
+ * Locates and parses classes of the Composer checkout (composer/src and composer/vendor),
+ * using the checkout's own PSR-4 autoload map.
+ */
+final class Project
+{
+ private Parser $parser;
+
+ /** @var array<string, list<string>> namespace prefix => base dirs, longest prefix first */
+ private array $psr4;
+
+ /** @var array<string, SourceFile> */
+ private array $cache = [];
+
+ public function __construct(string $composerRoot)
+ {
+ $this->parser = (new ParserFactory())->createForNewestSupportedVersion();
+
+ $mapFile = $composerRoot . '/vendor/composer/autoload_psr4.php';
+ if (!is_file($mapFile)) {
+ throw new GenerationError(["missing $mapFile (run composer install in the checkout)"]);
+ }
+ $map = require $mapFile;
+ $map['Composer\\'] ??= [$composerRoot . '/src/Composer'];
+ uksort($map, static fn (string $a, string $b): int => strlen($b) <=> strlen($a));
+ $this->psr4 = $map;
+ }
+
+ public function sourceFor(string $fqcn): SourceFile
+ {
+ return $this->cache[$fqcn] ??= new SourceFile($this->fileFor($fqcn), $fqcn, $this->parser);
+ }
+
+ private function fileFor(string $fqcn): string
+ {
+ foreach ($this->psr4 as $prefix => $dirs) {
+ if (!str_starts_with($fqcn, $prefix)) {
+ continue;
+ }
+ $relative = str_replace('\\', '/', substr($fqcn, strlen($prefix))) . '.php';
+ foreach ($dirs as $dir) {
+ $path = $dir . '/' . $relative;
+ if (is_file($path)) {
+ return $path;
+ }
+ }
+ }
+ throw new GenerationError(["cannot locate a source file for $fqcn"]);
+ }
+}