| Age | Commit message (Collapse) | Author |
|
Port Application::getPluginCommands: resolve the local composer with
plugins force-enabled, fall back to Factory::createGlobal, and collect
commands from CommandProvider capability adapters. PhpCommandProxy now
mirrors name/description/aliases/hidden over RPC so `list` output
matches upstream; input definitions remain TODO(plugin).
PhpClass::php_class_name returns an owned String because PHP-backed
proxies only know their class at runtime (the override-skip warning
prints get_class). CommandProvider::getCommands hands out shared
Rc<RefCell> handles since the commands are stored in the application.
Factory::createGlobal now propagates createConfig errors instead of
swallowing them, and Application::getComposer only catches the
exception classes upstream catches, so a ParsingException reaches
doRun's GithubActionError path as in Composer.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
getPluginCapability was a no-op returning None. Now it runs the real
flow: class_exists in the worker, new $capabilityClass($ctorArgs) with
the plugin's own phandle spliced in as $ctorArgs['plugin'], both
instanceof checks answered by is_a in the child, and a per-interface
Rust adapter over the resulting entity (CommandProvider and the plain
Capability marker; anything else is an explicit error).
getPluginCapabilities now propagates errors instead of swallowing them.
Capable::get_capabilities widens from IndexMap<String, String> to
IndexMap<String, PhpMixed>: upstream casts the return with (array) and
validates only the queried key, so the narrow type both rejected maps
Composer accepts and made the invalidImplementationClassNames data
provider unrepresentable. The old code also returned ' 0 ' (trimmed to
the falsy '0') as a valid class name where upstream throws; the
rewritten validation follows upstream's empty/is_string/trim sequence.
CommandProvider::get_commands returns BaseCommand adapters whose name is
read back over RPC after the PHP constructor ran configure(); executing
one still needs the PHP-side Symfony Application and stays an explicit
error. The is_array / instanceof BaseCommand checks that upstream's
Application::getPluginCommands performs on the raw getCommands value
live in the adapter, because Vec<Box<dyn BaseCommand>> asserts every
element up front.
Ports testCommandProviderCapability (plugin-v8 end to end against the
real worker) and testQueryingWithInvalidCapabilityClassNameThrows (all
eight provider cases); the two tests that pass a PHPUnit mock plugin
into PHP stay ignored — a Rust-native mock has no PHP-side entity to
cross the boundary as $ctorArgs['plugin'].
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
Widen the R table beyond Composer/IO with InstallationManager,
RepositoryManager, repository and package entities, interned by pointer
identity through a shared register_entity. Script events answer
getComposer/getIO, Composer hands out its graph getters, repositories list
their packages as per-variant stubs, package getters cover the
extension-installer surface (getRequires only while empty: the Link
snapshot encoding does not exist yet), and getInstallPath resolves its
package argument back through the R table. Everything else stays an
explicit error.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
Wires the addPlugin subscriber branch end to end: EventSubscriberInterface
and Capable become fallible and dyn-compatible (their sole implementor is
the PHP plugin proxy, which answers getSubscribedEvents over RPC), listeners
register as Callable::PhpMethod and are invoked with a per-call event handle,
and the R table now drops entries when a child-side stub destructs. The R
table keeps its IndexMap with monotonically increasing handles, so released
handles are never reused and no generation counter is needed.
Upstream has no subscriber-plugin test, so the path is covered by a
Shirabe-owned fixture exercising all three getSubscribedEvents shapes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
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>
|
|
InstallationManager::execute now takes &self (the mock recorder moved
into a RefCell) and its callers hold only shared borrows: plugin
registration inside a batch re-enters the same manager handle through
Composer::getInstallationManager()->getInstallPath(), which panicked
on the RefCell re-borrow under the &mut shape — the same re-entrancy
the repository side already fixed, unreachable from the ported tests
because they call PluginInstaller::install directly like PHPUnit does.
The worker-side InstalledVersions mirror now matches the full tail of
FilesystemRepository::write: unconditional reload plus the
reflection-based selfDir/installedIsLocalDir restore. The previous
class_exists(false) guard rested on a lazy-load assumption that does
not hold in the worker (its real ClassLoader only knows the Composer
checkout's vendor dir, so a later lazy load would read the checkout's
installed.php, not the project's); the mirror is now skipped only when
the class is not autoloadable at all, i.e. no plugin runtime and hence
no observer code. Boot-time seeding stays TODO(plugin).
Also from the review: registered_plugins entries are removed only
after the deactivate/uninstall loop (PHP unsets last, and a throw must
leave the entry observable); extra.class keeps associative-array
values and fails loudly on non-strings instead of silently dropping
them; the two discarded write() results now propagate (they carry the
reload-push failure); register_package's allow-plugins skip message is
DEBUG like the addPlugin side; the loader-eviction divergence of
REGISTERED_LOADERS and the lossy UTF-8 spots carry searchable
markers; the test-only proxy downcast follows the __ naming rule; the
R-table dispatch clones the entity out instead of holding the table
borrow across the handler; the IO/PartialComposer stubs turn a
plugin-side `new NullIO()` into an explicit error instead of an
ArgumentCountError; and the empty() emulation covers float 0.0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
Implement the remainder of PluginManager::registerPackage: the plugin
autoload map is built by the ported createLoader/parseAutoloads and
served to the worker over the existing reverse-RPC autoloader, files
entries go through a composerRequire-equivalent glue call, and
already-defined classes take the upstream _composer_tmp rename/eval
path. Instantiation uses the new NewObject/CallPhpMethod lanes backed
by a P table in the worker; PhpPluginProxy adapts the resulting handle
to PluginInterface, with $composer/$io exposed to plugin callbacks via
an R table (unsupported methods stay explicit errors). Hand-written
proxy stubs cover Composer, PartialComposer and the IO hierarchy, and
the stub autoloader is re-prepended after loading the Composer PHP
runtime so its vendor autoloader cannot shadow proxied FQCNs.
FilesystemRepository::write now mirrors InstalledVersions::reload into
a running worker (class_exists-guarded, so an unloaded class keeps its
upstream lazy-load behavior), removing the previously undefined
observation window.
The installer pipeline passes the installed repository as a shared
handle instead of a long-lived `&mut dyn`: plugin registration runs
inside InstallationManager::execute and re-enters the same local
repository through the RepositoryManager, which would panic on the
RefCell re-borrow under the old shape.
PluginInterface lifecycle methods now take an owned ComposerHandle
(plugins retain $composer past the call) and return anyhow::Result
(PHP plugin code may throw); the plugin list uses shared ownership so
the identity comparison of removePlugin survives the dual storage in
registeredPlugins, matching PHP reference semantics.
Ports the activate/upgrade/uninstall tests of PluginInstallerTest,
serialized across the shared worker process whose persistent class
table is exactly what exercises the rename path.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|