aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-shim
AgeCommit message (Collapse)Author
2026-08-02refactor(php-shim): remove unused curl functions/constantsnsfisis
2026-08-02refactor(console-io): make ensure_valid_utf8 a no-op, drop iconv shimnsfisis
Rust's &str is always valid UTF-8, so the mbstring/iconv sanitization chain can never trigger. Reduce it to a no-op with a TODO(phase-c) marker: once the codebase strictly separates Vec<u8> from String, this should take &[u8] and convert lossily. This removes the last caller of the php-shim iconv(), so delete it as well. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02refactor(symfony-console): drop unused Cursor methodsnsfisis
Remove the eight Cursor methods that have no callers, and the sscanf shim whose only caller was Cursor::get_current_position. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02refactor(php-shim): drop pack/unpack in favor of direct byte handlingnsfisis
The only callers were trivial fixed-format uses: reading the first four hash bytes as a native int, splitting in_addr byte strings, and building a constant ZIP EOCD record. Each site now does the byte manipulation directly, so the general-purpose shims are no longer needed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02refactor(php-shim): replace sprintf todo!() with an unsupported panicnsfisis
%e/%E/%g/%G never appear in Composer's own format strings (verified by sweeping composer/src and vendor); supporting PHP's exponent formatting is intentionally out of scope, so fail permanently instead of marking the specifiers as pending work. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-01feat(php-shim): implement Phar/PharData and the zlib/bzip2 functionsnsfisis
Adopt the tar, flate2, and bzip2 crates to fill in the phar.rs and compress.rs todos: PharData tar/zip reading, building, and whole-archive compression, plus a native .phar reader that follows the php.net file-format manual and verifies hash-based signatures. Callers now propagate the constructor/extract errors PHP throws, and fwrite accepts byte strings so gzread no longer needs lossy UTF-8. The native .phar writing API stays todo!() (no call sites; Composer's Compiler is not ported) and OPENSSL phar signatures are accepted unverified (TODO(phase-c)). This unblocks Tar::getComposerJson and the tar/phar/gzip downloaders; tar_test (7), artifact_repository_test (2), and phar_archiver_test zip (1) are un-ignored. The archive command itself still panics because ArchiveManager::archive always generates glob excludes whose look-ahead regexes the regex crate cannot compile; converting those patterns to regex-compatible ones is a separate, still-undecided work item. Co-Authored-By: Claude Fable 5 <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-26feat(diagnose-command): query the real PHP runtime in one RPC callnsfisis
diagnose used to read hardcoded shim stubs, so it described a fictional runtime: OPENSSL_VERSION_NUMBER was always 0 and tripped the TLSv1.1/1.2 check, PHP_BINARY and OPENSSL_VERSION_TEXT were empty, and the extension, function and ini probes answered from a fixed table. The PHP worker gained a `diagnose` entry that returns every fact the command needs as one PHP array, cached in a OnceLock so the several call sites share a single round trip. Reading it back needed array support in the serialize() parser, which in turn lets get_loaded_extensions and get_all_ini_files return real lists instead of comma-joined strings. Also fixes the openssl_version message, which dropped strstr()'s before_needle argument during the port, and check_connectivity's allow_url_fopen test, which did not follow PHP string truthiness. 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(solver-problems): compare RULE_LEARNED sort keys like PHP's <=>nsfisis
Problem::getPrettyString sorts same-priority reasons by getSortableString(), whose RULE_LEARNED key is a '-'-joined literal id string (e.g. "-95"). PHP's <=> compares two numeric strings numerically, but the port used plain String::cmp (byte-wise), which reverses relative order for same-length negative-number keys. Added shirabe_php_shim::loosely_compare to approximate PHP's <=> for this pattern (numeric compare when both sides parse as numbers, else byte compare) and switched the sort comparator to use it. The diagnosis this replaces (from the commit being amended) blamed Pool package-id assignment order diverging from PHP under COMPOSER_POOL_OPTIMIZER=0. That was disproven this session: direct instrumentation of both PHP and the Rust port confirmed identical relative package-id order, including on the ~335-package github-issues-7665 fixture (ids matched up to a constant +2 offset from a platform-mock package count difference). The sort comparator was the actual bug, not package loading order. Co-Authored-By: Claude Sonnet 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-24fix(php-shim): keep literal '>' outside tags in strip_tagsnsfisis
The b'>' match arm in strip_tags's state machine never pushed the character to the output buffer when encountered outside a tag (state 0), unlike every other special-character arm (!, ?, -, and the catch-all) which does push in that state. Every literal '>' not part of an HTML-like tag was silently dropped. This corrupted "=>" into "=" in Composer's operation trace strings (e.g. "Upgrading foo/bar (1.0.0 => 1.1.0)"), which go through strip_tags to remove the <info>/<comment> markup before comparison, un-ignoring 82 integration tests that were asserting on that exact arrow.
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-24refactor(symfony-process): remove unused Process API surfacensfisis
Since Process is php-native with no Rust-fidelity obligation (see plugin-class-classification.md), this port only needs to cover what Rust-ported Composer code actually calls. Made the pipes/process_utils modules pub(crate) (nothing outside symfony/process used them) and rebuilt with `--force-warn dead_code` (normally allowed workspace-wide) to find genuinely unreachable methods: Process lost 17 methods, 5 constants, and a private clone helper; ExecutableFinder lost two unused suffix setters; AbstractPipes lost handle_error, whose only caller (a stream_select error-handler registration) was never wired up. Removing several of those setters (set_pty, set_idle_timeout, disable_output/enable_output, set_options) then left the fields they used to write with no remaining writer, so they hold one constant value on every reachable path: pty always false, idle_timeout always None, output_disabled always false, options always {suppress_errors, bypass_shell}. Audited by value (not just call-graph reachability) and removed everything that depended on the now-constant value: - pty: is_pty(), is_pty_supported(), the PTY descriptor branch in UnixPipes::get_descriptors(), and the now-unconstructed Descriptor::Pty variant in shirabe-php-shim (plus its proc_open match arm). - idle_timeout: get_idle_timeout() and check_timeout()'s idle branch; ProcessTimedOutException collapses to the single reachable timeout type (dropped timeout_type/TYPE_GENERAL/TYPE_IDLE/is_general_timeout/ is_idle_timeout/get_exceeded_timeout). - output_disabled: is_output_disabled(), build_callback()'s disabled variant, get_descriptors()'s output_disabled term, and the always-false guard in read_pipes_for_output()/ProcessFailedException (its output section is now unconditional). - options: Drop::drop()'s create_new_console branch can never fire (that key can no longer exist), so it always just stops the process. - has_callback/last_output_time: left write-only once their only readers (the branches above) were gone. - have_read_support: constant true once output_disabled collapsed, so removed from PipesInterface, UnixPipes (incl. its /dev/null null-stream branch), WindowsPipes, and Process::wait()'s dead guard. No behavior change: every removed item/branch had zero callers, or was constant on every reachable call site.
2026-07-20fix(no-proxy-pattern): stop chr() corrupting IP bytes as lossy UTF-8nsfisis
shirabe_php_shim::chr() returned a Rust String, which lossily re-encodes bytes >= 0x80 as UTF-8 replacement characters. ip_get_mask, ip_get_network, and ip_map_to_6 relied on chr() to build raw in_addr and netmask byte arrays, corrupting IPv4-in-IPv6 mappings and CIDR netmasks. Build the Vec<u8> byte arrays directly instead of round-tripping through String, and un-ignore test_ip_address and test_ip_range now that the underlying bug is fixed. chr()'s only other caller (http_downloader.rs, an ASCII ESC byte in a regex pattern) didn't need the indirection either, so remove the shim function entirely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20fix(advisory): treat int-keyed ignore entries as plain IDsnsfisis
parseIgnoreWithApply distinguishes ['CVE-123' => 'reason'] from [0 => 'CVE-123'] by checking is_int($key) in PHP. AuditConfig only matched on the value's type, so a canonical-int-keyed string value was mistaken for an id => reason pair. Expose canonical_int_key from the php-shim to replicate PHP's key-canonicalization rule and unignore test_mixed_formats.
2026-07-20test(show-command): ignore test_self over UTC-only date() timezone gapnsfisis
The shim date() renders in UTC only (no timezone database) while PHP's date() uses the system default timezone, so get_relative_time misses the "today" match and prints "this week" whenever the local date differs from the UTC date (daily 00:00-09:00 JST on this machine). Verified by running the test with and without TZ=UTC at 07:40 JST. Mark the gap with a TODO(phase-c) in the shim; fixing it needs a timezone database (a new crate), which is a user decision. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18perf(regex): eliminate per-call clone overhead in preg_* dispatchnsfisis
regex::Regex::clone() does not share the underlying meta engine's search-cache pool, so every fresh clone pays a ~10us warmup cost on its first use. Two changes together eliminate this across nearly all preg_* call sites: - A php_regex! macro resolves PHP-style patterns to a per-call-site &'static regex::Regex (via regex-macro's LazyLock), applied at the majority of call sites throughout the codebase. - Call sites still passing dynamic pattern strings go through PATTERN_CACHE, which now stores Arc<(Regex, bool)> and hands out Arc::clone()s instead of cloning the Regex itself. PregPattern::resolve() returns a ResolvedPattern enum (Arc or 'static reference) rather than an owned Regex, so neither path ever clones the Regex proper. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11chore: use fully-qualified name for Rc/RefCellnsfisis
2026-07-05feat(remote-filesystem): support file:// URLs in get_remote_contentsnsfisis
get_remote_contents was a full stub always returning None, so any file:// download raised a TransportException. Read local files directly for the file scheme, mirroring PHP's file_get_contents transparently handling the file:// stream wrapper. Also fixes file_get_contents5 to strip the file:// prefix like the 0-arg variant already did.
2026-07-05perf(pcre): cache compiled regex patterns across preg_* callsnsfisis
Composer's classmap generator re-issues the same PHP-derived pattern string for every scanned file (and every token within it), relying on PCRE's built-in compiled-pattern cache to make that free. shirabe had no equivalent, so every Preg::* call recompiled the pattern from scratch via regex::Regex::new(), making `composer create-project` autoload generation ~130x slower than Composer on a fresh laravel/laravel install (130s vs ~1s). Memoizing compiled patterns by their raw string in compile_php_pattern closes that gap. 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-07-02chore(lint): ban std::io::Read/Write, Any, Command use importsnsfisis
Extends no_banned_use to cover std::any::Any, std::io::Read/Write, and std::process::Command, and teaches the linter to allow `as _` imports so trait methods can still be brought into scope without binding the banned name. Fully qualifies all existing usages across the codebase.
2026-06-29feat(php-shim): implement DirectoryIterator in fs shimnsfisis
Give DirectoryIteratorEntry a backing path (mirroring RecursiveIteratorFileInfo) and make directory_iterator enumerate entries, returning Result to match PHP DirectoryIterator's UnexpectedValueException on an unopenable directory. Wire the new Result through DumpCompletionCommand's get_supported_shells. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28chore: add comment to PhpMixednsfisis
2026-06-28fix(php-shim): emit NUL bytes as concatenation in var_exportnsfisis
PHP var_export breaks NUL bytes out of single-quoted string literals as `' . "\0" . '` because a raw NUL byte is invalid in PHP source. The shim embedded the raw byte instead, diverging from Composer's generated installed.php (where references can contain NUL). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27refactor(http): present reqwest instead of faking curl_versionnsfisis
- StreamContextFactory: User-Agent reports the HTTP stack as "reqwest" - RequestProxy::supports_secure_proxy: always true (reqwest+rustls can always TLS to a proxy); drop the now-dead curl<7.52 guard in get_curl_options - DiagnoseCommand::get_curl_version: phase-D TODO placeholder Empirically verified: reqwest sends no default User-Agent (shirabe sets it explicitly) and accepts https:// proxy URLs. init+install output stays byte-identical to Composer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27fix(php-shim): encode empty PhpMixed::Array as []nsfisis
PHP arrays do not distinguish an empty map from an empty list, and json_encode([]) always emits []. The Serialize impl emitted {} for an empty associative array, which diverged from Composer when computing the lock file content-hash for a composer.json with an empty require. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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-27refactor: fix compiler warnings and clippy warningsnsfisis
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-26test: recover binary_installer full-compat port; implement posix_getpwuidnsfisis
The stashed binary_installer port was blocked on posix_getpwuid (reached via Platform::is_virtual_box_guest); implement it via getpwuid(3) extern "C". The 4 install-and-exec tests now pass with the stream I/O and Process cwd fixes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26test: port 32 command/repository/downloader testsnsfisis
Add create_installed_json/create_composer_lock test helpers. Port command (8), repository path/forgejo/perforce/vcs (11), and fossil/hg/download_manager (13) tests. Fix production porting bugs: root_package_loader/forgejo_url/version_bumper regex delimiters, repository_manager create_repository_by_class, array_loader isset, licenses_command RefCell borrow; implement disk_free_space and touch2/touch3 via libc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26feat(shim): implement stream_select/stream_set_blocking via libcnsfisis
Add fcntl/select extern "C" declarations and a PhpResource::raw_fd seam so the symfony Process pipe loop can read a live child's stdout/stderr. Fix fread on a non-blocking fd (WouldBlock -> "") and the Process null-cwd default. Real subprocess output capture now works; un-ignore the process_executor tests. 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 59 autoload/vcs/installer/util/command tests; fix output capturensfisis
Port autoload_generator (24), bitbucket (14), suggested_packages (11), git_driver (6), archive_manager (3), and a bump command test. Fix the ApplicationTester output-capture root cause (php://memory streams must be readable regardless of fopen mode). Implement posix_getuid/geteuid, the PCRE 'A' anchored modifier, php_strip_whitespace, stream_get_wrappers, is_callable scalars; fix preg_quote angle-bracket escaping and class-map parser regexes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26test: port 35 auth/installer/io/zip/bitbucket tests; implement date_creatensfisis
Port auth_helper (14), library_installer (8), console_io (7), zip_downloader (3), git_bitbucket_driver (3) tests. Implement date_create/strtotime for the ISO8601/ RFC3339/relative formats Composer uses (unknown input -> None, no silent guess). Fix production bugs: Question::is_assoc list-vs-assoc, auth_helper gitlab-domains list handling, LibraryInstaller RefCell double-borrow, ZipArchive::extract_to ErrorException propagation. 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-26test: port 44 vcs/downloader/version tests using mock infransfisis
Port git, version_guesser, gitlab_driver, github_driver, and git_downloader tests using the ProcessExecutor/HttpDownloader mocks and IO/Config stubs. Fix production regex-porting bugs surfaced by the now-reachable paths: Url::sanitize and Response::find_header_value had non-delimited PCRE patterns; implement array_search_mixed non-strict branch and a datetime format mapping. Add HttpDownloader::__new_mock so mocked downloaders skip curl construction. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25feat(external-packages,shim): implement impl todos across componentsnsfisis
Port seld/jsonlint JsonParser (+hand-written Lexer), unblocking 10 json_file parse-error tests verified byte-for-byte against PHP. Implement Symfony Finder SplFileInfo, executable finders, String classes (byte/code-point/unicode), ZipArchive shim (via the zip crate), SPDX license validation, and shim date/stream functions. Genuinely-blocked sites (reflection, PHP runtime constants, non-UTF-8 transcoding, recursive PCRE) stay todo!() with reasons. 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-25fix(preg): treat escaped \$ in replacement as a literal dollar signnsfisis
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25feat(json): validate JSON schema via the jsonschema cratensfisis
Replace the todo!() json_schema::Validator stub with the jsonschema crate. Errors are surfaced as 'property : message'; the message wording follows the jsonschema crate and is *not* justinrainbow-compatible. Port the ComposerSchemaTest and JsonFileTest schema cases to the new wording (noted per test). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25feat(php-shim): model $_ENV/$_SERVER as OsString snapshotsnsfisis
Rework the environment shim around getenv/putenv on the real environment and $_ENV/$_SERVER as startup snapshots, all over OsString. Migrate every caller off the old server()/server_argv() helpers and force the snapshots in main() before any putenv() runs. Document the porting rules in docs/dev/env-vars-porting.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24feat(php-shim): implement non-strict in_array with PHP loose comparisonnsfisis
Replace the deferred todo!() path with loose_eq, implementing PHP's == semantics for in_array's non-strict mode (numeric-string-aware comparison, bool/null coercion, recursive array comparison).
2026-06-24feat(php-shim): support JSON_PRETTY_PRINT in json_encode_exnsfisis
PHP's JSON_PRETTY_PRINT uses a 4-space indent, so use serde_json's PrettyFormatter with a 4-space indent when the flag is set.
2026-06-24refactor(php-shim): remove is_resource/is_resource_valuensfisis
2026-06-24feat(process): redesign proc_* on PhpResource process handlesnsfisis
proc_open/proc_close/proc_get_status/proc_terminate represented the process handle as a PhpMixed, which cannot hold a live child process or its pipes, so they were stubs or todo!(). Model the handle as a new PhpResource::Process variant and child pipes as a StreamBacking::Pipe, with a native Descriptor enum for descriptorspec; proc_open now returns io::Result and fills pipes as IndexMap<i64, PhpResource>. Rewire the Symfony Process pipes and the Console terminal/cursor onto the new types, removing the "PhpMixed cannot carry a PhpResource" todo!()s. The remaining todo!()s are genuine syscall leaves (proc_terminate signal delivery, stream_select, stream_set_blocking, posix_kill, pty, fd>=3) left unimplemented since no syscall crate is introduced. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24test: port more unimplemented testsnsfisis
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>