| Age | Commit message (Collapse) | Author |
|
AutoloadGenerator::dump dispatches PRE_AUTOLOAD_DUMP / POST_AUTOLOAD_DUMP, and
EventDispatcher::make_autoloader answers those by walking back into the Composer
graph -- the local repository, the installation manager, and the generator
itself. Installer::run held mutable borrows of all three across the dump call,
so any plugin subscribing to either event panicked with "RefCell already
borrowed" before its listener ran.
dump() and build_package_map() now take the local repository, the installation
manager and the locker as shared handles instead of &mut dyn, and the borrow is
taken where it is used.
The generator itself is re-entered, not just borrowed twice: PHP listeners reach
it through Composer::getAutoloadGenerator() and call setDevMode() while dump()
is running, and make_autoloader asks the same object for a package map. That is
not expressible behind &mut self, so the mutable state moves into Cell/RefCell
fields and the whole AutoloadGeneratorInterface takes &self.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The RuntimeInterface seam asked the worker one question at a time: a round
trip per loaded extension, per ReflectionExtension::info() output and per
constant, so a single `show --platform` cost 70 to 100 of them. A `platform`
dispatch entry now answers all of it as one PHP array, which shirabe-php-rpc
decodes into a OnceLock-cached PlatformInfo, the way the diagnose command
already works.
Composer\Platform\Runtime therefore has no Rust counterpart any more. Its
work belongs to the running interpreter, and invoke()/construct() could only
be ported as a whitelist that panicked on anything unlisted; it is ported as
PHP into the worker instead, and PlatformRepository reads the answers off
PlatformInfo. Accessors panic on a name the payload does not carry, so the
worker and its consumers cannot drift apart unnoticed.
The tests describe the runtime as payload data where they used to mock the
seam, with the datasets unchanged. The one loss is the call-count assertion
of test_inet_pton_regression: the payload reports the result of
`@inet_pton('::')` rather than answering a call.
Co-Authored-By: Claude Opus 5 (1M context) <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>
|
|
Three fixes for the ComposerRepository/FilesystemRepository/PlatformRepository
hazards where inner-composition delegation skipped PHP's late-bound virtual
dispatch:
- ComposerRepository::has_package now builds its packageMap through the
late-bound getPackages() equivalent, so lazy-providers repos surface the
LogicException and available-packages repos load their package list, as in
PHP, instead of silently answering false from the raw array.
- RepositoryInterface::get_repo_name returns anyhow::Result<String>: PHP's
getRepoName() counts through the late-bound initialize(), which is fallible
in file-reading subclasses. FilesystemRepository and PackageRepository now
run that initialization instead of freezing the inner array repository to an
empty state (which also made a later write() truncate installed.json).
Supporting changes keep the initialization chain callable from &self:
JsonFile::read takes &self (indent moved into a RefCell), FilesystemRepository
dev_mode became a Cell, and WritableArrayRepository dev_package_names a
RefCell.
- PlatformRepository::new routes constructor packages through its own
add_package so the override handling and full platform initialization run
as they do via PHP's parent constructor; the inner find_package/add_package
delegations inside add_package (and ComposerRepository::add_package) gained
the same is_initialized guard, since the constructor path would otherwise
freeze the repository.
Same defect class as 7db937af, 97b5211a and 3e367f78.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
Operations are only ever constructed by the dependency resolver, so a
plugin has no way to inject an implementation of its own and the set is
closed. Modelling it as an enum, like AnyPackage, removes the
OperationInterface trait together with its two parallel downcast
mechanisms (as_any() + downcast_ref, and as_*_operation()) and the
get_package() default method that panicked on UpdateOperation.
The PHP idiom `$op instanceof UpdateOperation ? getTargetPackage() :
getPackage()`, written out at six call sites, becomes
AnyOperation::get_target_package(). InstallationManager's three blocks
that matched on the type string and then recovered the type with
expect() collapse into exhaustive matches.
SolverOperation keeps only its TYPE constant; the shared
getOperationType()/__toString() implementations move to AnyOperation,
which also drops the five Self::TYPE.to_string() allocations.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Locker::get_stability_flags returned IndexMap<String, String>, converting
each value via PhpMixed::as_string(), which only matches the String
variant. composer.lock's "stability-flags" values are always JSON
integers (BasePackage::STABILITIES), so every flag silently decoded to
"" and installer.rs's downstream .parse::<i64>() defaulted it to 0
(stable). This made any locked package pinned via a non-stable
stability-flags entry look "unacceptable" during `composer install`,
silently dropping it from the solver's pool instead of fixing/requiring
it — turning a real dependency conflict into a spurious "lock file needs
changes" result. Return i64 directly, matching
RootPackageInterface::get_stability_flags and set_lock_data's existing
convention for this same PHP array shape.
|
|
test_update was skipped for a stale reason; running it uncovered three
distinct bugs it was actually catching:
- ApplicationTester::run never restored SHELL_VERBOSITY after
Application::configureIO mutates it, so one dataset's -vv verbosity
leaked into later runs sharing the process (Symfony's tester restores
it in a finally block; the port dropped that).
- Installer::do_install built its RepositorySet with a hardcoded empty
temporary_constraints map instead of self.temporary_constraints, so
--with never actually constrained the resolver.
- BumpCommand was missing a <warning> tag pair around one of its output
lines.
|
|
PHP's createRepositorySet does `$this->fixedRootPackage = clone
$this->package;` once and then passes that same object both to `new
RootPackageRepository($this->fixedRootPackage)` and, later, to
createRequest($this->fixedRootPackage) — object identity matters
because the solver assigns a pool id by mutating the package object
itself.
The port instead called RootPackageInterfaceHandle::dup() a second
time when registering the RootPackageRepository, producing a second
object that never went through the pool and so never got an id.
create_request's `request.fix_package(root_package_handle)` then
referenced a package with id -1, which add_rules_for_request treats as
a real bug: "Fixed package ... was not added to solver pool." This
surfaced whenever a create-project run reached the second-stage
install (i.e. every dist-installed project with real dependencies).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
Auditor::audit took io as &mut dyn IOInterface, forcing the audit and
installer post-audit call sites to hold a borrow_mut() on the shared IO
RefCell for the whole call. During advisory fetching the repositories
write to their own clones of the same handle, so the borrow_mut()
collided with their borrow() and panicked with 'RefCell already mutably
borrowed' on 'audit --locked'. Take the Rc<RefCell<dyn IOInterface>>
handle instead so writes borrow briefly and never overlap. Un-ignore the
locked-audit regression test that this unblocks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Composer/PartialComposer exposed its RepositoryManager, InstallationManager,
EventDispatcher, Locker, DownloadManager, AutoloadGenerator and ArchiveManager
as concrete types, but Composer's public setters (setDownloadManager() etc.)
let plugins swap in subclasses. Introduce a *Interface trait per manager and
store each as Rc<RefCell<dyn ...Interface>> so a replacement is honored.
Only Composer's slots and the sinks fed from its accessors become trait
objects; managers injected concretely at construction keep their concrete
references, matching PHP semantics. Fluent setters on the affected classes now
return () and Locker::update_hash is de-generified to a boxed FnOnce so the
traits stay object-safe.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
|
|
composer::semver stubs
Flatten shirabe-semver's modules into glob re-exports at the crate
root and route all consumers through the short paths. Remove the
duplicate composer::semver stubs from shirabe-external-packages in
favor of the shirabe-semver types.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
solve() previously dropped the SolverProblemsException and returned a
placeholder anyhow error, because its Rc<RefCell<Rule>> payload is not
Send+Sync and cannot ride anyhow::Error. Return
anyhow::Result<Result<LockTransaction, SolverProblemsException>> instead,
keeping fatal errors on the outer Result and the recoverable exception on
the inner one.
This lets the three Installer callsites port their PHP catch handlers
faithfully (pretty-string output, GithubActionError emit, getCode-based
exit codes) and lets solver_test.rs assert getProblems/getCode/
getPrettyString. The four ported assertion tests stay ignored: their
pretty-string path still reaches unimplemented todo!()s downstream.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
The List and Array variants of PhpMixed boxed their elements
unnecessarily. Store PhpMixed values directly and update all callers
accordingly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
|
|
Convert every sprintf() call with a compile-time literal format string to
format!, implementing Display for PhpMixed (delegating to php_to_string) so
PhpMixed values render with PHP string semantics through {}. Also merge the
format!-wrapped and conditional-literal dynamic sites into single format!
calls. Genuinely runtime format strings (table styles, configurable error
messages, command synopsis, progress-bar modifiers, regex-built messages)
still go through sprintf.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
|
|
* Catch specific exception types instead of broad/placeholder handling.
* Drop the shim Countable trait.
|
|
Replace value-by-value signature workarounds with proper ownership:
- validate_json_schema: borrow JsonFile via ValidateJsonInput<&JsonFile>,
restoring the dropped local auth file validation call
- Auditor::audit / Solver::new / create_pool: pass cloned Rc handles and
share Pool via Rc<RefCell<Pool>>; create_pool now takes &mut Request
- MarkAlias{Installed,Uninstalled}Operation: hand over AliasPackageHandle
from the alias-confirmed branch
- dispatch_installer_event: clone the base Transaction (Rc-backed
contents) and enable the PRE_OPERATIONS_EXEC dispatch
- SuggestedPackagesReporter: share between command and installer via
Rc<RefCell<>> to mirror PHP reference semantics
PrePoolCreateEvent remains a TODO: it is plugin-only and would require a
speculative Rc migration of Request whose payload is never read today.
|
|
PHP shares a single PlatformRepository by reference across the RepositorySet,
createRequest, VersionSelector, and (in show) the installed repository. The
port worked with owned values / &mut, so it could not share: create_repository_set
silently dropped the platform repo from the pool (PlatformRepository is not
Clone), show rebuilt a fresh PlatformRepository per use, and the package
discovery / show version selectors were stubbed because VersionSelector wanted
an owned RepositorySet.
Thread the existing PlatformRepositoryHandle (Rc<RefCell<PlatformRepository>>)
through installer.rs and show, restoring the RootPackageRepository + platform
repo registration and implementing same_repository via RepositoryInterfaceHandle
ptr_eq. Hold package-discovery repos as a shared RepositoryInterfaceHandle, and
share RepositorySet as Rc<RefCell<RepositorySet>> in the set caches and
VersionSelector (which only reads it), unblocking both stubbed selector sites
and dropping show's placeholder set.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Replace TODO(phase-b) placeholders (todo!() and commented-out code)
with real implementations:
- Share JsonFile via Rc<RefCell<JsonFile>> so JsonConfigSource and the
owning command can hold the same instance (base_config_command,
config_command, repository_command, require_command, create_project,
remove_command, factory)
- Change InstallerInterface methods (is_installed, download, prepare,
cleanup, get_install_path) to &mut self so initialize_vendor_dir can
run, propagated to all installer implementations
- Pass io/config/filesystem/process by clone instead of moving or
stubbing (auth_helper, svn_driver, curl_downloader, library_installer)
- Make TransportException Clone and store it by value in VcsRepository
- Clone operations in Transaction sort, root_aliases/temporary_constraints
in RepositorySet::create_pool, and share CompletePackage via handle in
PlatformRepository
- Wire up set_option, set_requires/set_dev_requires, installation manager
setters, BumpCommand::set_composer, and clean_backups/set_local_phar
|
|
Composer does not use its own deprecated APIs internally, so drop their
Rust ports: whole deprecated classes, methods, constants, and field.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Resolve the phase-b TODO that left the supported-link-types loop as dead
code (links were always an empty Vec), so requires/conflicts/provides/
replaces/require-dev are dumped again via
PackageInterface::get_links_for_type, matching the PHP magic-call loop.
Every Link in production is constructed with a pretty constraint (all
ArrayLoader/AliasPackage/PlatformRepository/InstalledRepository sites
pass one), so make Link::pretty_constraint a required String instead of
Option<String>. get_pretty_constraint() now returns &str directly rather
than anyhow::Result<&str>, dropping the unreachable
UnexpectedValueException guard, and all call sites are updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Resolve TODO(phase-b) in LocalRepoTransaction.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Change RepositoryInterface and WritableRepositoryInterface read methods
(find_package, find_packages, get_packages, load_packages, search,
get_providers, get_canonical_packages) to take &mut self and return
anyhow::Result, so lazy-loading repositories such as ComposerRepository
can perform fallible I/O and mutate internal state on access. Update all
implementors and call sites to propagate the Result and pass mutable
references.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
OperationInterface::clone_box (a todo!() trait-object clone stub) is
removed in favor of Rc<dyn OperationInterface> shared ownership. All its
methods are &self, so operations are immutable value objects that Rc can
share; pushing the same operation into multiple lists (installer's
install/uninstall splits) becomes a cheap Rc clone instead of clone_box.
Box<dyn OperationInterface> is replaced with Rc<dyn ...> across
Transaction (and its Lock/LocalRepo wrappers), Installer, PackageEvent,
InstallationManager and EventDispatcher; Box::new operation constructions
become Rc::new.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
PlatformRequirementFilterInterface::clone_box (a todo!() trait-object
clone stub) is removed in favor of Rc<dyn ...> shared ownership, matching
PHP's by-reference sharing of the single filter object. Box<dyn ...> is
replaced with Rc<dyn ...> across the factory, Solver/RuleSetGenerator,
VersionSelector, AutoloadGenerator, Installer and the command layer
(BaseCommand/PackageDiscoveryTrait and their impls); clone_box call
sites become Rc clones.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
PolicyInterface::clone_box (a todo!() trait-object clone stub) is removed
in favor of Rc<dyn PolicyInterface> shared ownership, matching PHP's
by-reference sharing of the single $policy object across Solver,
RuleSetGenerator and PoolOptimizer. With PoolOptimizer::new now taking an
Rc, Installer::create_pool_optimizer is implemented faithfully
(return new PoolOptimizer($policy)); create_policy returns the shared Rc.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
Package/CompletePackage/RootPackage
Fill the trait-impl todo!() across the package accessor layer so loaded
packages are actually usable (the ArrayLoader::load path depends on it):
- Package: implement BasePackage/PackageInterface from struct fields and
existing inherent methods; Display via get_unique_name.
- CompletePackage: delegate PackageInterface to inner Package.
- RootPackage: delegate CompletePackageInterface/PackageInterface to inner
CompletePackage; RootPackageInterface link setters delegate to Package.
Correct three unfaithful trait signatures found during implementation:
- get_target_dir returns Option<String> (PHP computes a normalized value;
a borrow cannot represent it, and AliasPackage could not implement &str
across its aliasOf handle).
- RootPackageInterface link setters take IndexMap<String, Link>, matching
Package and the real ArrayLoader caller (PHP RootPackage inherits
Package::setRequires; the Link[] docblock was imprecise).
- get_full_pretty_version takes a DisplayMode enum instead of a raw i64;
the match is now exhaustive, so it returns String without an error path.
Move mirror conversion to the boundaries: PackageInterface mirror methods
use Vec<Mirror> (the typed form, matching the inherent methods), with
array<->Mirror conversion done by the producer (ComposerRepository) and
consumer (ArrayDumper).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
|
|
|
|
Share LockArrayRepository via Rc<RefCell<_>> while preserving the PHP
?LockArrayRepository type strength: Request.locked_repository, Locker
::get_locked_repository, and the installer create_* helpers now thread
the typed handle instead of an owned LockArrayRepository, and pool
builder identity check becomes a strict ptr_eq via widening into
RepositoryInterfaceHandle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|
PHP packages have reference semantics, so introduce shared-ownership
handles over an AnyPackage enum (PackageInterfaceHandle and friends)
and replace Box<dyn PackageInterface> throughout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Replace the dyn ConstraintInterface trait objects with an AnyConstraint
enum closing over its four implementors (Simple, Multi, MatchAll,
MatchNone), mirroring the earlier Rule enum conversion. Rename
constraint.rs to simple_constraint.rs to match the renamed Constraint
type.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Convert Pool to Rc<RefCell<Pool>> so Solver, Decisions, and
RuleSetGenerator share it, resolving the todo!() placeholders that
blocked the dependency resolver (Phase C shared ownership).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Model PHP's `Composer extends PartialComposer` as a PartialOrFullComposer
enum and merge partial_composer.rs into composer.rs. Introduce
ComposerHandle / PartialComposerHandle (plus their Weak variants) so the
graph can be shared, and build it at once with Rc::new_cyclic in the
factory to resolve the back-reference cycles.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
|
|
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|