northrook / pathfinder
Resolve paths for files and directories at runtime using predefined key-value pairs
Requires
- php: >=8.5
- northrook/core-contracts: dev-main
- northrook/php-filesystem: dev-main
- northrook/php-generator: dev-main
Requires (Dev)
- northrook/php-cs: dev-main
- northrook/php-debug: dev-main
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^13
This package is auto-updated.
Last update: 2026-08-20 07:28:13 UTC
README
Resolves configured parameter references into filesystem paths (Northrook\Filesystem\Path) and hyperlink references (Northrook\Reference\Href).
Implements Northrook\PathfinderInterface.
Install
composer require northrook/pathfinder
Requires PHP 8.5+, northrook/core-contracts, and northrook/php-filesystem.
Quick start
use Northrook\Pathfinder;
$pathfinder = new Pathfinder([
'project.dir' => '/srv/app',
'url.base' => 'https://example.test',
'path.cache' => '{project.dir}/var/cache',
'url.assets' => '{url.base}/assets',
]);
$pathfinder->getPath('{path.cache}/items.json'); // Path → /srv/app/var/cache/items.json
$pathfinder->getHref('{url.assets}/app.css'); // Href → https://example.test/assets/app.css
$pathfinder->getHref('resources/icon.svg'); // Href → resources/icon.svg
$pathfinder->getPath('robots.txt'); // Path → robots.txt (passthrough)
References
| Form | Meaning |
|---|---|
{key} | Expanded parameter value |
{key}/relative/suffix | Value + joined suffix (URL suffixes insert before ? / #) |
{key}/…/{other} | Leading key joined as above; further {…} in the suffix expanded |
…/{key}/… | Any {key} in a non-leading form expanded left-to-right |
Bare / absolute string (no {…}) | Passed through unchanged, then type-checked |
Parameters always use braces. A bare dotted name like path.cache is a literal string — not a parameter lookup. There is no implicit join onto project.dir or url.base.
Parameter values may contain any number of {nested.key} placeholders (and literal %). Nested unknown keys inside configured values throw; unknown keys at the call site (any placeholder in the reference) return null.
Path vs Href
| Call | Resolved shape is an href | Resolved shape is a filesystem path |
|---|---|---|
getPath | InvalidArgumentException | Path |
getHref | Href | InvalidArgumentException |
Href accepts safe relative references, path-absolute references, fragments, query-only references, protocol-relative links, and allowed schemes such as http, https, mailto, tel, and sms.
“Filesystem path” for getHref means Windows drive / UNC shapes (C:\…, \\server\share). POSIX absolute strings like /assets/app.css are valid hrefs.
Non-existent filesystem targets may still yield a Path — existence is checked on the object (exists(), etc.).
Null vs throw
| Outcome | When |
|---|---|
null | Missing / unresolvable reference (unknown {key}, empty string) |
| Throw | Cycles, empty/{} or invalid placeholders, unbalanced {, wrong {key} remainder, path/URL shape mismatch |
Href is an attribute-safe reference value object. Promote it with asUrl() when you need Url transport or structural helpers.
Constructor
use Northrook\Pathfinder\PathfinderConfig;
new Pathfinder(
array $parameters = [],
?PathfinderConfig $config = null,
?FilesystemInterface $filesystem = null,
?LoggerInterface $logger = null,
);
new PathfinderConfig(
bool $preload = false, // call preload() in the constructor
bool|string $useCache = false, // false = off; true = {cacheParameter} or get_temp_path('pathfinder.cache'); string = path
bool $commitOnDestruct = true,
string $cacheParameter = 'path.pathfinder_cache',
);
Defaults live on PathfinderConfig::DEFAULTS and can be passed (or merged) into PathfinderConfig::from().
Construction is light unless $preload is true — the cache dump is not loaded until preload(). Call preload() after Autowire (logger, etc.) when you need the dump hydrated before the first resolve. getPath, getHref, commit, and bustCache call preload() automatically if it has not run yet.
Cache
Optional PHP dump-file persistence of resolved parameter values (placeholders already expanded). Speeds up repeated boots by skipping placeholder parsing.
- The dump is keyed by a checksum of the raw constructor map. If that map changes, the dump is ignored and rewritten on the next commit.
- A malformed or unreadable cache file is logged at
criticaland treated as empty — boot continues. commit(bool $force = false): boolexpands every parameter, then writes when the source checksum changed (or always when$force).bustCache(): boolclears the in-memory resolved map and deletes the dump file (truewhen a file was removed).- When
$commitOnDestructis true and a cache file is configured,__destructpreloads (if needed) and commits; failures there are logged, not thrown.commitOnDestructis ignored when caching is off. - When
$useCacheistrue, the path comes from the parameter named by$cacheParameter(defaultpath.pathfinder_cache), elseget_temp_path('pathfinder.cache').
Parameter key names have no inherent path/URL meaning — prefixes like path. or url. are never trusted; only the resolved string value matters.
Path and Href
getPath returns Northrook\Filesystem\Path. getHref returns Northrook\Reference\Href.
Both are immutable value objects from core-contracts. Use Path::join() for filesystem composition. Use Href::asUrl() when you need Url::append() / with*() / mergeQuery() or HTTP helpers.
HTTP operations still live on Url, not Href.
Path
File::write()creates parent directories by default.glob($pattern)runs patterns relative to this path as a directory base. When the path is an existing file, the base isdirname($path).
Href
| Shape | Behavior |
|---|---|
Relative / /absolute | Accepted |
#fragment / ?query | Accepted |
http: / https: | Accepted |
mailto: / tel: / sms: | Accepted |
javascript: / data: | Rejected |
| Windows drive / UNC path | Rejected as filesystem paths |
Parameter keys
Validated via Assert::validKey() with charset alnum, ., _, \, -, :.
Rejected in keys: % and / (suffixes are never part of the key). Values must be non-empty after trim.