ohffs / php-pathlib
Very basic port of pythons pathlib to PHP
Installs: 2
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/ohffs/php-pathlib
Requires
- php: ^8.0
Requires (Dev)
- pestphp/pest: ^4.1
This package is auto-updated.
Last update: 2025-09-21 11:48:24 UTC
README
A tiny, Python pathlib
‑flavoured Path
for PHP that makes path handling and small file ops pleasant and testable.
Installation
composer require ohffs/php-pathlib
TL;DR — why this over raw PHP?
Before (string wrangling + globals):
$base = rtrim(getenv('HOME'), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . 'myapp'; $cfg = $base . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'settings.json'; @mkdir(dirname($cfg), 0777, true); file_put_contents($cfg, json_encode(['debug' => true]));
After (fluent, intention‑revealing):
use Ohffs\PhpPathlib\Path; Path::setAutoExpandTilde(); // optional: call once at bootstrap to expand "~" $cfg = Path::of('~/myapp') ->joinpath('config', 'settings.json'); $cfg->writeText(json_encode(['debug' => true]));
You get consistent separators, easy joins, and readable code you can test safely.
Heads-up: PHP itself doesn’t expand
~
. Either callexpanduser()
before filesystem operations, or enable auto-expansion in your app (see Behaviour details).
Quick start
use Ohffs\PhpPathlib\Path; Path::setAutoExpandTilde(); // optional: expand "~" automatically // Construct & chain $project = Path::of('~/projects/example'); $config = $project->joinpath('config', 'app.json'); $readme = $project->joinpath('README.md'); // Queries $config->exists(); // bool $config->isFile(); // bool $project->isDir(); // bool // Name parts $readme->name(); // "README.md" $readme->stem(); // "README" $readme->suffix(); // "md" (no leading dot) $project->parent(); // Path("~/projects") $project->parts(); // absolute parts after expanduser() // IO $config->writeText('{"debug":true}'); $text = $config->readText(); // Transformations $logDir = Path::of('/var/log')->resolve(); $logFile = $logDir->joinpath('app.log').""; // castable to string $tmpMd = Path::of('report.txt')->withSuffix('md'); // report.md // Globbing (non‑recursive) foreach (Path::of('src')->glob('*.php') as $php) { echo $php->name(), "\n"; }
Fake filesystem for tests
Sandbox all file ops under a disposable temp directory. Absolute logical paths (like /etc/hosts
) are mapped under the fake base, never touching your real FS.
Pest example
use Ohffs\PhpPathlib\Path; beforeEach(function () { $this->fs = Path::fake(); // returns a guard; keep the ref alive }); afterEach(function () { unset($this->fs); // or Path::unfake() }); it('writes without touching the real FS', function () { $p = Path::of('/app/data.txt'); $p->writeText('ok'); expect($p->exists())->toBeTrue(); // logical expect(is_file($this->fs->base().'/app/data.txt'))->toBeTrue(); // actual temp });
Notes
Path::fake()
returns aScopedFake
withbase(): string
and cleans up on__destruct()
.__toString()
always shows the logical path; the temp base is applied only when touching the real filesystem.
Common recipes
Ensure a directory exists and write a file
$dir = Path::of('~/cache/images'); $dir->mkdir(true); // parents=true $dir->joinpath('index.json')->writeText('[]');
List items in a folder
$names = array_map(fn($p) => $p->name(), Path::of('logs')->iterdir());
Change extension safely (dotfiles supported)
Path::of('/a/archive.tar.gz')->withSuffix('zip'); // /a/archive.tar.zip Path::of('/a/.env')->withSuffix('bak'); // /a/.env.bak
Normalize a messy path without requiring it to exist
Path::of('/x/y/../z/./a')->resolve(); // /x/z/a
Behaviour details
-
~
(tilde) expansion: PHP itself doesn’t expand~
. Either callexpanduser()
before filesystem operations, or enable auto-expansion once in your bootstrap:Path::setAutoExpandTilde(true);
. -
Separators: accepts
/
and\\
in inputs; outputs use your OS separator for real FS calls. Logical string helpers normalise sensibly. -
Dotfiles:
.env
/.gitignore
are treated as no extension;suffix()
returns''
,stem()
returns the whole name. -
glob()
: non‑recursive; uses PHP’sglob()
under the hood; returnsPath[]
with logical paths (no temp prefixes). -
resolve()
: if the actual path exists, usesrealpath()
; otherwise performs a pure string normalisation (.
/..
). -
Safety: in fake mode, even logical absolutes are sandboxed under the temp base.
API reference (essentials)
Construction
Path::of(string $path): Path
Query
exists(): bool
isFile(): bool
isDir(): bool
isAbsolute(): bool
name(): string
stem(): string
suffix(): string
(no leading dot)parent(): Path
parts(): string[]
Transform
joinpath(string ...$parts): Path
withSuffix(string $suffix): Path
withName(string $name): Path
expanduser(): Path
resolve(): Path
Filesystem ops
readText(): string
writeText(string $content): void
mkdir(bool $parents = false, int $mode = 0777): void
iterdir(): Path[]
glob(?string $pattern = null): Path[]
Testing helpers
Path::fake(): ScopedFake
(returns guard withbase()
; auto‑cleanup on destruct)Path::unfake(): void
Requirements
- PHP 8.0+ (typed properties & arrow functions)
- No runtime dependencies
License
MIT — see LICENSE
.