PHPackages                             nandan108/prop-path - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. nandan108/prop-path

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

nandan108/prop-path
===================

A composable, JSONPath-inspired query engine for deep property access in PHP

v0.4.1(2mo ago)02031MITPHPPHP ^8.1CI passing

Since Jun 29Pushed 2mo agoCompare

[ Source](https://github.com/Nandan108/prop-path)[ Packagist](https://packagist.org/packages/nandan108/prop-path)[ RSS](/packages/nandan108-prop-path/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (4)Dependencies (6)Versions (8)Used By (1)

PropPath
========

[](#proppath)

[![CI](https://github.com/nandan108/prop-path/actions/workflows/ci.yml/badge.svg)](https://github.com/nandan108/prop-path/actions/workflows/ci.yml/badge.svg)[![Coverage](https://camo.githubusercontent.com/2916d9073e6dc6575dc838040d64baedae2c1e590cf1f3255e4f63d39e98cffc/68747470733a2f2f636f6465636f762e696f2f67682f6e616e64616e3130382f70726f702d706174682f6272616e63682f6d61696e2f67726170682f62616467652e737667)](https://camo.githubusercontent.com/2916d9073e6dc6575dc838040d64baedae2c1e590cf1f3255e4f63d39e98cffc/68747470733a2f2f636f6465636f762e696f2f67682f6e616e64616e3130382f70726f702d706174682f6272616e63682f6d61696e2f67726170682f62616467652e737667)[![Style](https://camo.githubusercontent.com/77a781eb39a9e5a05b2ca0fb24ed710d9ba4e063d4077b5a622535447c5d4790/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7374796c652d7068702d2d63732d2d66697865722d627269676874677265656e)](https://camo.githubusercontent.com/77a781eb39a9e5a05b2ca0fb24ed710d9ba4e063d4077b5a622535447c5d4790/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7374796c652d7068702d2d63732d2d66697865722d627269676874677265656e)[![Packagist](https://camo.githubusercontent.com/b2d52e319d4eeb238c679541153d9dd8d4e8965d4a1c94fe5c3be6534b8015d2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6e616e64616e3130382f70726f702d70617468)](https://camo.githubusercontent.com/b2d52e319d4eeb238c679541153d9dd8d4e8965d4a1c94fe5c3be6534b8015d2/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6e616e64616e3130382f70726f702d70617468)[![Psalm Level](https://camo.githubusercontent.com/b34062f64bef150d261a2e2e10feac619bc4ea3bd5cb86ab084d726e9e9fc4de/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7073616c6d2d6c6576656c2d2d312d627269676874677265656e)](https://psalm.dev/)

> **TL;DR** PropPath is a powerful query engine for PHP that lets you extract deeply nested values from arrays and objects using concise, expressive syntax. Inspired by JSONPath but tailored for modern PHP, it supports recursive traversal, multi-key mapping, fallback resolution, bracket grouping, and structured mode — all compiled into fast, reusable closures.

---

What is PropPath?
-----------------

[](#what-is-proppath)

**PropPath** is a feature-rich, extensible query engine for extracting values from complex PHP object graphs, arrays, or a mix of both. Inspired by JSONPath and built for modern PHP codebases, PropPath compiles string-based or structured path expressions into efficient extractor closures that traverse nested structures using a powerful set of operators.

It powers advanced features in the [DTO Toolkit](https://packagist.org/packages/nandan108/dto-toolkit), enabling concise and expressive mapping between input payloads and strongly typed DTOs.

---

### Use cases and philosophy

[](#use-cases-and-philosophy)

PropPath is designed for:

- **Structured data extraction** from deeply nested objects or mixed arrays
- **Declarative field mapping** in DTO systems, data transformation layers, or form normalizers
- **Reusable compiled resolvers**, allowing precompiled paths to be cached or reused

It follows a few guiding principles:

- **Minimalism**: Do one thing well — extract values, not transform or mutate them.
- **Expressive and powerful**: Supports structured extraction, shallow and recursive wildcards, multi-key mapping, fallback resolution, flattening, and more.
- **Clarity over magic**: Although expressive, the syntax is designed to be predictable and consistent.

---

📦 Installation
--------------

[](#-installation)

```
composer require nandan108/prop-path
```

- Requires **PHP 8.1+**
- Only *one* runtime dependency: [nandan108/prop-access](https://github.com/nandan108/prop-access)

---

🚀 Quick Start
-------------

[](#-quick-start)

PropPath compiles a path string (or structured array) into an extractor closure:

```
use Nandan108\PropPath\PropPath;
$roots = ['dto' => ['user' => ['email' => 'jane@example.com']]];
$extractor = PropPath::extract('$dto.user.email', $roots);
// $email === 'jane@example.com'
```

The compiled closure takes an associative array of **roots** as its argument.

### 📚 Nested Example

[](#-nested-example)

```
$data = [
    'dto' => [
        'user' => [
            'name' => 'Jane',
            'email' => 'jane@example.com',
            'addresses' => [
                'home' => ['city' => 'Geneva', 'zip' => '1201'],
                'office' => ['city' => 'Vernier', 'zip' => '1214', 'phone' => '1234'],
            ],
        ],
    ],
    'context' => ['request' => ['search' => 'foo']],
];

$extractor = PropPath::compile('$dto.user.addresses.home.city');
$homeCity = $extractor($data); // 'Geneva'

// direct extraction:
PropPath::extract('$dto.user["homeCity" => addresses.home.city]', $data);
// ['homeCity' => 'Geneva']

PropPath::extract('user[
    // extract map of zip codes by city name from addresses
    "zips"  => addresses[*[city => zip]]@~,
    // get value of first "phone" field anywhere in structure,
    // (default to "no phone")
    "phone" => [**phone ?? "no phone"],
    "fax"   => [**fax ?? "no fax"],
    // grab "search" => request.search from $context (not default root)
    $context.request.@search
]', $data);
// $result === [
//     'zips' => ['Geneva' => '1201', 'Vernier' => 1214],
//     'phone' => '1234',
//     'fax' => 'no fax',
//     'search' => 'foo',
// ];
```

---

🧩 Syntax Reference
------------------

[](#-syntax-reference)

Find the full syntax reference at [docs/Syntax.md](docs/Syntax.md)

---

🧵 Structured Mode
-----------------

[](#-structured-mode)

Instead of a single path string, you can pass an array structure:

```
$roots = ['root' => ['path' => ['a', 'b', 'c']]];
$result = PropPath::extract(['foo' => 'path.0', ['path.1', ['path.2']]], $roots);
// ['foo' => 'a', ['b', ['c']]]
```

This allows you to mirror a desired shape without building complex bracket paths.

---

🧠 How It Compares to JSONPath
-----------------------------

[](#-how-it-compares-to-jsonpath)

PropPath is inspired by JSONPath but diverges where needed for better PHP ergonomics:

FeatureJSONPathPropPathRoot marker`$``$`, `$dto`, `$context`, etc.Wildcard`*``*`, with depth controlRecursive descent`..``**`Filters✅🔸 Not supported (may be added later via `symfony/expression-language`)Multi-key extraction❌✅ `[foo, bar]` or `['x' => path]`Fallback resolution (`??`)❌✅Array flattening❌✅ `~`, `@~`Structured input mode❌✅> 🧠 **Container-agnostic access**JSONPath uses different syntax to access objects vs arrays. PropPath does not. It uses a unified syntax for all container types (arrays, objects, or `ArrayAccess`).
>
> Brackets in PropPath do **not** indicate container type — they serve to:
>
> 1. Build arrays from multiple values
> 2. Group expressions for correct evaluation order For example, `foo.*.bar.0` applies `.0` per item. `[foo.*.bar].0` applies `.0` to the overall result.

---

📌 Performance Notes
-------------------

[](#-performance-notes)

PropPath compiles each path into a memoized closure using an `xxh3`-based hash. Structured and recursive queries (`**`) may be slower; typical paths are fast and safe to cache.

---

⚙️ Tooling + Integration
------------------------

[](#️-tooling--integration)

- PropPath depends only on [`nandan108/prop-access`](https://github.com/nandan108/prop-access)
- Integrates with [`dto-toolkit`](https://packagist.org/packages/nandan108/dto-toolkit)
- Easily pluggable with Laravel, Symfony, or standalone projects

---

🛠 API Reference
---------------

[](#-api-reference)

```
PropPath::compile(string|array $paths, ...): \Closure
PropPath::extract(string|array $paths, array $roots, ...): mixed
PropPath::clearCache(): void
PropPath::boot(): void
```

### Evaluation Failures

[](#evaluation-failures)

`PropPath::compile()` returns an extractor with this shape:

```
fn (array $roots, ?Closure $failEvalWith = null): mixed
```

Custom evaluation handlers receive a failure snapshot as second argument:

```
use Nandan108\PropPath\Support\EvaluationFailureDetails;

$extractor = PropPath::compile('!user.email');

$value = $extractor($roots, function (string $message, EvaluationFailureDetails $failure): never {
    // Example: machine-readable code + normalized path
    throw new RuntimeException($failure->code->value.' at '.$failure->getPropertyPath());
});
```

When using the default handler, `EvaluationError` exposes machine-readable fields:

```
try {
    PropPath::extract('!missing', ['value' => []]);
} catch (\Nandan108\PropPath\Exception\EvaluationError $e) {
    $code = $e->getErrorCode();               // EvaluationErrorCode enum
    $params = $e->getMessageParameters();     // includes "errorCode", often "key"/"containerType"
    $path = $e->getPropertyPath();            // e.g. "$value.missing"
    $debug = $e->getDebugInfoMap();
}
```

---

✅ Quality
---------

[](#-quality)

- 100% test coverage
- Psalm: level 1 (the strictest)
- Code style enforced with [PHP-CS-Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer):
    - Based on the `@Symfony` rule set
    - Aligned `=>` for better readability
    - Disallows implicit loose comparisons (`==`, `!=`)

---

📄 License and Attribution
-------------------------

[](#-license-and-attribution)

MIT License © [nandan108](https://github.com/nandan108)Author: Samuel de Rougemont

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance84

Actively maintained with recent releases

Popularity11

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity40

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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 ~39 days

Recently: every ~58 days

Total

7

Last Release

85d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/024c3beb5cbe22cd3e3f3db6938cf893c09d0b3b90076e2e1566530cd0693372?d=identicon)[Nandan108](/maintainers/Nandan108)

---

Top Contributors

[![Nandan108](https://avatars.githubusercontent.com/u/354944?v=4)](https://github.com/Nandan108 "Nandan108 (11 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/nandan108-prop-path/health.svg)

```
[![Health](https://phpackages.com/badges/nandan108-prop-path/health.svg)](https://phpackages.com/packages/nandan108-prop-path)
```

###  Alternatives

[diesdasdigital/kirby-meta-knight

Meta Knight – SEO for Kirby

11025.9k](/packages/diesdasdigital-kirby-meta-knight)

PHPackages © 2026

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