SourcePath
The shared realpath boundary for the operations that return a file's content by path: line reads, source pages, and `test`. One question answers for all of them (greenhouse decisions/0618): **which house is this path from, and is it that house's source or its state?** The rules are asked about the file each path resolves to, not about how the path was spelled, so two spellings of one file get one answer (greenhouse decisions/0618). {@see canonical()} walks from the root down and, at each level, asks the directory which entry the component actually IS — by device + inode — and takes that entry's STORED name; the state and test rules read that canonical path, matching the segment case-insensitively, the same way the secret rule does. When a directory on the way cannot be listed the path cannot be canonicalised: the answer then fails CLOSED (state, not source; not a test), because a file whose place in the house cannot be verified is not handed out as source.
SourcePath::inside()
public static function inside(string $root, string $path): ?stringResolve a regular file strictly inside the host, after resolving links and traversal.
Parameters
| Name | Type | Description |
|---|---|---|
| $root | string | |
| $path | string |
SourcePath::relative()
public static function relative(string $file, string $root): stringA stable relative name for a file already admitted by inside() — the caller's spelling, unfolded.
Parameters
| Name | Type | Description |
|---|---|---|
| $file | string | |
| $root | string |
SourcePath::canonical()
public static function canonical(string $file, string $root): ?stringThe path of $file relative to $root with the STORED name of every segment — the name the directory actually holds, found by identity (device + inode), not by how the caller spelled it. Returns null when a directory on the way cannot be listed (so the path's identity cannot be established) or the file is outside the root.
Parameters
| Name | Type | Description |
|---|---|---|
| $file | string | |
| $root | string |
SourcePath::isHouseState()
public static function isHouseState(string $file, string $root): boolWhether a file already admitted by {@see inside()} is the STATE of the house it belongs to (greenhouse decisions/0618, D20), judged over its canonical path and matched case-insensitively — the same comparison the secret rule uses. A file inside a COPY of the house is judged against the copy's root. A path that cannot be canonicalised is treated as state (the safe direction).
Parameters
| Name | Type | Description |
|---|---|---|
| $file | string | |
| $root | string |
SourcePath::isTest()
public static function isTest(string $file, string $root): boolWhether a file already admitted by {@see inside()} is a TEST of the house it belongs to — a `.php` file or a directory under that house's (or a copy's) `tests/` (greenhouse decisions/0618, D22), judged over its canonical path, case-insensitively. Any other path is refused; a path that cannot be canonicalised is not a test.
Parameters
| Name | Type | Description |
|---|---|---|
| $file | string | |
| $root | string |
SourcePath::matchByIdentity()
public static function matchByIdentity(array $entries, int $dev, int $ino): ?stringThe name of the entry whose device AND inode equal ($dev, $ino), or null if none — the one place the identity comparison lives, so a test can pin that both numbers count (not the inode alone) without a second disk. The name is cast to string because a decimal-integer directory name must not come back as an int (F-NUM).
Parameters
| Name | Type | Description |
|---|---|---|
| $entries | list<array{0: string, 1: int, 2: int}> | name, device, inode |
| $dev | int | |
| $ino | int |