| Age | Commit message (Collapse) | Author |
|
This is the type Filesystem::directory_size() actually iterates, and it
swallowed a failed stat as 0 the same way. PHP's iterator yields
\SplFileInfo there, so raise the same \RuntimeException it would.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
\SplFileInfo::getSize() throws a \RuntimeException when stat fails, and
neither Symfony's subclass nor Composer's Filesystem::directorySize()
catches it. Returning 0 turned an unreadable file into an empty one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
PHP keeps three separate environment storages and docs/dev/env-vars-
porting.md maps each to its own shim construct. Reaching for std::env
silently picks one, so the porting target has to be chosen by reading
the PHP source rather than by whichever Rust call is at hand.
Detect var/var_os/vars/vars_os/set_var/remove_var. current_dir, args,
consts, temp_dir, current_exe and the path split/join helpers are not
environment storage and stay allowed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
These sites have no PHP counterpart to mirror, so they read std::env
directly. Going through the shim's getenv() keeps every environment
read in one place and lets a lint forbid the direct form.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
CaBundle::getEnvVariable() prefers $_SERVER and falls back to getenv()
only under the CLI SAPI. The port read the live process environment for
both SSL_CERT_FILE and SSL_CERT_DIR, skipping the snapshot entirely.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
ProxyManager::initProxyData() reads $_SERVER[$name], a startup snapshot,
while the port read the live process environment. A putenv() issued
after startup changed the proxy the port picked but not the one PHP
would pick.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Factory::useXdg() enumerates array_keys($_SERVER). The port enumerated
the live process environment instead, so a COMPOSER_HOME resolved
against keys the PHP side never sees.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The signature took usize, so PHP's negative $start and $length, which
count from the end of the string, could not be expressed. Take i64 and
an optional length, and apply PHP's clamping rules.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
serialize(), var_export() and the string cast each hand-rolled
`format!("{}", f)`, which never emits PHP's exponential spelling and
carries the wrong precision for the string cast. PHP writes 1.0E+20
where Rust writes 1e20, and (string) 1/3 is 0.33333333333333, not the
shortest round-trip form.
Delegate all three to smart_str_append_double, which shirabe-php-src
already provides and shirabe-php-rpc's codec already used: precision -1
for serialize() and var_export(), 14 for the string cast.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Rust's shortest float formatting rounds a tie away from zero where
zend_dtoa mode 0 rounds to even, so digits taken straight from `{:e}`
diverged from PHP on values such as -166050639803968.125.
Take only the digit count from `{:e}` and re-format at that fixed
precision, which rounds the exact decimal value half to even. The same
routine supplies the fixed-precision mode `php_gcvt` previously refused
with an assert, so both modes now share one path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The class was shadowed by a guard, so a plugin doing `new Filesystem()`
got an explicit error. It is classified rust-proxy and plugin-constructible
and the Rust port is complete, so listing it as a stub target and answering
its public surface from the entity is all it takes.
The constructor rejects a caller-supplied ProcessExecutor: that class has no
proxy stub, so the argument could only be a second instance the Rust side
never sees. findShortestPath re-checks its arguments at the boundary because
the port panics where PHP throws, and a panic would take the process down
instead of reaching the plugin's catch block.
phpstan/extension-installer matches upstream Composer byte for byte again.
|
|
The worker is a long-lived child holding the environment it was handed
at spawn, so `@putenv`, the bin dir the event dispatcher prepends to
PATH, and COMPOSER_DEV_MODE never reached the PHP code running in it.
The shim now journals every write to the three storages PHP exposes, and
the outermost rpc_call replays the entries the worker has not seen yet
through __shirabe_sync_env. Replaying the writes rather than pushing a
whole snapshot keeps the worker's own $_SERVER entries intact.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The worker's autoloader fell through to the real Composer source for every
Rust-owned FQCN without a proxy stub, so plugin code doing `new Filesystem()`
or subclassing `LibraryInstaller` silently ran on a second instance the Rust
side never sees. An unimplemented part of the plugin API has to fail with an
explicit error naming it, not quietly work on a disconnected copy.
The stub generator now emits a guard class for each of those FQCNs: the real
declaration, hierarchy and constants, with every constructor and method
raising an explicit error. References satisfied by the declaration alone
(`instanceof`, `X::class`, `Link::TYPE_REQUIRE`) keep working. Two FQCNs stay
resolvable to the real class, each listed with the worker-side mechanism that
makes a natively constructed instance correct.
The error had nowhere to go: `Installer::run` dropped the `Result` of both
`dispatch_script` calls, so an exception from a listener ended in exit 0.
Both propagate now, the way the exception does upstream.
Three real-plugin E2E comparisons stop at a guard and are ignored, each
naming the class it needs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
json_decode built a serde_json::Value first and then converted it. Drive
serde_json's Deserializer with a DeserializeSeed instead, so the value is
built in one pass without the intermediate representation.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The assoc flag was always a literal at every call site, so the boolean
carried no information the function name could not. json_decode_assoc
and json_decode_obj make the resulting PhpMixed shape visible at the
call site.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Config::get() resolves cache-files-maxsize by matching the stored value
against a size regex. The port read that value with as_string(), which
yields None for anything that is not a string, where PHP casts with
(string).
create-project feeds Config::all() back through Config::merge(), so the
second read finds the byte count the first read produced rather than the
original "300MiB". as_string() then yields an empty string, the regex
fails, and the resulting RuntimeException is swallowed by Config::get(),
which maps Err to null. FileDownloader turns that null into a zero max
size via .as_int().unwrap_or(0), and Cache::gc() deletes every file in
the download cache because the total always exceeds zero.
Apply the same cast on the path branch, the other site where Config.php
writes (string).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
fstat and lstat now return Option<FileStat> instead of a PhpMixed array, so
Platform::is_tty and Filesystem::is_junction read `mode` as a field. The
array carried each of the 13 values twice — once under its numeric index and
once under its name — which no caller relied on, and building it spelled the
field list out four times.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
posix_getpwuid now returns Option<PasswdEntry> instead of a PhpMixed array,
so Platform reads the field it wants rather than digging through the map.
posix_getuid and posix_geteuid return u32, matching the uid PasswdEntry is
looked up by.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
parse_url now returns Option<UrlComponents> instead of a PhpMixed array,
and the component-selecting overload with the PHP_URL_* constants is gone:
callers read the field they want. Two call sites change behaviour as a
result, both towards PHP:
* CurlDownloader::handle_redirect tested scheme and host with is_null(), so
an unparsable Location header (PhpMixed::Bool(false)) counted as an
absolute URL. PHP's truthiness test sends it to the relative-path branch.
* Url::get_origin appended a literal port 0, which PHP treats as falsy and
leaves off.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
SymfonyStyle modelled PHP's string|array message parameters, its array of
listing elements and its table headers/rows as PhpMixed, then normalised
and stringified them at every entry point. Take &[String] for the message
and listing parameters, Vec<Cell>/Vec<Row> for table (matching the
horizontal_table signature) and Vec<String> for the choice options, so the
is_array/is_iterable normalisation and the php_string helper both go away.
PhpMixed stays where PHP is genuinely mixed: the question answers returned
by ask/ask_hidden/choice, their validators, choice's string|int|null
default, and the progress_iterate elements.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
doWrite, doOverwrite and sanitize accept PHP's string|list<string>, which
this port modelled as PhpMixed. Every call site inside ConsoleIO passes a
single string, so take &str and return String instead, dropping the
(array) casts and the to_string_list helper.
Auditor is the only caller that passed a list: it builds table rows, whose
cells must stay separate, so it now sanitizes each cell. select() likewise
sanitizes each choice while projecting them into the keyed form.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The listeners of five tests are methods of
`Composer\Test\EventDispatcher\EventDispatcherTest`, which the worker
could not load because the class extends `PHPUnit\Framework\TestCase` and
phpunit is not part of the Composer runtime bundle. An empty stand-in for
that base class is enough: a parent class only has to exist at declaration
time, and method bodies and type declarations resolve lazily. The upstream
file is then required into the worker unchanged, so the listener bodies and
their `__DIR__` stay what Composer ships. The two assertions those bodies
call are the only PHPUnit members the stand-in has to implement.
`remove_listener` now compares a handle for an object the worker really
holds instead of a hand-written one, and `create_composer_instance` wires
the collaborators the autoloader-rebuild path reaches for, as the PHP
helper does.
Two tests stay ignored for a different reason: `Platform::put_env` writes
only the Shirabe process environment, while the listeners read `getenv()`
inside the long-lived worker.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Porting mapped every PHP `protected` member onto `pub(crate)`, which is
wider than nearly all of them need. Each item demoted here is reached
only from the module that defines it, so the crate-wide visibility
conveyed nothing.
Every `pub(crate)` that survives has at least one reader in another
module of the same crate.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Split date() into date_utc() and date_local(), the latter resolving the
system's local timezone through the tzfile crate ($TZ, then
/etc/localtime, falling back to UTC when neither is readable). The
timestamps Composer renders for humans -- the GitHub OAuth token note,
the GitHub API rate limit reset time, the Perforce client spec fields and
the "today" check of the show command -- now go through date_local().
PHP resolves its default timezone from the date.timezone ini setting,
which Shirabe does not read, so date_default_timezone_get/set have no
input left to model and are dropped from the shim and its callers. The
resulting difference is recorded in docs/known-incompatibilities.md.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
PhpDirHandle recorded the opened path and nothing else, and its only
caller asked just whether the open had succeeded. A resource type that
has to gain readdir and closedir before it means anything is worse than
the std::fs::read_dir call it wraps, so Filesystem::isReadable now makes
that call directly.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The two TODOs marked deferred work that nothing reaches.
FILE_USE_INCLUDE_PATH appears nowhere in Composer or its vendored
dependencies, and the only site passing LOCK_EX to file_put_contents is
Symfony's Filesystem::appendToFile(), which no caller invokes and which
raises the flag only when handed a third argument. The stream $context
is dropped just as narrowly: the network scheme that would carry one is
already marked at the call site in RemoteFilesystem, leaving the shim
reachable through the local-file branch alone.
Record what these functions do and do not support instead, so the tags
stay a list of work that is actually pending.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The comment claimed the port loses the file type bits that PHP's
fileperms() reports. It does not: MetadataExt::mode() hands back the raw
st_mode, the same value php_stat() returns for FS_PERMS, and both agree
on directories, regular files and executables.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
The binary called itself Composer everywhere: the application name, the
logo, --version, about, and every warning that talks about the running
program. Prompts to file a bug also pointed at Composer's issue tracker.
Add SHIRABE_VERSION and SHIRABE_RELEASE_DATE next to the Composer version
constants and report those, naming the Composer version this port tracks
alongside them. Composer::VERSION and getVersion() are untouched, so the
composer platform package, composer-runtime-api and the HTTP User-Agent
keep the value plugins and package repositories expect.
build.rs stamps the release date with the UTC date of the HEAD commit,
the way Composer's Compiler fills in @release_date@ when building the
phar. It now also fails the build when git cannot be read, instead of
letting COMPOSER_DEV_WARNING_TIME fall back to the tagged-release value
and suppress the outdated-build warning forever.
Messages about the Composer ecosystem keep their wording. Two of them are
pinned by upstream installer fixtures (Rule's "cannot be modified by
Composer" and SolverProblemsException's "you can run Composer with") and
stay as they are so those fixtures can keep being used verbatim.
The e2e list comparison against upstream Composer now skips the banner,
which cannot match by design, and compares everything below it as before.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The shim reported a fixed PHP 8.1.0 through PHP_VERSION, PHP_VERSION_ID,
the major/minor/release triple and the PHP_WINDOWS_VERSION_* trio. Their
uses split in two.
Some guarded branches PHP only needs on runtimes this port cannot be:
proc_get_status reports the exit status on every call, so Symfony's
pre-8.3 exit-code cache has nothing to work around; hash_raw and
hash_file always offer xxh3, so the sha1 fallback is unreachable; and
http_get_last_response_headers is always available, so the pre-8.4
$http_response_header branch is gone. safeJunctions reads the host
Windows version rather than PHP state, and joins the Windows work on
hold.
The rest ask about the PHP the user actually runs, and now reach the
worker through a new php-rpc PhpVersion payload: the startup banner and
the 7.2.5 warning, self-update's min-php filter, the ext-*
recommendation in VersionSelector, the stream User-Agent, and whether
PhpFileParser scans for enums.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
`http_build_query` ignores `numeric_prefix` because a string-keyed slice never
holds an integer key, and every caller passes `"&"` as the separator. Drop both
parameters and hard-code the separator.
Callers that pass a map of scalar literals to `http_build_query_mixed` no longer
need to build an `IndexMap<String, PhpMixed>` for it, so move them to the slice
form.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
`http_build_query(['packages' => array_keys($packageConstraintMap)])` maps a
single array value, so the body PHP sends is `packages%5B0%5D=a&packages%5B1%5D=b`.
The port flattened the array into repeated `packages=<name>` pairs, losing the
indices, and passed `"&"` as the numeric prefix and `"="` as the argument
separator, joining every pair with `=`.
Build the query from a `packages` key holding the name list and hand it to
`http_build_query_mixed`, which walks the nested value.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
A worker whose PHP cannot read the bundle out of the executable gets it
from an unpacked copy, which went to a directory derived from
XDG_CACHE_HOME alone. That ignored COMPOSER_CACHE_DIR, COMPOSER_HOME and
the cache-dir setting, and put the files outside the directory clear-cache
and the platform conventions cover. The callers now pass Composer's
configured cache directory down to base_path().
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
checkComposerAudit reported success instead of auditing anything, because
the binary ships no vendor/composer/installed.json on disk. It reads the one
in the embedded Composer PHP runtime now, and Composer's warning for a
missing installed.json is back.
Only that file leaves the bundle, into a temporary directory that goes away
with the handle; the runtime is unpacked whole only for a worker that cannot
read the bundle in place. Phar::extractTo's $files argument selects it,
which the shim ignored so far.
SHIRABE_COMPOSER_PHP_DIR moves into composer_runtime, so the worker and a
reader on the Rust side resolve the runtime through the same branch.
DiagnoseCommandTest::testCmdSuccess is ignored: packagist has advisories
against composer/composer 2.9.7, the version Composer::VERSION reports, so
diagnose exits 1 where the test expects 0. Upstream Composer 2.9.7 reports
the same advisories.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
`__halt_compiler` is a PHP keyword, so a stub may spell the token in any
letter case. The native phar reader compared the bytes exactly and rejected
such an archive, and the lint that keeps a second token out of the
executable missed lowercase ones, which would shadow the embedded bundle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Plugins and scripts need the real `Composer\` classes and the packages
Composer depends on, which so far came from a checkout found through
SHIRABE_COMPOSER_PHP_DIR or a path next to the workspace. Neither exists
for a distributed binary.
The build script now archives those PHP sources into a phar the way
Compiler.php does and the executable carries it. The worker maps it with
Phar::loadPhar and reads a content-addressed sentinel back to tell a
bundle it can use from one it cannot; where its PHP cannot open the phar,
the bundle is unpacked once into the cache directory and autoloaded from
there. SHIRABE_COMPOSER_PHP_DIR still overrides both for development.
PHP locates a phar's manifest by the first __HALT_COMPILER(); token in
the file, so the executable must hold no other copy of it: phar.rs builds
the token at run time, and a linter keeps further literals out of the
sources that reach the binary.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
InstalledVersions has no Rust port, so the skeletons left behind asserted
nothing about compatibility. The tests now require the Composer checkout's
vendor/autoload.php into the PHP worker and drive the real class there, which
puts $selfDir, the registered ClassLoader and Composer\Semver\VersionParser in
the same state as the upstream PHPUnit run. The upstream setUp reflection and
the installed_relative.php require stay in PHP; the expected values are Rust.
The class Composer's own vendor directory autoloads and the one
FilesystemRepository::write dumps from include_str! are separate files, so an
added test asserts they hold the same bytes.
FilesystemRepositoryTest::testSafelyLoadInstalledVersions moves to the worker
too, against the php/stubs FilesystemRepository, whose safelyLoadInstalledVersions
runs the PCRE recursive grammar natively.
The shared worker helpers live in tests/common/php_worker.rs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Measuring every ignored test with `cargo test --workspace
--no-fail-fast -- --ignored` turned up 11 that pass today; their
reasons named todo!()s and gaps that have since been implemented.
The two run_script_command_test cases still fail, but not for the
reason given: the user's Command class is imported and executed, and
what is missing is only a way to observe it, since PhpCommandProxy::run
forwards the run to the worker-side console application.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The command's own definition covers only --dev/--no-dev and the args
array, so any other application-level option reaching it (as when
GlobalCommand re-parses `--no-interaction=1` out of ArrayInput's
stringification) aborted the run with "does not accept a value".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The constant was None and defined("HHVM_VERSION") reports it undefined, so
every branch guarded by it was dead: shirabe is a Rust binary and never runs
on HHVM. HhvmDetector keeps probing for an `hhvm` binary in PATH, which is
what actually produces the hhvm platform package.
Two of the dropped branches ask about the PHP runtime that consumes the
result rather than about shirabe itself -- the class loader's Hack file
lookup and the class map parser's enum scanning -- so both get a
TODO(php-runtime) marker.
|
|
str_contains(), str_starts_with() and str_ends_with() were thin wrappers
over the str methods of the same semantics. Call sites now use
contains()/starts_with()/ends_with() directly.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
PHP_INT_MAX/MIN/SIZE have exact Rust counterparts under the int -> i64
mapping, so the call sites use i64::MAX directly. PHP_INT_SIZE is
queried from the PHP runtime where it is actually needed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Output buffering captures whatever the PHP interpreter would echo to
stdout. The shim routes no output through a buffer, so these functions
could never capture anything and stayed todo!(). Record the gap in
docs/known-incompatibilities.md instead.
|
|
The SignalHandler port was a no-op stub, so all four of Composer's abort
paths were dead code: nothing removed a half-created project, reverted
composer.json, or cleaned up half-installed packages.
Composer runs those handlers from pcntl callbacks, which a Rust signal
handler cannot do -- it may touch nothing beyond atomics. SignalSubscription
records the signal instead, and the abort runs from checkpoints on the normal
call stack, where the clean-up can borrow the state it needs. That also
resolves the closure-capture TODO(phase-c)s in RequireCommand and
InstallationManager, and replaces exit_with_last_signal's exit(0) with the
restore-and-re-raise Seld\Signal does.
A subscription is live only inside the four abort regions, so elsewhere the
signals keep their default disposition and kill the process at once. It is
installed without SA_RESTART so a signal interrupts an interactive prompt
rather than resuming the read. A signal reaches only the innermost
subscription, reproducing SignalHandler's single-stack dispatch.
Drop SignalRegistry, SignalableCommandInterface and the Application wiring
for them: nothing in Composer reaches that path, and SignalHandler discards
whatever they register. Signal handling from plugins and scripts is
undefined behavior; see docs/dev/signals.md.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Composer's Composer\Util\TlsHelper is marked deprecated for removal in
Composer 3.0 and has no caller in composer/composer outside its own
test: PHP's stream layer verifies certificate hostnames itself, and the
one surviving method delegates to composer/ca-bundle.
The Rust port had no caller either, so it, its test, and the
openssl_x509_parse shim it was the sole user of are removed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
`Application::do_run` registers a `composer.json` script whose value names a
`Symfony\Component\Console\Command\Command` subclass as a live command. The
class checks and `new $dummy($script)` need a real PHP runtime, so they run in
the worker: the command object lives there and this side keeps a metadata
mirror for `list`/`help`, forwarding a run to the worker-side console
application it is added to. The name and description fixups are applied to the
worker-side object, so both sides carry the same values.
Loading the Composer PHP runtime into the worker is gated on the Rust-side
`ClassLoader`s resolving the class to a file, keeping that load out of every
run whose scripts are plain shell commands.
The worker-side console application handoff now accepts commands registered
after it was published, since the scripts scan runs after plugin commands are
collected. Whether it was published is tracked per application: the handoff is
process-wide, so a second application must replace it rather than extend it.
`shirabe_php_shim::is_subclass_of` has no callers left.
|
|
isSigchildEnabled() detects a PHP built with --enable-sigchild, where PHP
reaps children itself and proc_get_status()/proc_terminate() stop reporting
or reaching them. Shirabe spawns child processes from Rust, so that build
option cannot affect them and every sigchild branch was unreachable.
Removing the branches retires the state that existed only to feed them:
fallbackStatus (written by the fourth pipe and by doSignal, read only by
the sigchild merge in updateStatus) and useFileHandles (whose sole reader
was the sigchild condition). shirabe-php-shim loses phpinfo(),
INFO_GENERAL and posix_kill(), which had no other callers.
DiagnoseCommand keeps its --enable-sigchild warning: it reports on the
user's PHP installation, not on how Shirabe runs processes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
ClassLoader's PSR-0 include_path lookup went through a shim stub that could
only have answered with an invented include_path: the setting is PHP runtime
state, and the one thing that turns useIncludePath on is the generated
autoload_real.php, whose set_include_path() call runs inside the worker.
Ask the worker instead, so the search sees the same path list as the process
that ends up including the resolved file.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|