league/json-reference
A PHP library for resolving JSON References, including JSON Pointers ($ref) in JSON documents and schemas. It dereferences local and remote references through a simple API, with support for custom retrievers and integration with JSON Schema tooling.
What the package is. league/json-reference is a narrow, low-level resolver for JSON Reference (the abandoned draft spec built on RFC 6901 JSON Pointer and RFC 3986 URI resolution). It resolves {"$ref": "..."} nodes against local documents and remote URIs; it does not validate schemas, does not understand OpenAPI semantics, and ships no Laravel integration (no service provider, no config, no facade).
Architecture fit. Weak-to-moderate fit for a Laravel codebase. It is a pure PHP value-object library with a small surface (JsonReference, Reference, Resolver), so it is architecturally clean to embed — but it solves a problem most Laravel applications either do not have or already solve elsewhere. The realistic use cases are: validating inbound webhook payloads against bundled JSON Schemas, linting/validating OpenAPI contracts in CI, and resolving $ref graphs in config-as-data pipelines. In all three, the package is only the plumbing, not the capability — you still need a validator (opis/json-schema, justinrainbow/json-schema) and the validator likely handles $ref itself.
Integration feasibility. Mechanically easy (Composer, PSR-4, no framework coupling), organizationally hard:
league/uri version bounds predate PHP 8.1+, while Laravel 10/11 require it. Expect to need --ignore-platform-reqs, a fork, or a fork-with-patched-constraints — each of which is a maintenance commitment you are making on day one.Technical risk.
$ref URIs means your app makes outbound HTTP requests to arbitrary hosts (http://169.254.169.254/...). In a multi-tenant SaaS this is a first-class security finding, not a footnote.$ref semantics ($id, anchors, dynamic scope) differ materially. Behavior mismatch will be silent and hard to debug.Key questions.
$ref resolution inside OpenAPI/JSON Schema bundles we control? If the latter, this package is the wrong abstraction level.league/openapi-psr7-validator or league/json-guard)? If yes, this is a migration project, not an adoption decision.$ref resolves to the wrong node and a schema check silently passes? If it gates anything financial, contractual, or access-control-related, abandonware is disqualifying.Stack fit. Poor as a direct dependency; acceptable as an internalized dependency behind an owned interface. The correct pattern is an anti-corruption layer:
interface SchemaReferenceResolver {
public function resolve(string $documentUri, string $pointer): array;
}
Bind it in a SchemaServiceProvider, resolve via the container, and never let League\JsonReference\* types appear in application, domain, or test code outside a single adapter file. This makes the eventual replacement a one-file change and lets you swap an archived library for a maintained one without touching call sites.
Migration path. Do not adopt the library standalone. Evaluate in this order:
| Option | When it wins | Trade-off |
|---|---|---|
opis/json-schema v2 |
You need schema validation + $ref; active, PHP 8 native |
Different API; still must sandbox remote refs |
justinrainbow/json-schema |
Legacy compat, Laravel ecosystem familiarity | Older internals, weaker modern-draft support |
Vendor a patched fork of json-reference |
You have an existing transitive dependency and need behavioral parity now | You now own a library; fork drift; SCA exceptions |
Hand-rolled resolver (league/uri + JSON Pointer walk, 150–250 LOC) |
Your $ref surface is bounded (local pointers + a whitelisted base URI) |
Small build cost, total control, no third-party risk |
| Pre-resolution at build time (no runtime resolver) | Contracts are versioned artifacts | Loses runtime flexibility; usually the best answer |
For a Laravel app, my default recommendation is: pre-resolve contracts in CI and ship flattened schemas, then use opis/json-schema at runtime. That removes the resolver from the request path entirely.
Compatibility. Verify before anything else: composer why league/json-reference, composer show --tree, PHP constraint (^7.x era vs Laravel 10/11's ^8.1), and league/uri major-version collision with anything else in the tree. Treat a failed constraint resolution as the decision point — do not paper over it with --ignore-platform-reqs in production; that flag converts a build error into a runtime fatal.
Sequencing.
$ref resolution is needed; classify each as local-only, remote-required, or eliminable. Confirm whether the package is already transitive.SchemaReferenceResolver interface and two implementations: the fork (parity reference) and opis/json-schema (target). Run both against real fixtures — your actual OpenAPI spec, your actual webhook schemas.Decision recommendation. Reject for greenfield adoption. If already present, treat it as tech debt with the 3-week plan above; if not present, do not introduce it — the capability is available from maintained libraries or from ~200 lines you control.
Maintenance. This is the dominant cost and it lands on you, not on a vendor. Archived means: no bug fixes, no PHP 9 readiness, no security advisories, no compatibility patches when league/uri or PSR interfaces move. Budget realistically 2–6 hours per quarter for a fork (constraint bumps, deprecation sweeps) and a full day at each major PHP/Laravel upgrade. Multiply by the number of forks you accumulate — this is how a two-person platform team ends up spending 15% of capacity on dependency archaeology.
Support. Escalation path is effectively "read the source." No upstream maintainer, no issue triage, no CVE feed entry. Your on-call engineer debugging a malformed $ref at 2 a.m. has GitHub and a 32-star repo. That is acceptable for a build-time tool, unacceptable for anything on the request path of a customer-facing endpoint. If it touches production traffic, it needs an internal owner named in the service catalog with an explicit risk-acceptance record.
Scaling. Resolution cost is CPU (traversal) plus, if remote refs are enabled, network I/O — the latter is the killer. Per-request remote dereferencing means p99 latency is coupled to third-party availability and DNS; a slow $ref host becomes your outage. Mitigations, in priority order:
ResolutionException rather than recursing.Horizontal scaling is fine; the library is stateless. The scaling question is really isolation, not throughput.
Failure modes.
| Failure | Trigger | Symptom | Blast radius | Mitigation |
|---|---|---|---|---|
| SSRF | Attacker-supplied $ref URI |
Outbound request to internal metadata/host | Critical — credential exposure | Egress allowlist; disable remote refs |
| Circular reference | Self-referential schema | Infinite recursion, OOM | High — request-level DoS | Depth cap + visited-set |
| Deep nesting / oversized doc | Crafted payload | Memory exhaustion | High | Size + node-count limits |
| Silent misresolution | Draft-spec mismatch ($id, anchors) |
Schema check passes when it shouldn't | High if it gates auth/entitlements | Golden-fixture contract tests |
| Platform constraint break | PHP/Laravel upgrade | Composer resolution failure at build, or runtime fatal with --ignore-platform-reqs |
Medium | Pin in composer.json, block the flag in CI |
| Stale cache | Remote doc changed, TTL not elapsed | Validation against outdated contract | Medium | Document-hash keys; deploy-time invalidation |
| SCA alert fatigue | Abandoned package flagged indefinitely | Real alerts ignored | Medium (organizational) | Time-boxed risk acceptance with expiry date |
Ramp-up. Low technically, deceptive organizationally. The API surface is tiny — a mid-level engineer reads it in an afternoon and is productive in a day. But effective use requires domain knowledge that is scarce: RFC 6901 pointer escaping (~0/~1), RFC 3986 relative-URI resolution, and the differences between JSON Reference draft semantics and JSON Schema 2019-09/2020-12 $ref. That spec knowledge is the actual onboarding cost — roughly 1–2 weeks to competence, and it is knowledge that does not transfer to the maintained alternatives in the same shape.
Net TPM position. Score 0.87 with opportunity 22.09 reads as "well-built artifact, negligible strategic value." Archived + 0 dependents + no security response path means this is a liability with a small convenience attached. If it is already in the tree: bound it behind an owned interface, add allowlist and depth guardrails this sprint, and schedule replacement within two quarters. If it is not: decline, and spend the same three weeks on opis/json-schema plus build-time pre-resolution — you get a maintained dependency, a smaller attack surface, and no fork to babysit.
How can I help you explore Laravel packages today?