aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-04 05:09:04 +0900
committernsfisis <nsfisis@gmail.com>2026-08-04 05:43:32 +0900
commit80631930a6ca7b64d96a89de61c8b63f302161a8 (patch)
treedc5acf4a8dbe74b576ec7826206defa0e3f8fba1
parent850b67c048bbf80d9739520d950f4dd1ed4190a5 (diff)
downloadphp-shirabe-80631930a6ca7b64d96a89de61c8b63f302161a8.tar.gz
php-shirabe-80631930a6ca7b64d96a89de61c8b63f302161a8.tar.zst
php-shirabe-80631930a6ca7b64d96a89de61c8b63f302161a8.zip
feat(plugin): generate proxy stubs for the package and repository graph
Emit the eleven stubs a subscriber plugin's object-graph calls reach (BasePackage through RootPackage, the installed-repository hierarchy, RepositoryManager, InstallationManager). The generator learns the member kinds these classes need: builtin interfaces contribute no closure entries, public static properties are materialized, public instance properties forward through __get/__set, __toString forwards like a plain method, __clone throws (proxy clone semantics are still an open design question), and a subclass stub may add interfaces to its inherited surface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Installer/InstallationManager.php137
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Package/BasePackage.php388
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Package/CompletePackage.php134
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Package/Package.php114
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Package/RootPackage.php71
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Repository/ArrayRepository.php99
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Repository/FilesystemRepository.php34
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Repository/InstalledArrayRepository.php14
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Repository/InstalledFilesystemRepository.php14
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Repository/RepositoryManager.php89
-rw-r--r--crates/shirabe-php-rpc/php/stubs/Composer/Repository/WritableArrayRepository.php41
-rw-r--r--crates/shirabe-php-rpc/src/lib.rs44
-rw-r--r--docs/dev/plugin-stub-generation.md26
-rw-r--r--scripts/plugin-stub-generator/src/Generator.php83
-rw-r--r--scripts/plugin-stub-generator/targets.list11
15 files changed, 1285 insertions, 14 deletions
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Installer/InstallationManager.php b/crates/shirabe-php-rpc/php/stubs/Composer/Installer/InstallationManager.php
new file mode 100644
index 00000000..35682561
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Installer/InstallationManager.php
@@ -0,0 +1,137 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Installer\InstallationManager: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Installer;
+
+use Composer\IO\IOInterface;
+use Composer\Package\PackageInterface;
+use Composer\Repository\InstalledRepositoryInterface;
+use Composer\DependencyResolver\Operation\InstallOperation;
+use Composer\DependencyResolver\Operation\UpdateOperation;
+use Composer\DependencyResolver\Operation\UninstallOperation;
+use Composer\DependencyResolver\Operation\MarkAliasInstalledOperation;
+use Composer\DependencyResolver\Operation\MarkAliasUninstalledOperation;
+use React\Promise\PromiseInterface;
+
+class InstallationManager implements \ShirabeRustStub
+{
+ /** @var int */
+ protected $__rhandle;
+ /** @var int */
+ protected $__epoch;
+
+ public function __construct(int $rhandle = 0, int $epoch = 0)
+ {
+ if (func_num_args() < 2) {
+ // Constructing the class from plugin code (a common idiom for e.g. `new BufferIO()`)
+ // is an open question of the plugin design; only proxy instantiation passes a
+ // Rust handle. Fail with a diagnosable message instead of an ArgumentCountError.
+ throw new \RuntimeException(
+ 'Shirabe does not support constructing ' . static::class . ' inside the plugin process yet'
+ );
+ }
+ $this->__rhandle = $rhandle;
+ $this->__epoch = $epoch;
+ }
+
+ public function __destruct()
+ {
+ \ShirabeRustObjectRegistry::release($this->__rhandle);
+ }
+
+ public function __shirabeRustHandleDescriptor(): array
+ {
+ return [
+ '__rhandle' => $this->__rhandle,
+ '__class' => static::class,
+ '__epoch' => $this->__epoch,
+ ];
+ }
+
+ public function reset(): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'reset', []);
+ }
+
+ public function addInstaller(InstallerInterface $installer): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'addInstaller', [$installer]);
+ }
+
+ public function removeInstaller(InstallerInterface $installer): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'removeInstaller', [$installer]);
+ }
+
+ public function disablePlugins(): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'disablePlugins', []);
+ }
+
+ public function getInstaller(string $type): InstallerInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getInstaller', [$type]);
+ }
+
+ public function isPackageInstalled(InstalledRepositoryInterface $repo, PackageInterface $package): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'isPackageInstalled', [$repo, $package]);
+ }
+
+ public function ensureBinariesPresence(PackageInterface $package): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'ensureBinariesPresence', [$package]);
+ }
+
+ public function execute(InstalledRepositoryInterface $repo, array $operations, bool $devMode = true, bool $runScripts = true, bool $downloadOnly = false): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'execute', [$repo, $operations, $devMode, $runScripts, $downloadOnly]);
+ }
+
+ public function download(PackageInterface $package): ?PromiseInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'download', [$package]);
+ }
+
+ public function install(InstalledRepositoryInterface $repo, InstallOperation $operation): ?PromiseInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'install', [$repo, $operation]);
+ }
+
+ public function update(InstalledRepositoryInterface $repo, UpdateOperation $operation): ?PromiseInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'update', [$repo, $operation]);
+ }
+
+ public function uninstall(InstalledRepositoryInterface $repo, UninstallOperation $operation): ?PromiseInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'uninstall', [$repo, $operation]);
+ }
+
+ public function markAliasInstalled(InstalledRepositoryInterface $repo, MarkAliasInstalledOperation $operation): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'markAliasInstalled', [$repo, $operation]);
+ }
+
+ public function markAliasUninstalled(InstalledRepositoryInterface $repo, MarkAliasUninstalledOperation $operation): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'markAliasUninstalled', [$repo, $operation]);
+ }
+
+ public function getInstallPath(PackageInterface $package): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getInstallPath', [$package]);
+ }
+
+ public function setOutputProgress(bool $outputProgress): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setOutputProgress', [$outputProgress]);
+ }
+
+ public function notifyInstalls(IOInterface $io): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'notifyInstalls', [$io]);
+ }
+}
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Package/BasePackage.php b/crates/shirabe-php-rpc/php/stubs/Composer/Package/BasePackage.php
new file mode 100644
index 00000000..e55c3fb5
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Package/BasePackage.php
@@ -0,0 +1,388 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Package\BasePackage: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Package;
+
+use Composer\Repository\RepositoryInterface;
+
+abstract class BasePackage implements PackageInterface, \ShirabeRustStub
+{
+ /** @var int */
+ protected $__rhandle;
+ /** @var int */
+ protected $__epoch;
+
+ public function __construct(int $rhandle = 0, int $epoch = 0)
+ {
+ if (func_num_args() < 2) {
+ // Constructing the class from plugin code (a common idiom for e.g. `new BufferIO()`)
+ // is an open question of the plugin design; only proxy instantiation passes a
+ // Rust handle. Fail with a diagnosable message instead of an ArgumentCountError.
+ throw new \RuntimeException(
+ 'Shirabe does not support constructing ' . static::class . ' inside the plugin process yet'
+ );
+ }
+ $this->__rhandle = $rhandle;
+ $this->__epoch = $epoch;
+ }
+
+ public function __destruct()
+ {
+ \ShirabeRustObjectRegistry::release($this->__rhandle);
+ }
+
+ public function __shirabeRustHandleDescriptor(): array
+ {
+ return [
+ '__rhandle' => $this->__rhandle,
+ '__class' => static::class,
+ '__epoch' => $this->__epoch,
+ ];
+ }
+
+ public const STABILITY_STABLE = 0;
+ public const STABILITY_RC = 5;
+ public const STABILITY_BETA = 10;
+ public const STABILITY_ALPHA = 15;
+ public const STABILITY_DEV = 20;
+ public const STABILITIES = [
+ 'stable' => self::STABILITY_STABLE,
+ 'RC' => self::STABILITY_RC,
+ 'beta' => self::STABILITY_BETA,
+ 'alpha' => self::STABILITY_ALPHA,
+ 'dev' => self::STABILITY_DEV,
+ ];
+
+ public static $supportedLinkTypes = [
+ 'require' => ['description' => 'requires', 'method' => Link::TYPE_REQUIRE],
+ 'conflict' => ['description' => 'conflicts', 'method' => Link::TYPE_CONFLICT],
+ 'provide' => ['description' => 'provides', 'method' => Link::TYPE_PROVIDE],
+ 'replace' => ['description' => 'replaces', 'method' => Link::TYPE_REPLACE],
+ 'require-dev' => ['description' => 'requires (for development)', 'method' => Link::TYPE_DEV_REQUIRE],
+ ];
+ public static $stabilities = self::STABILITIES;
+
+ /** The real class declares public properties; every access forwards to the entity. */
+ public function __get($name)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, '__get', [$name]);
+ }
+
+ public function __set($name, $value): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, '__set', [$name, $value]);
+ }
+
+ public function __clone()
+ {
+ // Cloning a proxy is an open design question; fail instead of silently sharing
+ // the Rust-side entity between two stub instances.
+ throw new \RuntimeException('Shirabe does not support cloning ' . static::class . ' inside the plugin process yet');
+ }
+
+ public static function packageNameToRegexp(string $allowPattern, string $wrap = '{^%s$}i'): string
+ {
+ $cleanedAllowPattern = str_replace('\\*', '.*', preg_quote($allowPattern));
+
+ return sprintf($wrap, $cleanedAllowPattern);
+ }
+
+ public static function packageNamesToRegexp(array $packageNames, string $wrap = '{^(?:%s)$}iD'): string
+ {
+ $packageNames = array_map(
+ static function ($packageName): string {
+ return BasePackage::packageNameToRegexp($packageName, '%s');
+ },
+ $packageNames
+ );
+
+ return sprintf($wrap, implode('|', $packageNames));
+ }
+
+ public function getName(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getName', []);
+ }
+
+ public function getPrettyName(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getPrettyName', []);
+ }
+
+ public function getNames($provides = true): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getNames', [$provides]);
+ }
+
+ public function setId(int $id): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setId', [$id]);
+ }
+
+ public function getId(): int
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getId', []);
+ }
+
+ public function isDev(): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'isDev', []);
+ }
+
+ public function getType(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getType', []);
+ }
+
+ public function getTargetDir(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getTargetDir', []);
+ }
+
+ public function getExtra(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getExtra', []);
+ }
+
+ public function setInstallationSource(?string $type): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setInstallationSource', [$type]);
+ }
+
+ public function getInstallationSource(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getInstallationSource', []);
+ }
+
+ public function getSourceType(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getSourceType', []);
+ }
+
+ public function getSourceUrl(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getSourceUrl', []);
+ }
+
+ public function getSourceUrls(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getSourceUrls', []);
+ }
+
+ public function getSourceReference(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getSourceReference', []);
+ }
+
+ public function getSourceMirrors(): ?array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getSourceMirrors', []);
+ }
+
+ public function setSourceMirrors(?array $mirrors): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setSourceMirrors', [$mirrors]);
+ }
+
+ public function getDistType(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getDistType', []);
+ }
+
+ public function getDistUrl(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getDistUrl', []);
+ }
+
+ public function getDistUrls(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getDistUrls', []);
+ }
+
+ public function getDistReference(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getDistReference', []);
+ }
+
+ public function getDistSha1Checksum(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getDistSha1Checksum', []);
+ }
+
+ public function getDistMirrors(): ?array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getDistMirrors', []);
+ }
+
+ public function setDistMirrors(?array $mirrors): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setDistMirrors', [$mirrors]);
+ }
+
+ public function getVersion(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getVersion', []);
+ }
+
+ public function getPrettyVersion(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getPrettyVersion', []);
+ }
+
+ public function getFullPrettyVersion(bool $truncate = true, int $displayMode = PackageInterface::DISPLAY_SOURCE_REF_IF_DEV): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getFullPrettyVersion', [$truncate, $displayMode]);
+ }
+
+ public function getReleaseDate(): ?\DateTimeInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getReleaseDate', []);
+ }
+
+ public function getStability(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getStability', []);
+ }
+
+ public function getRequires(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getRequires', []);
+ }
+
+ public function getConflicts(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getConflicts', []);
+ }
+
+ public function getProvides(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getProvides', []);
+ }
+
+ public function getReplaces(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getReplaces', []);
+ }
+
+ public function getDevRequires(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getDevRequires', []);
+ }
+
+ public function getSuggests(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getSuggests', []);
+ }
+
+ public function getAutoload(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getAutoload', []);
+ }
+
+ public function getDevAutoload(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getDevAutoload', []);
+ }
+
+ public function getIncludePaths(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getIncludePaths', []);
+ }
+
+ public function getPhpExt(): ?array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getPhpExt', []);
+ }
+
+ public function setRepository(RepositoryInterface $repository): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setRepository', [$repository]);
+ }
+
+ public function getRepository(): ?RepositoryInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getRepository', []);
+ }
+
+ public function getBinaries(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getBinaries', []);
+ }
+
+ public function getUniqueName(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getUniqueName', []);
+ }
+
+ public function getNotificationUrl(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getNotificationUrl', []);
+ }
+
+ public function __toString(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, '__toString', []);
+ }
+
+ public function getPrettyString(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getPrettyString', []);
+ }
+
+ public function isDefaultBranch(): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'isDefaultBranch', []);
+ }
+
+ public function getTransportOptions(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getTransportOptions', []);
+ }
+
+ public function setTransportOptions(array $options): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setTransportOptions', [$options]);
+ }
+
+ public function setSourceReference(?string $reference): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setSourceReference', [$reference]);
+ }
+
+ public function setDistUrl(?string $url): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setDistUrl', [$url]);
+ }
+
+ public function setDistType(?string $type): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setDistType', [$type]);
+ }
+
+ public function setDistReference(?string $reference): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setDistReference', [$reference]);
+ }
+
+ public function setSourceDistReferences(string $reference): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setSourceDistReferences', [$reference]);
+ }
+
+ public function isPlatform(): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'isPlatform', []);
+ }
+
+ public function equals(PackageInterface $package): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'equals', [$package]);
+ }
+
+ public function getStabilityPriority(): int
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getStabilityPriority', []);
+ }
+}
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Package/CompletePackage.php b/crates/shirabe-php-rpc/php/stubs/Composer/Package/CompletePackage.php
new file mode 100644
index 00000000..588084b0
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Package/CompletePackage.php
@@ -0,0 +1,134 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Package\CompletePackage: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Package;
+
+class CompletePackage extends Package implements CompletePackageInterface
+{
+ public function getScripts(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getScripts', []);
+ }
+
+ public function setScripts(array $scripts): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setScripts', [$scripts]);
+ }
+
+ public function getRepositories(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getRepositories', []);
+ }
+
+ public function setRepositories(array $repositories): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setRepositories', [$repositories]);
+ }
+
+ public function getLicense(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getLicense', []);
+ }
+
+ public function setLicense(array $license): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setLicense', [$license]);
+ }
+
+ public function getKeywords(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getKeywords', []);
+ }
+
+ public function setKeywords(array $keywords): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setKeywords', [$keywords]);
+ }
+
+ public function getDescription(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getDescription', []);
+ }
+
+ public function setDescription(?string $description): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setDescription', [$description]);
+ }
+
+ public function getHomepage(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getHomepage', []);
+ }
+
+ public function setHomepage(?string $homepage): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setHomepage', [$homepage]);
+ }
+
+ public function getAuthors(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getAuthors', []);
+ }
+
+ public function setAuthors(array $authors): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setAuthors', [$authors]);
+ }
+
+ public function getSupport(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getSupport', []);
+ }
+
+ public function setSupport(array $support): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setSupport', [$support]);
+ }
+
+ public function getFunding(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getFunding', []);
+ }
+
+ public function setFunding(array $funding): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setFunding', [$funding]);
+ }
+
+ public function isAbandoned(): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'isAbandoned', []);
+ }
+
+ public function getReplacementPackage(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getReplacementPackage', []);
+ }
+
+ public function setAbandoned($abandoned): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setAbandoned', [$abandoned]);
+ }
+
+ public function getArchiveName(): ?string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getArchiveName', []);
+ }
+
+ public function setArchiveName(?string $name): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setArchiveName', [$name]);
+ }
+
+ public function getArchiveExcludes(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getArchiveExcludes', []);
+ }
+
+ public function setArchiveExcludes(array $excludes): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setArchiveExcludes', [$excludes]);
+ }
+}
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Package/Package.php b/crates/shirabe-php-rpc/php/stubs/Composer/Package/Package.php
new file mode 100644
index 00000000..5b80060d
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Package/Package.php
@@ -0,0 +1,114 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Package\Package: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Package;
+
+class Package extends BasePackage
+{
+ public function setType(string $type): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setType', [$type]);
+ }
+
+ public function setTargetDir(?string $targetDir): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setTargetDir', [$targetDir]);
+ }
+
+ public function setExtra(array $extra): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setExtra', [$extra]);
+ }
+
+ public function setBinaries(array $binaries): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setBinaries', [$binaries]);
+ }
+
+ public function setSourceType(?string $type): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setSourceType', [$type]);
+ }
+
+ public function setSourceUrl(?string $url): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setSourceUrl', [$url]);
+ }
+
+ public function setDistSha1Checksum(?string $sha1checksum): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setDistSha1Checksum', [$sha1checksum]);
+ }
+
+ public function setReleaseDate(?\DateTimeInterface $releaseDate): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setReleaseDate', [$releaseDate]);
+ }
+
+ public function setRequires(array $requires): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setRequires', [$requires]);
+ }
+
+ public function setConflicts(array $conflicts): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setConflicts', [$conflicts]);
+ }
+
+ public function setProvides(array $provides): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setProvides', [$provides]);
+ }
+
+ public function setReplaces(array $replaces): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setReplaces', [$replaces]);
+ }
+
+ public function setDevRequires(array $devRequires): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setDevRequires', [$devRequires]);
+ }
+
+ public function setSuggests(array $suggests): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setSuggests', [$suggests]);
+ }
+
+ public function setAutoload(array $autoload): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setAutoload', [$autoload]);
+ }
+
+ public function setDevAutoload(array $devAutoload): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setDevAutoload', [$devAutoload]);
+ }
+
+ public function setIncludePaths(array $includePaths): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setIncludePaths', [$includePaths]);
+ }
+
+ public function setPhpExt(?array $phpExt): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setPhpExt', [$phpExt]);
+ }
+
+ public function setNotificationUrl(string $notificationUrl): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setNotificationUrl', [$notificationUrl]);
+ }
+
+ public function setIsDefaultBranch(bool $defaultBranch): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setIsDefaultBranch', [$defaultBranch]);
+ }
+
+ public function replaceVersion(string $version, string $prettyVersion): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'replaceVersion', [$version, $prettyVersion]);
+ }
+}
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Package/RootPackage.php b/crates/shirabe-php-rpc/php/stubs/Composer/Package/RootPackage.php
new file mode 100644
index 00000000..7584787d
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Package/RootPackage.php
@@ -0,0 +1,71 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Package\RootPackage: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Package;
+
+class RootPackage extends CompletePackage implements RootPackageInterface
+{
+ public const DEFAULT_PRETTY_VERSION = '1.0.0+no-version-set';
+
+ public function getAliases(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getAliases', []);
+ }
+
+ public function getMinimumStability(): string
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getMinimumStability', []);
+ }
+
+ public function getStabilityFlags(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getStabilityFlags', []);
+ }
+
+ public function getReferences(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getReferences', []);
+ }
+
+ public function getPreferStable(): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getPreferStable', []);
+ }
+
+ public function getConfig(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getConfig', []);
+ }
+
+ public function setStabilityFlags(array $stabilityFlags): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setStabilityFlags', [$stabilityFlags]);
+ }
+
+ public function setMinimumStability(string $minimumStability): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setMinimumStability', [$minimumStability]);
+ }
+
+ public function setPreferStable(bool $preferStable): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setPreferStable', [$preferStable]);
+ }
+
+ public function setConfig(array $config): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setConfig', [$config]);
+ }
+
+ public function setReferences(array $references): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setReferences', [$references]);
+ }
+
+ public function setAliases(array $aliases): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setAliases', [$aliases]);
+ }
+}
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Repository/ArrayRepository.php b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/ArrayRepository.php
new file mode 100644
index 00000000..33454fa2
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/ArrayRepository.php
@@ -0,0 +1,99 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Repository\ArrayRepository: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Repository;
+
+use Composer\Package\PackageInterface;
+
+class ArrayRepository implements RepositoryInterface, \ShirabeRustStub
+{
+ /** @var int */
+ protected $__rhandle;
+ /** @var int */
+ protected $__epoch;
+
+ public function __construct(int $rhandle = 0, int $epoch = 0)
+ {
+ if (func_num_args() < 2) {
+ // Constructing the class from plugin code (a common idiom for e.g. `new BufferIO()`)
+ // is an open question of the plugin design; only proxy instantiation passes a
+ // Rust handle. Fail with a diagnosable message instead of an ArgumentCountError.
+ throw new \RuntimeException(
+ 'Shirabe does not support constructing ' . static::class . ' inside the plugin process yet'
+ );
+ }
+ $this->__rhandle = $rhandle;
+ $this->__epoch = $epoch;
+ }
+
+ public function __destruct()
+ {
+ \ShirabeRustObjectRegistry::release($this->__rhandle);
+ }
+
+ public function __shirabeRustHandleDescriptor(): array
+ {
+ return [
+ '__rhandle' => $this->__rhandle,
+ '__class' => static::class,
+ '__epoch' => $this->__epoch,
+ ];
+ }
+
+ public function hasPackage(PackageInterface $package)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'hasPackage', [$package]);
+ }
+
+ public function findPackage(string $name, $constraint)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'findPackage', [$name, $constraint]);
+ }
+
+ public function findPackages(string $name, $constraint = null)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'findPackages', [$name, $constraint]);
+ }
+
+ public function getPackages()
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getPackages', []);
+ }
+
+ public function loadPackages(array $packageNameMap, array $acceptableStabilities, array $stabilityFlags, array $alreadyLoaded = [])
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'loadPackages', [$packageNameMap, $acceptableStabilities, $stabilityFlags, $alreadyLoaded]);
+ }
+
+ public function search(string $query, int $mode = 0, ?string $type = null)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'search', [$query, $mode, $type]);
+ }
+
+ public function getProviders(string $packageName)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getProviders', [$packageName]);
+ }
+
+ public function getRepoName()
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getRepoName', []);
+ }
+
+ public function addPackage(PackageInterface $package)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'addPackage', [$package]);
+ }
+
+ public function removePackage(PackageInterface $package)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'removePackage', [$package]);
+ }
+
+ public function count(): int
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'count', []);
+ }
+}
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Repository/FilesystemRepository.php b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/FilesystemRepository.php
new file mode 100644
index 00000000..00e42ac6
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/FilesystemRepository.php
@@ -0,0 +1,34 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Repository\FilesystemRepository: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Repository;
+
+use Composer\Pcre\Preg;
+
+class FilesystemRepository extends WritableArrayRepository
+{
+ public static function safelyLoadInstalledVersions(string $path): bool
+ {
+ $installedVersionsData = @file_get_contents($path);
+ $pattern = <<<'REGEX'
+{(?(DEFINE)
+ (?<number> -? \s*+ \d++ (?:\.\d++)? )
+ (?<boolean> true | false | null )
+ (?<strings> (?&string) (?: \s*+ \. \s*+ (?&string))*+ )
+ (?<string> (?: " (?:[^"\\$]*+ | \\ ["\\0] )* " | ' (?:[^'\\]*+ | \\ ['\\] )* ' ) )
+ (?<array> array\( \s*+ (?: (?:(?&number)|(?&strings)) \s*+ => \s*+ (?: (?:__DIR__ \s*+ \. \s*+)? (?&strings) | (?&value) ) \s*+, \s*+ )*+ \s*+ \) )
+ (?<value> (?: (?&number) | (?&boolean) | (?&strings) | (?&array) ) )
+)
+^<\?php\s++return\s++(?&array)\s*+;$}ix
+REGEX;
+ if (is_string($installedVersionsData) && Preg::isMatch($pattern, trim($installedVersionsData))) {
+ \Composer\InstalledVersions::reload(eval('?>'.Preg::replace('{=>\s*+__DIR__\s*+\.\s*+([\'"])}', '=> '.var_export(dirname($path), true).' . $1', $installedVersionsData)));
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Repository/InstalledArrayRepository.php b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/InstalledArrayRepository.php
new file mode 100644
index 00000000..6cee4b45
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/InstalledArrayRepository.php
@@ -0,0 +1,14 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Repository\InstalledArrayRepository: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Repository;
+
+class InstalledArrayRepository extends WritableArrayRepository implements InstalledRepositoryInterface
+{
+ public function isFresh(): bool
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'isFresh', []);
+ }
+}
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Repository/InstalledFilesystemRepository.php b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/InstalledFilesystemRepository.php
new file mode 100644
index 00000000..f4fb9e2b
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/InstalledFilesystemRepository.php
@@ -0,0 +1,14 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Repository\InstalledFilesystemRepository: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Repository;
+
+class InstalledFilesystemRepository extends FilesystemRepository implements InstalledRepositoryInterface
+{
+ public function isFresh()
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'isFresh', []);
+ }
+}
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Repository/RepositoryManager.php b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/RepositoryManager.php
new file mode 100644
index 00000000..252bab4d
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/RepositoryManager.php
@@ -0,0 +1,89 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Repository\RepositoryManager: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Repository;
+
+use Composer\Package\PackageInterface;
+
+class RepositoryManager implements \ShirabeRustStub
+{
+ /** @var int */
+ protected $__rhandle;
+ /** @var int */
+ protected $__epoch;
+
+ public function __construct(int $rhandle = 0, int $epoch = 0)
+ {
+ if (func_num_args() < 2) {
+ // Constructing the class from plugin code (a common idiom for e.g. `new BufferIO()`)
+ // is an open question of the plugin design; only proxy instantiation passes a
+ // Rust handle. Fail with a diagnosable message instead of an ArgumentCountError.
+ throw new \RuntimeException(
+ 'Shirabe does not support constructing ' . static::class . ' inside the plugin process yet'
+ );
+ }
+ $this->__rhandle = $rhandle;
+ $this->__epoch = $epoch;
+ }
+
+ public function __destruct()
+ {
+ \ShirabeRustObjectRegistry::release($this->__rhandle);
+ }
+
+ public function __shirabeRustHandleDescriptor(): array
+ {
+ return [
+ '__rhandle' => $this->__rhandle,
+ '__class' => static::class,
+ '__epoch' => $this->__epoch,
+ ];
+ }
+
+ public function findPackage(string $name, $constraint): ?PackageInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'findPackage', [$name, $constraint]);
+ }
+
+ public function findPackages(string $name, $constraint): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'findPackages', [$name, $constraint]);
+ }
+
+ public function addRepository(RepositoryInterface $repository): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'addRepository', [$repository]);
+ }
+
+ public function prependRepository(RepositoryInterface $repository): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'prependRepository', [$repository]);
+ }
+
+ public function createRepository(string $type, array $config, ?string $name = null): RepositoryInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'createRepository', [$type, $config, $name]);
+ }
+
+ public function setRepositoryClass(string $type, $class): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setRepositoryClass', [$type, $class]);
+ }
+
+ public function getRepositories(): array
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getRepositories', []);
+ }
+
+ public function setLocalRepository(InstalledRepositoryInterface $repository): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, 'setLocalRepository', [$repository]);
+ }
+
+ public function getLocalRepository(): InstalledRepositoryInterface
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getLocalRepository', []);
+ }
+}
diff --git a/crates/shirabe-php-rpc/php/stubs/Composer/Repository/WritableArrayRepository.php b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/WritableArrayRepository.php
new file mode 100644
index 00000000..8f2b8377
--- /dev/null
+++ b/crates/shirabe-php-rpc/php/stubs/Composer/Repository/WritableArrayRepository.php
@@ -0,0 +1,41 @@
+<?php
+
+// Generated by scripts/plugin-stub-generator; do not edit by hand.
+// Proxy stub for Composer\Repository\WritableArrayRepository: the public surface forwards to the Rust-side entity over RPC.
+
+namespace Composer\Repository;
+
+use Composer\Installer\InstallationManager;
+
+class WritableArrayRepository extends ArrayRepository implements WritableRepositoryInterface
+{
+ public function write(bool $devMode, InstallationManager $installationManager)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'write', [$devMode, $installationManager]);
+ }
+
+ public function getCanonicalPackages()
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getCanonicalPackages', []);
+ }
+
+ public function reload()
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'reload', []);
+ }
+
+ public function setDevPackageNames(array $devPackageNames)
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'setDevPackageNames', [$devPackageNames]);
+ }
+
+ public function getDevPackageNames()
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getDevPackageNames', []);
+ }
+
+ public function getDevMode()
+ {
+ return \ShirabeRpcRuntime::callRust($this->__rhandle, 'getDevMode', []);
+ }
+}
diff --git a/crates/shirabe-php-rpc/src/lib.rs b/crates/shirabe-php-rpc/src/lib.rs
index e41f289f..ba5443d2 100644
--- a/crates/shirabe-php-rpc/src/lib.rs
+++ b/crates/shirabe-php-rpc/src/lib.rs
@@ -594,6 +594,50 @@ const STUB_FILES: &[(&str, &str)] = &[
"Composer/IO/NullIO.php",
include_str!("../php/stubs/Composer/IO/NullIO.php"),
),
+ (
+ "Composer/Package/BasePackage.php",
+ include_str!("../php/stubs/Composer/Package/BasePackage.php"),
+ ),
+ (
+ "Composer/Package/Package.php",
+ include_str!("../php/stubs/Composer/Package/Package.php"),
+ ),
+ (
+ "Composer/Package/CompletePackage.php",
+ include_str!("../php/stubs/Composer/Package/CompletePackage.php"),
+ ),
+ (
+ "Composer/Package/RootPackage.php",
+ include_str!("../php/stubs/Composer/Package/RootPackage.php"),
+ ),
+ (
+ "Composer/Repository/ArrayRepository.php",
+ include_str!("../php/stubs/Composer/Repository/ArrayRepository.php"),
+ ),
+ (
+ "Composer/Repository/WritableArrayRepository.php",
+ include_str!("../php/stubs/Composer/Repository/WritableArrayRepository.php"),
+ ),
+ (
+ "Composer/Repository/InstalledArrayRepository.php",
+ include_str!("../php/stubs/Composer/Repository/InstalledArrayRepository.php"),
+ ),
+ (
+ "Composer/Repository/FilesystemRepository.php",
+ include_str!("../php/stubs/Composer/Repository/FilesystemRepository.php"),
+ ),
+ (
+ "Composer/Repository/InstalledFilesystemRepository.php",
+ include_str!("../php/stubs/Composer/Repository/InstalledFilesystemRepository.php"),
+ ),
+ (
+ "Composer/Repository/RepositoryManager.php",
+ include_str!("../php/stubs/Composer/Repository/RepositoryManager.php"),
+ ),
+ (
+ "Composer/Installer/InstallationManager.php",
+ include_str!("../php/stubs/Composer/Installer/InstallationManager.php"),
+ ),
];
struct Worker {
diff --git a/docs/dev/plugin-stub-generation.md b/docs/dev/plugin-stub-generation.md
index 1fb77ff4..09bb5625 100644
--- a/docs/dev/plugin-stub-generation.md
+++ b/docs/dev/plugin-stub-generation.md
@@ -51,14 +51,24 @@ the generator's vendor directory or the classifier report is unavailable.
ones it extends, methods in declaration order; a concrete redeclaration in
the class wins over the interface signature) followed by the class's own
remaining public methods. A subclass stub declares only the methods whose
- *name* is new relative to the inherited stub surface; an omitted override
- must match the inherited parameter list (names, arity, defaults, passing
- modes — type declarations may differ), otherwise generation fails.
+ *name* is new relative to the inherited stub surface — including methods of
+ interfaces the subclass adds; an omitted override must match the inherited
+ parameter list (names, arity, defaults, passing modes — type declarations
+ may differ), otherwise generation fails. PHP builtin interfaces
+ (`Countable`, `Stringable`, ...) contribute no closure entries of their own;
+ the class's own public methods already cover their surface.
* Methods returning `self`/`static` perform the RPC and then `return $this;`
to preserve identity instead of round-tripping the handle.
-* **Class constants and static methods** are materialized verbatim from the
- real source (they read no instance state and run locally in the worker),
- together with any non-public static helpers they call.
+* **Class constants, static methods and public static properties** are
+ 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.
+* **`__toString`** is forwarded like any other method. **`__clone`** emits a
+ throwing body: proxy clone semantics are an open design question, and
+ cloning must not silently share the Rust handle between two stubs.
* **Imports**: the original file's `use` statements are kept in their original
order, restricted to names the emitted stub references; signatures declared
elsewhere (interface files) are re-spelled through that import table.
@@ -69,8 +79,8 @@ Generation fails — instead of emitting something quietly wrong — on:
* a target missing from the classifier report, classified other than
`rust-proxy`/`contract`, or a report carrying violations,
-* public properties (a stub cannot forward property access),
-* by-ref or variadic parameters, magic methods, static interface methods,
+* by-ref or variadic parameters, static interface methods, magic methods
+ other than `__toString`/`__clone`,
* an omitted override diverging from the inherited stub signature,
* a subclass target listed before its base class, or extending a class that is
not a target,
diff --git a/scripts/plugin-stub-generator/src/Generator.php b/scripts/plugin-stub-generator/src/Generator.php
index 9ab16ed8..b7cfc859 100644
--- a/scripts/plugin-stub-generator/src/Generator.php
+++ b/scripts/plugin-stub-generator/src/Generator.php
@@ -52,6 +52,28 @@ 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]);
+ }
+
+ public function __set($name, $value): void
+ {
+ \ShirabeRpcRuntime::callRust($this->__rhandle, '__set', [$name, $value]);
+ }
+ PHP;
+
+ private const CLONE_THROW = <<<'PHP'
+ public function __clone()
+ {
+ // Cloning a proxy is an open design question; fail instead of silently sharing
+ // the Rust-side entity between two stub instances.
+ throw new \RuntimeException('Shirabe does not support cloning ' . static::class . ' inside the plugin process yet');
+ }
+ PHP;
+
private Project $project;
private NamePrinter $printer;
@@ -122,9 +144,20 @@ final class Generator
$isRoot = true;
}
+ $hasPublicInstanceProperties = false;
+ $staticProperties = [];
foreach ($class->getProperties() as $property) {
- if ($property->isPublic()) {
- $this->errors[] = "$fqcn declares a public property; a proxy stub cannot forward property access";
+ if (!$property->isPublic()) {
+ continue;
+ }
+ if ($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;
}
}
@@ -140,15 +173,25 @@ final class Generator
$publicStatics = [];
$nonPublicStatics = [];
$ownInstanceMethods = [];
+ $cloneThrows = false;
foreach ($class->getMethods() as $method) {
$name = $method->name->toString();
if ($name === '__construct') {
continue;
}
if (str_starts_with($name, '__')) {
- if ($method->isPublic()) {
+ if ($method->isPublic() && !in_array($name, ['__toString', '__clone'], true)) {
$this->errors[] = "$fqcn::$name: magic methods cannot be proxied";
}
+ // __toString is an ordinary zero-argument call under a magic name and is
+ // forwarded below; __clone semantics are an open design question and the
+ // emitted body throws instead of silently sharing the Rust handle.
+ if ($method->isPublic() && $name === '__toString') {
+ $ownInstanceMethods[$name] = $method;
+ }
+ if ($method->isPublic() && $name === '__clone') {
+ $cloneThrows = true;
+ }
continue;
}
if ($method->isStatic()) {
@@ -183,12 +226,24 @@ final class Generator
}
} else {
$surface = $this->surfaces[$parentFqcn];
- if ($class->implements !== []) {
- $this->errors[] = "$fqcn adds interfaces to a stub base class; this is not supported yet";
+ // Interfaces the subclass adds contribute the methods whose names are new
+ // relative to the inherited stub surface.
+ foreach ($this->interfaceClosure($class) as $interface) {
+ foreach ($interface->classLike->getMethods() as $method) {
+ $name = $method->name->toString();
+ if ($method->isStatic()) {
+ $this->errors[] = "$fqcn: static interface method $name is not supported";
+ continue;
+ }
+ if (!isset($surface[$name])) {
+ $emitted[$name] ??= $method;
+ }
+ }
}
foreach ($ownInstanceMethods as $name => $method) {
if (isset($surface[$name])) {
$this->checkOmittedOverride($fqcn, $name, $method, $file, $surface[$name]);
+ unset($emitted[$name]);
} else {
$emitted[$name] = $method;
}
@@ -206,9 +261,11 @@ final class Generator
if ($class->extends !== null) {
$decl .= ' extends ' . $this->printer->renderName($class->extends, $file);
}
+ $interfaces = array_map(fn (Name $n): string => $this->printer->renderName($n, $file), $class->implements);
if ($isRoot) {
- $interfaces = array_map(fn (Name $n): string => $this->printer->renderName($n, $file), $class->implements);
$interfaces[] = '\ShirabeRustStub';
+ }
+ if ($interfaces !== []) {
$decl .= ' implements ' . implode(', ', $interfaces);
}
@@ -219,6 +276,15 @@ final class Generator
if ($constants !== []) {
$members[] = implode("\n", $constants);
}
+ if ($staticProperties !== []) {
+ $members[] = implode("\n", $staticProperties);
+ }
+ if ($hasPublicInstanceProperties) {
+ $members[] = self::PROPERTY_FORWARDERS;
+ }
+ if ($cloneThrows) {
+ $members[] = self::CLONE_THROW;
+ }
$members = array_merge($members, $staticMethods, $methodTexts);
$body = implode("\n\n", $members);
@@ -273,6 +339,11 @@ final class Generator
return;
}
$seen[$fqcn] = true;
+ if (interface_exists($fqcn, false) && (new \ReflectionClass($fqcn))->isInternal()) {
+ // A PHP builtin interface (Countable, Stringable, ...) has no source file to
+ // read; the class's own public methods already cover its surface.
+ return;
+ }
$file = $this->project->sourceFor($fqcn);
if (!$file->classLike instanceof Interface_) {
$this->errors[] = "$fqcn is implemented as an interface but is not one";
diff --git a/scripts/plugin-stub-generator/targets.list b/scripts/plugin-stub-generator/targets.list
index 42794e13..59656c6a 100644
--- a/scripts/plugin-stub-generator/targets.list
+++ b/scripts/plugin-stub-generator/targets.list
@@ -10,3 +10,14 @@ Composer\IO\BaseIO
Composer\IO\ConsoleIO
Composer\IO\BufferIO
Composer\IO\NullIO
+Composer\Package\BasePackage
+Composer\Package\Package
+Composer\Package\CompletePackage
+Composer\Package\RootPackage
+Composer\Repository\ArrayRepository
+Composer\Repository\WritableArrayRepository
+Composer\Repository\InstalledArrayRepository
+Composer\Repository\FilesystemRepository
+Composer\Repository\InstalledFilesystemRepository
+Composer\Repository\RepositoryManager
+Composer\Installer\InstallationManager