aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-php-shim/src/fs.rs
AgeCommit message (Collapse)Author
2026-08-23fix(fs): carry file_get_contents results as bytesnsfisis
file_get_contents() and file_get_contents_with_max_length() return Vec<u8> instead of a from_utf8_lossy'd String. Call sites whose consumer takes a &str still convert lossily and are marked TODO(bytes). file_get_contents_with_max_length() now reads at most the requested number of bytes instead of the whole file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16fix(php-shim): fail RecursiveIteratorFileInfo::get_size on a failed statnsfisis
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>
2026-08-16refactor(php-shim): give fstat and lstat a typed FileStat resultnsfisis
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>
2026-08-15refactor(php-shim): drop the unused opendir directory handlensfisis
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>
2026-08-15docs(php-shim): state the ignored file I/O flags as a contractnsfisis
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>
2026-08-15docs(php-shim): drop the inaccurate fileperms TODOnsfisis
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>
2026-08-12docs(todo): retag TODO markers by root causensfisis
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10fix(php-shim): return an error from fileperms instead of 0nsfisis
fileperms swallowed metadata errors and reported a mode of 0, which the callers could not tell apart from a real (if implausible) mode: the zip archiver stored entries with no permission bits, and Filesystem::copy chmod-ed the target to 0. It now returns Result<u32, io::Error> so both callers propagate the failure. The switch to u32 also drops the casts around the mode arithmetic and ZipArchive's entry attributes. 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(path-downloader): restrict a mirrored path package to its archivable filesnsfisis
PathDownloader builds an ArchivableFilesFinder and hands it to Filesystem::mirror as the iterator that selects what to copy, so a path package installed by mirroring drops what .gitattributes marks export-ignore, what .gitignore excludes and the VCS directories. The port dropped the argument, and mirrored the source tree whole. Every method mirror() calls on the entries it walks is derived from the pathname, so the iterator is modeled as a list of pathnames rather than as a Traversable of SplFileInfo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09feat(symfony-filesystem): finish the Filesystem port and trim its APInsfisis
doRemove() renames a directory to a random hidden name before emptying it, and undoes that rename when the final rmdir fails, so a concurrent process cannot recreate the path mid-removal. It also walks one level at a time through FilesystemIterator instead of flattening the whole tree, and lets an inner rmdir failure pass, both as upstream does. copy() keeps the mode fopen($targetFile, 'w') would have left rather than the origin's, symlink() and mirror() call readlink() and getLinkTarget() where upstream does, and a directory iterator that cannot be opened propagates its UnexpectedValueException instead of being swallowed or flattened into an IOException. Error message text is out of scope per docs/known-incompatibilities.md, so the three TODO(phase-c) markers that only tracked wording are gone, along with linkException()'s Windows-only branch. So are the arguments no caller varies -- symlink()'s copyOnWindows, mirror()'s iterator and options, copy()'s overwriteNewerFiles -- which removes the last TODO(phase-c) in the file. New shim functions: readlink, filesystem_iterator, stream_is_local, strrev and SplFileInfo::getLinkTarget. base64_encode takes bytes so random_bytes() can feed it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09refactor(php-shim): make the fs mutators return Resultnsfisis
mkdir, rmdir, unlink and symlink each had a bool version and a _result twin returning the io::Error, which left two names for one call. Keep only the Result form and let the callers that want a boolean spell out .is_ok(). Call sites that discard the outcome, as their PHP originals do, are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09feat(symfony-filesystem): append the failure reason to IOExceptionsnsfisis
Symfony wraps its native filesystem calls in box(), which captures the warning text of a failed call into self::$lastError and appends it to the IOException message. The port dropped that text because the shim's mkdir, rmdir, unlink and symlink only return PHP's bare false. Give each of those a Result-returning variant carrying the io::Error, and use it to restore the appended reason. That also makes two conditions portable: doRemove()'s "Permission denied" test, and linkException()'s Windows error code 1314 special case. 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-09refactor(env): split and join PATH lists with std::envnsfisis
The shim's PATH_SEPARATOR was hardcoded to ":", so every PATH list was split and joined on the wrong character off Unix. std::env::split_paths and join_paths use the platform's separator, which also lets the bin-dir membership test in EventDispatcher compare list entries instead of regex-matching the raw PATH string. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09refactor(php-shim): use std::path::MAIN_SEPARATOR over a shim constantnsfisis
The shim's DIRECTORY_SEPARATOR was hardcoded to "/", so every ported `'\\' === DIRECTORY_SEPARATOR` check compared against a constant that does not track the target platform. std::path::MAIN_SEPARATOR and MAIN_SEPARATOR_STR carry the same meaning as PHP's constant and resolve per platform, so the Windows branches are selected on Windows targets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08feat(php-shim): give ported exceptions PHP's class hierarchynsfisis
Ported exceptions were flat structs reached with `downcast_ref`, so Composer's `catch (\RuntimeException $e)` only matched the exact leaf type and `get_class($e)` had nothing to report. Each exception now embeds an instance of the class it extends and travels inside an `AnyThrowable`; `Catch::catch`/`catch_mut` walk that chain, and `PhpClass::php_class_name` yields the PHP FQCN. Dropping the `std::error::Error` impls from the exception types leaves `AnyThrowable` as the only route into an `anyhow::Error`, so the walk cannot be bypassed. A `no_exception_downcast` linter catches the `downcast::<X>()` calls that would now silently answer `None`. Three sites change behavior as a result: the `TransportException` exit-code override reaches `MaxFileSizeExceededException`, the `catch (\LogicException)` in findSimilar() reaches its subclasses, and rendered exception titles carry the real class name rather than a guess. `get_class_err()` is no longer a `todo!()`, which re-enables FilesystemRepositoryTest::testCorruptedRepositoryFile. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06refactor(php-shim): take &str and return String from pathinfonsfisis
pathinfo only ever receives a string and only ever returns one for the single-component options it supports, so the PhpMixed wrapping forced every call site to pack and unpack the value again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06fix(symfony-filesystem): restore upstream behavior in five spotsnsfisis
Port the Symfony Filesystem branches this file had left out: * copy() preserves the origin mtime via touch() * exists() rejects paths longer than PHP_MAXPATHLEN - 2, so its return type becomes anyhow::Result<bool> * doRemove()'s symlink branch keeps the DIRECTORY_SEPARATOR disjunct, which stops it from throwing on Unix where upstream never does * symlink() normalizes separators and mirrors instead of linking when copyOnWindows is set * mirror() skips entries whose real path is the target directory or was already created earlier in the same call PHP_MAXPATHLEN is new in shirabe-php-shim. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06refactor(php-shim): take filesystem paths as impl AsRef<Path>nsfisis
The shim's filesystem entry points took `&str` even though each one resolves to a local path through `std::fs` or a syscall, so callers holding a `PathBuf` had to stringify it at the call site. They now take `impl AsRef<Path>`, the form `file_exists`, `is_dir`, `unlink` and the rest of the already-converted set use. `Phar`, `PharData` and `ZipArchive` keep their archive path as a `PathBuf`. `PharData::compress` names the compressed sibling by appending the suffix to the file name rather than formatting the path into a `String`. Arguments PHP resolves through a stream wrapper (`fopen`, `file_put_contents`, `include`) still take `&str`, as do the byte-string operations (`dirname`, `basename`, `pathinfo`) and archive-internal entry names, which are `/`-joined logical names rather than OS paths. 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-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-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-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-27refactor: fix compiler warnings and clippy warningsnsfisis
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-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-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-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>
2026-06-22feat(php-shim): implement fopen-family stream API on PhpResourcensfisis
Redesign PhpResource into a real stream handle (File/Memory backing with tracked position, eof, closed state) and unify the whole fopen family (fopen/fwrite/fread/fgets/fgetc/feof/fclose/ftell/fseek/rewind/fstat/ ftruncate/fflush and stream_get_contents/stream_copy_to_stream) on &PhpResource, replacing the split PhpMixed/PhpResource APIs and their todo!() stubs. fopen now returns Result; read functions stay String for now (TODO(phase-e) to move to byte strings). Propagate the signatures through callers: Process stdout/stderr, Cursor input, curl header/body handles (extracted into typed maps keyed by job id), Filesystem copy/safe_copy/files_are_equal, BufferIO, error_handler, platform, perforce, zip. The proc_open pipe paths cannot carry a PhpResource in a PhpMixed list, so they are left as todo!() with notes.
2026-06-22feat(json): resolve bundled Composer schema files via build.rsnsfisis
JsonFile's schema paths previously relied on php_dir() (__DIR__), which has no runtime equivalent. Add a build.rs that copies composer-schema.json and composer-lock-schema.json next to the built executable, and resolve them with std::env::current_exe(). FilesystemRepository now embeds InstalledVersions.php directly via include_str! instead of reading it through php_dir(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21feat(php-shim): implement fs helpers and recursive directory iteratorsnsfisis
Give RecursiveDirectoryIterator/RecursiveIteratorIterator real backing data: the iterator walks the tree in SELF_FIRST/CHILD_FIRST order, yields SplFileInfo-like entries (is_dir/is_file/is_link/get_pathname/get_size), and a Cell cursor lets get_sub_pathname() report the current entry. Implement the std-backed file helpers: lstat, mkdir (umask-aware via DirBuilder), symlink, chmod, fileperms, fileowner, is_executable, unlink_silent, touch (create-if-absent), file()/file_put_contents3/ file_get_contents5, tempnam, umask (via /proc/self/status), and a glob() supporting *, ?, [...] and {..} brace expansion. The PhpMixed-keyed fopen stream family, disk_free_space, touch with an explicit time, opendir and DirectoryIterator remain TODO(phase-d): they need a PhpMixed stream representation or syscalls std does not expose. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21refactor(php-shim): split lib.rsnsfisis