[PHP] Mount parent directory for file symlinks so __DIR__ works#3377
Merged
[PHP] Mount parent directory for file symlinks so __DIR__ works#3377
Conversation
When a symlink points to a file, NODEFS previously mounted only that single file. PHP's __DIR__ would then resolve to the parent path, which was just empty MEMFS scaffolding – so require() calls to sibling files (e.g. wp-includes/version.php from wp-load.php) failed. Now, file symlinks mount the parent directory instead. The readlink return path is unchanged, but the NODEFS mount covers the whole directory, making siblings accessible through __DIR__.
Contributor
There was a problem hiding this comment.
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
When the readlink interceptor resolves a symlink, it mounts the target into the VFS under /internal/symlinks/. Previously, file symlinks mounted only the individual file, and directory symlinks mounted only the target directory. This meant that upward traversal from __DIR__ (e.g. __DIR__ . '/../../') would land in empty MEMFS scaffolding. Now, the first symlink resolution mounts the host filesystem root at /internal/symlinks via NODEFS. This makes the entire host tree accessible under that prefix, so any depth of ../ traversal works. Subsequent symlink resolutions reuse the existing mount. The scaffolding logic (mkdirTree + writeFile) still runs on the first call to create /internal/symlinks as a mount point. The NODEFS mount then shadows it. On later calls the target file is found through NODEFS and the scaffolding is skipped.
Mounting the host filesystem root is too risky. Revert to mounting just the symlink target's parent directory, which is enough for __DIR__ sibling access. Add a TODO for the ../../ traversal case.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When a symlink points to a file, the readlink interceptor previously mounted only that single file via NODEFS. PHP's
__DIR__inside the mounted file would resolve to its parent path — which was just empty MEMFS scaffolding created bymkdirTree. Anyrequire()call to a sibling file would fail.This is exactly what happens when WordPress boots through a symlinked
wp-load.php: the file itself loads fine, butrequire_once(__DIR__ . '/wp-includes/version.php')fails because the parent directory is empty.The fix mounts the parent directory instead of just the file. The
readlinkreturn path doesn't change — it still points to the specific file under/internal/symlinks/. The only difference is that the NODEFS mount now covers the whole directory, so sibling files are accessible through__DIR__.Test plan
scandir()on the parent directory of a resolved file symlink returns sibling files, confirming__DIR__would work correctly inside a symlinked PHP file