aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-shim/src/runtime.rs
AgeCommit message (Collapse)Author
2026-08-16feat(php-shim): render human-facing timestamps in the local timezonensfisis
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>
2026-08-15feat(php-shim): drop the modelled PHP version constantsnsfisis
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>
2026-08-12docs(todo): retag TODO markers by root causensfisis
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-11chore(php-shim): drop the HHVM_VERSION constantnsfisis
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.
2026-08-11chore(php-shim): drop the PHP_INT_* constantsnsfisis
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>
2026-08-11feat(signal): abort on SIGINT, SIGTERM and SIGHUP at checkpointsnsfisis
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>
2026-08-10refactor(symfony-process): drop the --enable-sigchild workaroundsnsfisis
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>
2026-08-10style(php-shim): drop the underscore from used parameter namesnsfisis
These parameters kept the leading underscore they were given while their function bodies were still todo!(), and the underscore now reads as "this argument is ignored" for arguments the bodies do use. Removing the prefix stops it from suppressing four clippy lints, fixed alongside: one redundant field name, and three `&mut Vec` parameters that only need a slice. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09feat(php-shim): replace error_get_last with the failing call's io::Errornsfisis
The shim raises no PHP-level errors, so error_get_last() always returned None and every message built from it lost its trailing reason. Now that the fs mutators and php_strip_whitespace carry an io::Error, take the reason from the call that actually failed instead of a global last-error slot. Filesystem::unlinkImplementation returns that error rather than a bool so ensureDirectoryExists, unlink and rmdir can report it, and PhpFileParser appends it to the "following message may be helpful" hint. The wording is Rust's io::Error text, not PHP's warning text, for the same reason noted in symfony/filesystem. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09refactor(installed-versions): drop the Rust port, which has no readersnsfisis
InstalledVersions is a runtime API for plugins and project code; Composer itself never reads it. Its consumers run in the PHP worker against the copy FilesystemRepository dumps to vendor/composer/InstalledVersions.php, whose static state is already kept in sync by __shirabe_installed_versions_reload. Nothing in Rust read the mirrored statics, so reload() and the reflection setters were no-ops. The tests covered only the Rust port, not the PHP class the worker loads, so they assert nothing about compatibility; they are left as todo!() skeletons. This also removes the shim functions method_exists, php_dir and require_php_file, whose only caller was the deleted module. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09refactor(installer): drop the GC control callsnsfisis
Composer turns the cycle collector off around the dependency solver, but Rust has no GC, so the gc_collect_cycles/gc_disable/gc_enable shims were no-ops. Remove the call sites and the now-callerless shim functions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09refactor(installed-versions): merge the name lists without call_user_func_arraynsfisis
The only live caller passed the constant 'array_merge', so the list concatenation is written out in place, as ClassLoader::get_prefixes already does. That leaves the shim function without callers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09refactor(php-shim): remove call_user_func, which has no callersnsfisis
Its doc-by-reference sibling call_user_func_array now carries the full reason text. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09refactor(php-shim): remove shim functions with no callersnsfisis
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09fix(dependency-resolver): query the PHP runtime for the php versionnsfisis
Problem reported the platform php version from the shim's compile-time PHP_VERSION constant, and the shim's phpversion() hit todo!() for any extension. Go through the RPC bridge instead, as PHP's Composer\Platform\Runtime does, and drop the shim function that now has no callers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09test(platform): drop HHVM-only paths from HhvmDetector testnsfisis
shirabe never runs on HHVM, so the HHVM_VERSION_ID branches are dead. Dropping them leaves the shim's unimplemented `constant()` without a caller, so remove it as well. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09feat(archiver): preserve file permissions in zip archivesnsfisis
The shim reported setExternalAttributesName as missing, so ZipArchiver took the branch for libzip below 0.11.2 and every archived entry got the zip crate's default mode. The attributes now travel as arguments to add_file and add_empty_dir, because the crate fixes an entry's external attributes when the entry is started and offers no way to amend one already written. unix_permissions keeps only the low 9 mode bits, on top of which the crate restores S_IFREG and S_IFDIR, so setuid/setgid/sticky bits and other file types are still dropped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09test(console): cover the dev build warning branchnsfisis
The deadline is a constant baked in at build time, so PHP's define() of COMPOSER_DEV_WARNING_TIME cannot make Application take that branch. Application now holds the deadline in a field that __set_dev_warning_time overrides, letting testDevWarning run instead of staying ignored. The define() shim, whose only caller was that test, goes away with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08feat(composer): bake the dev build warning deadline in at build timensfisis
Composer defines COMPOSER_DEV_WARNING_TIME from its phar stub when the compiled version is a commit hash rather than a tag, so the value exists only in the build artifact. It was a todo!() in the PHP shim, leaving the warning branch in Application::do_run unreachable. A build script now derives it the way Compiler does, from git describe and the HEAD commit date, and composer::COMPOSER_DEV_WARNING_TIME holds the result as a Rust constant instead of a runtime-defined one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08refactor(php-shim): dispatch spl_object_hash on Rc and referencesnsfisis
Getting a rule's identity meant spl_object_hash(&*rule.borrow()), so the solver borrowed a RefCell just to read an address, and a second function spl_object_hash_process existed because one generic fn cannot tell Rc<T> from &T. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08refactor(semver): replace Constraint's OP_*/STR_OP_* with CmpOpnsfisis
PHP's version_compare takes its operator as a string, so the shim's port did too, and Constraint carried two families of operator constants plus translation tables to convert between the string form and its own int codes. Five copies of those tables had accumulated across Constraint, CompilingMatcher and the plugin value bridge. version_compare now takes a CmpOp, which makes an invalid operator unrepresentable and removes the tables' reason to exist. Constraint stores a CmpOp and keeps only the string parsing its constructor needs; getOperator, compile and CompilingMatcher::match speak CmpOp as well. PHP's OP_* numbering stays observable: a plugin reads the raw integer off the Constraint object over RPC, so get_operator_constant and its new inverse hold that 0..5 mapping. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08chore: remove unused functionsnsfisis
2026-08-07test: port the tests left as todo!() stubsnsfisis
Replace the todo!() bodies with real ports. Four autoload-generator tests now run for real; the rest stay #[ignore]d, but each ignore reason now names the concrete missing symbol instead of a vague subsystem. Production additions the ports need: the deprecated AuthHelper::addAuthenticationHeader wrapper, EventDispatcher::__set_dispatch_script_override as the seam for PHPUnit onlyMethods(['dispatchScript']), and a define() stub in the shim. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02chore(todo): consolidate TODO comments into the five fixed marker tagsnsfisis
Retag every Shirabe-authored TODO comment to one of the fixed tags: phase-c, phase-d, plugin, php-runtime, phase-e. Upstream-authored TODO comments from Composer/Symfony are left untouched to preserve the ported code shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02feat(php-shim): implement syscall-backed functions via the nix cratensfisis
Several shim functions were left as todo!() because the standard library exposes no equivalent and no syscall crate was available. Adding nix unblocks them: - proc_open now wires descriptors beyond stderr, creating the pipe itself and installing the child end with dup2(2) from pre_exec - proc_terminate and posix_kill deliver arbitrary signals via kill(2) - get_current_user reports the owner of the running executable - php_uname answers every mode from uname(2) instead of only "s" and "r" It also closes gaps that were previously approximated: - fstat stats the stdio streams and pipes rather than reporting failure - touch stamps mtime/atime on an existing path, including directories - is_writable/is_executable use access(2) instead of permission bits - umask falls back to the read-modify-write umask(2) off Linux The hand-written repr(C) structs and extern "C" declarations for getpwuid, utime, statvfs, fcntl and select are replaced by their nix wrappers, which in turn lets disk_free_space drop its Linux-only cfg. cli_set_process_title, setproctitle and the pcntl_signal pair stay as todo!(): the first two need access to the process's own argv block, and the latter two depend on the signal-handling subsystem rather than on sigaction(2) itself. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01feat(php-src): add a BSD-licensed crate for php-src derived codensfisis
The audit in .ken/php-shim-copying.md judged 14 functions in shirabe-php-shim (plus php_wordwrap in shirabe-external-packages) to be line-by-line transcriptions or structural imitations of php-src. PHP's relicensing to 3-clause BSD makes keeping them legal, but the boundary between BSD-derived and MIT code was invisible in the source tree. Moving them into their own crate puts the license into the build metadata (so NOTICE generation follows the binary), makes a reverse dependency a compile error, and encodes the origin in the module path, which mirrors php-src's ext tree. Each function records its origin in a fixed-format doc comment, and a new php_src_derivation_boundary linter fails if `php-src` appears in any Rust source outside the crate. Public paths under shirabe_php_shim:: are unchanged: functions that are themselves derived are re-exported with `pub use`, and the wrappers that only validate arguments stay on the MIT side. This also resolves the duplicate wordwrap implementation. shirabe_php_shim::wordwrap was todo!(), so SymfonyStyle::block panicked, while shirabe-external-packages carried its own copy. Both now go through the single port, verified against real PHP on 13 cases covering multi-character breaks and cut. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25refactor: replace redundant clones with movesnsfisis
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-24fix(dependency-resolver): query real PHP for ext-* version/loaded checksnsfisis
Two shim gaps made the extension-related branches of solver-problem messages wrong: - phpversion($ext) with a non-empty extension can't be known statically (it's shirabe-php-shim's todo!()); Problem::get_missing_package_reason now calls shirabe_php_rpc::phpversion, the same RPC bridge platform::runtime::Runtime::get_extension_version already uses. - extension_loaded's hardcoded allowlist was missing "pcre", a mandatory always-compiled-in PHP extension, so ext-pcre was misreported as "missing from your system" instead of "disabled by your platform config" whenever a platform override disabled it. Also fix XdebugHandler::getAllIniFiles() always returning `[""]` (a php-runtime stub): create_extension_hint()'s early-return guard (`paths[0] empty && len==1`) fired unconditionally, silently dropping the entire "To enable extensions..." hint from every solver-problem message that mentions missing extensions. shirabe-external-packages can't depend on shirabe-php-rpc (shirabe-php-rpc already depends on shirabe-external-packages), so IniHelper::get_all() queries a new get_all_ini_files RPC command directly instead of going through the stub. This exposed that XdebugHandler is never constructed with a name because bin/composer's restart-without-Xdebug bootstrap was never ported to main.rs, making COMPOSER_ORIGINAL_INIS-driven behavior unreachable; documented with a TODO(phase-c) and updated ini_helper_test.rs's ignore reasons (and ignored test_with_no_ini, which only passed before by coincidence with the old stub's constant output) to match.
2026-07-24refactor(filesystem-repository): drop eval() shim, defer installed.php ↵nsfisis
reload to PHP runtime Rust has no PHP interpreter, so eval() can never be ported faithfully. safely_load_installed_versions()'s job of priming Composer\InstalledVersions before plugins run only matters within a single shared PHP process, which the RPC-based plugin architecture does not have; the PHP runtime process can call InstalledVersions::reload() itself instead. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04feat(php-shim): stub memory_get_usage/memory_get_peak_usage to return 0nsfisis
2026-06-27chore(php-shim): remove an unnecessary shim functionnsfisis
2026-06-27feat(php-shim): drop shutdown function shim, defer OOM message to PHPnsfisis
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26feat(php-shim): stub spl_autoload_register/unregister (TODO-flagged)nsfisis
Compiled Rust never loads a class by name, so the registered callback is dropped. Return success so callers that register an autoloader during startup can proceed. Kept the TODO(phase-d) comment: this is an unblocking stub, not a faithful implementation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26feat(http): reimplement CurlDownloader on reqwest; port 15 more testsnsfisis
Replace the libcurl-shim CurlDownloader with a reqwest+tokio implementation per the .ken sketch, resolving the construction panic that blocked command tests (mock path via __new_mock is untouched). Port remote_filesystem (7), hg/svn driver (4), zip_archiver/git_exclude_filter (4) tests. Fix hg/svn/git_exclude regex-delimiter and svn result-propagation porting bugs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26test: port 70 perforce/repository/downloader/installer/dispatcher testsnsfisis
Port perforce (36), locker (10), composer_repository (7), installation_manager (6), file_downloader (5), and event_dispatcher (6) tests via the mock infra. Fix production porting bugs surfaced en route: BufferIO::get_output look-behind regex, ComposerRepository list-form package iteration and initialize dispatch, gethostname and spl_autoload_functions shims; add EventDispatcher get_listeners test seam. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25feat(semver): hand-port constraint AND-split to drop look-around regexnsfisis
The PCRE delimiter `(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)` used to split AND-constraints relies on look-around, which the regex crate cannot compile (parse_constraints panicked). Reproduce its semantics in a hand-written `split_and_constraints` scanner shared by VersionParser and RootPackageLoader. Also model `method_exists` for the class-name form (shirabe runs no dumped Composer ClassLoader) and un-ignore the InstalledVersions tests, serialized via `#[serial]` since they share global static state. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21feat(php-shim): implement version_compare/spl_object_hash/clone et alnsfisis
Port php_version_compare (canonicalization + special-form ordering) and expose it via version_compare()/version_compare_2(). Implement clone() (via Clone), spl_object_hash()/spl_object_hash_process() (address-based), get_loaded_extensions() and the main-version case of phpversion(). Interpreter/reflection/autoload/eval/$GLOBALS/memory-accounting and Windows-only SAPI helpers remain TODO(phase-d): they have no equivalent in the compiled shim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21refactor(json): eliminate json_last_error in favor of Resultnsfisis
json_encode/json_encode_ex now return anyhow::Result<String> instead of Option, so callers no longer need json_last_error() to get the failure reason. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21refactor(php-shim): split lib.rsnsfisis