nahid/jsonq
JsonQ is a PHP library for querying JSON data with a fluent, jq-like API. Traverse nested objects and arrays, filter with where, sort, select, and extract values without manual array handling. Ideal for APIs, config files, and dynamic JSON.
What it is: nahid/jsonq is a framework-agnostic PHP library that loads a JSON document (file, string, or URL) into memory and exposes a fluent, SQL-flavored API — where, orWhere, whereIn, sortBy, groupBy, sum, avg, first, paginate — over the decoded structure. It is a read-only query layer, not a persistence engine, not a schema validator, and not a streaming processor.
Architecture fit. The package is a leaf dependency: no service provider required, no config to publish, no database, queue, or HTTP coupling. That is architecturally clean — it can be dropped into a controller, a console command, or a domain service without infecting the rest of the codebase. The tension is that it encourages a pattern (loading a whole document, chaining queries inline) that is easy to write and hard to test at scale. Treat it as an anti-corruption layer candidate, not a core primitive.
Integration feasibility. Composer-installable with a permissive CC0-1.0 license, so there is essentially zero legal friction to adoption, forking, or vendoring. The real friction is version currency: last tagged release is February 2021, which predates PHP 8.1/8.2/8.3 hardening. Expect to verify:
php constraint in composer.json versus your runtime (PHP 8.2+ deprecations around dynamic properties, strlen(null), implicit float-to-int casts).Technical risk. Ranked:
| Risk | Severity | Notes |
|---|---|---|
| Unmaintained upstream | High | Four-plus years without a tag; you inherit any bug you hit |
| PHP 8.x compatibility | Medium-High | Likely works, not guaranteed; must be proven by your CI, not assumed |
| Whole-document memory model | Medium | Cost scales with file size × decoded-array overhead (~5–10× raw bytes); no streaming, no indexing |
| Evaluation semantics of condition strings | Medium | String operators and loose comparison can coerce types ("10" > 9); ambiguity in where behavior is the #1 source of silent wrong answers |
| Exception/edge behavior | Medium | Missing-key returns versus throws, empty-result shapes, malformed-JSON handling — undocumented enough that you must codify it |
| Supply chain | Low-Medium | Small dependency graph is good; but no security-response channel exists |
Key questions to answer before committing:
eval() or dynamic function invocation anywhere? If yes, hard stop for any path touching user-supplied operator input.new JsonQ(...) in every class.jmespath.php for path extraction, justinwalsh/query-style array helpers, or Laravel's native Arr::/data_get() for the 80% case?Assessment: technically viable for bounded, read-only querying of small-to-medium JSON fixtures, config blobs, or third-party API payloads. It is not viable as foundational infrastructure without a fork-and-own commitment.
Stack fit. Strong for Laravel 9–11 on PHP 8.1–8.3 provided a compatibility spike passes. It is framework-neutral, so it also fits Symfony or plain PHP, which is an advantage for shared internal packages. It does not integrate with Eloquent, so do not let it become a shadow ORM — the moment a query is doing relational work or paginating a large dataset, that data belongs in a database.
Migration path. Do not call the library directly from controllers or jobs. Introduce a thin internal adapter:
JsonDocumentRepository interface with methods expressed in your domain vocabulary (findActiveAccounts(), summarizeByRegion()), not where()->orWhere().jsonq, one backed by jmespath.php or plain array filtering, selectable by config.That adapter costs roughly one to two days and converts an unmaintained dependency from an architectural risk into a replaceable detail.
Compatibility. Pin an exact version with a caret floor and a hard ceiling in composer.json. Run the library's own test suite inside your CI image on your target PHP matrix — treat a green run as the compatibility gate, not Packagist metadata. Enable composer audit (it will likely report nothing, which is itself informative), and suppress third-party deprecations via PHPUnit's deprecation handler rather than by downgrading PHP.
Sequencing.
jsonq results against the current implementation or a jq/JMESPath baseline on real payloads; diff outputs for a week.nahid/jsonq under the org, CC0 permits this with no attribution obligation) the moment a patch is needed; publish to a private VCS repo and track upstream diffs manually.Maintenance. This is the dominant cost. With no release since 2021, the library is effectively yours the day it lands in production. Budget: ~1–2 engineer-days per year for PHP-version upkeep and dependency bumps, plus unbounded time if a data-shape bug surfaces in a hot path. CC0-1.0 makes the licensing side frictionless — no attribution, no copyleft contamination, no redistribution constraints — but note it is a public-domain dedication rather than an OSI-approved software license and carries no express patent grant. For a JSON query helper that is a negligible risk; it would not be for anything cryptography-adjacent.
Support. Community-only. GitHub issues with no SLA, no security disclosure policy, no LTS branch. Escalation path is "read the source." Your team must be comfortable debugging the library itself — the codebase is small enough that this is realistic, but it should be an explicit, named-owner assumption rather than an unspoken hope.
Scaling. Constrain this deliberately:
memory_limit exhaustion is a fatal, uncatchable failure in many configurations.jsonb), a streaming parser, or jq as a subprocess. The adapter boundary makes that a contained change.Failure modes to instrument. Malformed or truncated JSON (upstream API changes are the usual cause) — should raise a typed domain exception, not a raw library exception. Missing-key behavior drifting between "null" and "throws" across query shapes. Silent type coercion producing subtly wrong sums and filters — this is the dangerous one, because it fails open and looks plausible. Memory exhaustion on an unexpectedly large payload. Deprecation notices promoted to exceptions by a strict error handler after a PHP upgrade.
Ramp-up. Very low for developers: the API reads like SQL, and a mid-level Laravel engineer is productive within an hour from the README. Documentation is README-grade, not reference-grade, so the effective onboarding cost is not learning the API but learning its undocumented edge behavior — which is precisely what the conformance tests from the integration phase should absorb. Do not let individual developers rediscover those semantics in production.
Recommendation. Adopt for narrow, read-only, small-document querying behind an internal adapter, with an explicit named owner and a documented fork plan. Do not adopt as shared platform infrastructure, do not expose it to user-controlled operator input, and do not use it where a database or a streaming parser is the honest answer. The 870 stars and public-domain license make it a low-friction experiment; the 2021 release date makes it a poor long-term bet without ownership.
How can I help you explore Laravel packages today?