ergebnis/json-pointer
RFC 6901 JSON Pointer abstraction for PHP. Create and convert reference tokens and pointers between plain strings, JSON strings, and URI fragment identifiers, handling proper escaping/encoding. Install via Composer: ergebnis/json-pointer.
Installation:
composer require ergebnis/json-pointer
Add to composer.json under require if using Laravel's autoloader.
First Use Case:
Navigate to vendor/ergebnis/json-pointer to inspect the package structure. For quick testing, use the JsonPointer class in a Laravel Tinker session:
use Ergebnis\Json\Pointer;
$pointer = Pointer\JsonPointer::fromJsonString('/user/address');
echo $pointer->toString(); // '/user/address'
Key Classes:
JsonPointer: Core class for working with JSON pointers (RFC 6901).ReferenceToken: Represents individual tokens in a pointer (e.g., /user/address → user, address).Specification: For validating pointers against custom rules.$pointer = Pointer\JsonPointer::fromJsonString('/api/v1/users/123');
#/api/v1/users/123):
$pointer = Pointer\JsonPointer::fromUriFragmentIdentifierString('#/api/v1/users/123');
$tokens = [
Pointer\ReferenceToken::fromString('api'),
Pointer\ReferenceToken::fromString('v1'),
Pointer\ReferenceToken::fromString('users'),
Pointer\ReferenceToken::fromInt(123),
];
$pointer = Pointer\JsonPointer::fromReferenceTokens(...$tokens);
$basePointer = Pointer\JsonPointer::fromJsonString('/user');
$newToken = Pointer\ReferenceToken::fromString('profile');
$extendedPointer = $basePointer->append($newToken);
// Result: '/user/profile'
$pointer = Pointer\JsonPointer::fromJsonString('/user/123/settings');
$tokens = $pointer->getReferenceTokens();
// Returns array of ReferenceToken objects.
$spec = Pointer\Specification::closure(fn($p) => str_starts_with($p->toJsonString(), '/api'));
$spec->isSatisfiedBy($pointer); // true if pointer starts with '/api'
$spec = Pointer\Specification::equals(Pointer\JsonPointer::fromJsonString('/api/v1/users'));
$spec->isSatisfiedBy($pointer); // true if exact match
$spec = Pointer\Specification::anyOf(
Pointer\Specification::equals(Pointer\JsonPointer::fromJsonString('/api/v1/users')),
Pointer\Specification::equals(Pointer\JsonPointer::fromJsonString('/api/v1/products'))
);
JsonPointer to parse query parameters or route segments:
$path = request()->input('path', '/default');
$pointer = Pointer\JsonPointer::fromJsonString($path);
$userPointer = Pointer\JsonPointer::fromJsonString('/users/123');
$tokens = $userPointer->getReferenceTokens();
$userId = (int) $tokens->last()->toString();
$user = User::find($userId);
return response()->json([
'pointer' => $pointer->toJsonString(),
]);
try {
$pointer = Pointer\JsonPointer::fromJsonString('/invalid~pointer');
} catch (Pointer\Exception\InvalidJsonPointer $e) {
report($e); // Log in Laravel
abort(400, 'Invalid JSON pointer');
}
URI Fragment vs. JSON String:
#/user~1name) must be URL-encoded. Use toUriFragmentIdentifierString() for safe encoding:
$pointer = Pointer\JsonPointer::fromJsonString('/user~1name');
$uriFragment = $pointer->toUriFragmentIdentifierString(); // '#/user%7E1name'
fromUriFragmentIdentifierString().Token Escaping:
~, /) in tokens must be escaped in JSON strings:
$token = Pointer\ReferenceToken::fromString('name~value');
$jsonString = $token->toJsonString(); // 'name~0value'
fromJsonString() to parse escaped tokens.Empty Pointers:
JsonPointer::document() returns an empty pointer (''), which refers to the root of a JSON document. Ensure you handle this case when validating or traversing:
$rootPointer = Pointer\JsonPointer::document();
$rootPointer->toJsonString(); // ''
Specification Logic:
Specification::anyOf() short-circuits (stops at the first true match). For exhaustive checks, chain specifications or use Specification::closure() with custom logic.Performance:
Specification objects in hot loops. Reuse them:
$spec = Pointer\Specification::equals($expectedPointer);
foreach ($pointers as $pointer) {
if ($spec->isSatisfiedBy($pointer)) {
// ...
}
}
Inspect Tokens:
Use getReferenceTokens() to debug pointer structure:
$tokens = $pointer->getReferenceTokens();
foreach ($tokens as $token) {
echo $token->toString(), "\n";
}
Validate Input: Sanitize user-provided pointers before processing:
$input = request()->input('pointer');
if (!Pointer\JsonPointer::isValidJsonPointer($input)) {
abort(400, 'Invalid pointer format');
}
Laravel Logging: Log pointer operations for debugging:
\Log::debug('Pointer created', ['pointer' => $pointer->toJsonString()]);
Custom Specifications:
Extend Specification for domain-specific rules:
class ApiVersionSpecification implements Pointer\Specification
{
public function isSatisfiedBy(Pointer\JsonPointer $pointer): bool
{
$tokens = $pointer->getReferenceTokens();
return $tokens->count() >= 2 &&
$tokens->first()->toString() === 'api' &&
$tokens->get(1)->toString() === 'v1';
}
}
Pointer Builders: Create a fluent builder for complex pointers:
class PointerBuilder
{
public static function build(string $path): Pointer\JsonPointer
{
$tokens = explode('/', trim($path, '/'));
return Pointer\JsonPointer::fromReferenceTokens(
...array_map(fn($token) => Pointer\ReferenceToken::fromString($token), $tokens)
);
}
}
Laravel Service Provider: Bind the package to Laravel's container for dependency injection:
// config/app.php
'aliases' => [
'JsonPointer' => Ergebnis\Json\Pointer\JsonPointer::class,
];
Then inject JsonPointer into controllers/services:
public function __construct(private JsonPointer $pointer) {}
Testing:
Use Specification for testing pointer logic:
public function test_pointer_validation()
{
$spec = Pointer\Specification::equals(Pointer\JsonPointer::fromJsonString('/valid/path'));
$this->assertTrue($spec->isSatisfiedBy($pointer));
}
Route Model Binding: Bind pointers to route parameters:
Route::get('/user/{pointer}', function (Pointer\JsonPointer $pointer) {
// $pointer is automatically validated and parsed
});
Requires a custom RouteParameterBinding implementation.
Caching: Cache parsed pointers if used frequently:
$cacheKey = 'pointer_' . md5($jsonString);
$pointer = Cache::remember($cacheKey, now()->addHours(1), fn()
How can I help you explore Laravel packages today?