PHPackages                             ohffs/php-pathlib - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. ohffs/php-pathlib

ActiveLibrary

ohffs/php-pathlib
=================

Very basic port of pythons pathlib to PHP

1.0.2(8mo ago)32[1 issues](https://github.com/ohnotnow/php-pathlib/issues)MITPHPPHP ^8.0

Since Sep 21Pushed 8mo agoCompare

[ Source](https://github.com/ohnotnow/php-pathlib)[ Packagist](https://packagist.org/packages/ohffs/php-pathlib)[ RSS](/packages/ohffs-php-pathlib/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (1)Versions (4)Used By (0)

PHP PathLib
===========

[](#php-pathlib)

*A tiny, Python `pathlib`‑flavoured `Path` for PHP that makes path handling and small file ops pleasant and testable.*

Installation
------------

[](#installation)

```
composer require ohffs/php-pathlib
```

TL;DR — why this over raw PHP?
------------------------------

[](#tldr--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 call `expanduser()` before filesystem operations, or enable auto-expansion in your app (see Behaviour details).

---

Quick start
-----------

[](#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
-------------------------

[](#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 a `ScopedFake` with `base(): 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
--------------

[](#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
-----------------

[](#behaviour-details)

- **`~` (tilde) expansion**: PHP itself doesn’t expand `~`. Either call `expanduser()` 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’s `glob()` under the hood; returns `Path[]` with *logical* paths (no temp prefixes).
- **`resolve()`**: if the actual path exists, uses `realpath()`; otherwise performs a pure string normalisation (`.`/`..`).
- **Safety**: in fake mode, even logical absolutes are sandboxed under the temp base.

---

API reference (essentials)
--------------------------

[](#api-reference-essentials)

### Construction

[](#construction)

- `Path::of(string $path): Path`

### Query

[](#query)

- `exists(): bool`
- `isFile(): bool`
- `isDir(): bool`
- `isAbsolute(): bool`
- `name(): string`
- `stem(): string`
- `suffix(): string` *(no leading dot)*
- `parent(): Path`
- `parts(): string[]`

### Transform

[](#transform)

- `joinpath(string ...$parts): Path`
- `withSuffix(string $suffix): Path`
- `withName(string $name): Path`
- `expanduser(): Path`
- `resolve(): Path`

### Filesystem ops

[](#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

[](#testing-helpers)

- `Path::fake(): ScopedFake` *(returns guard with `base()`; auto‑cleanup on destruct)*
- `Path::unfake(): void`

---

Requirements
------------

[](#requirements)

- PHP **8.0+** (typed properties &amp; arrow functions)
- No runtime dependencies

License
-------

[](#license)

MIT — see `LICENSE`.

###  Health Score

28

—

LowBetter than 54% of packages

Maintenance55

Moderate activity, may be stable

Popularity6

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

240d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/5472be37b2cbedfa6c905bb674cb5780378cf2adcfc47befe01ad7bb4d14f734?d=identicon)[ohnotnow](/maintainers/ohnotnow)

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/ohffs-php-pathlib/health.svg)

```
[![Health](https://phpackages.com/badges/ohffs-php-pathlib/health.svg)](https://phpackages.com/packages/ohffs-php-pathlib)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
