iliaal / fastjson
Fast JSON encode/decode/validate for PHP 8.1+, backed by yyjson. Drop-in alternative to ext/json with namespaced fastjson_* functions and json_last_error-compatible error reporting.
Package info
Type:php-ext
Ext name:ext-fastjson
pkg:composer/iliaal/fastjson
Requires
- php: >=8.1
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-25 17:23:05 UTC
README
Fast JSON encode, decode, and validate for PHP 8.1+. A drop-in alternative to ext/json with a fastjson_* API and json_last_error-compatible error reporting, built on yyjson 0.12.0. It coexists with ext/json, so you adopt it one call site at a time.
Status: pre-release. Available now:
fastjson_encode/fastjson_decode/fastjson_validate,fastjson_last_error/_msg/_pos/_info, the file helpersfastjson_file_decode/fastjson_file_encode, and the JSON Pointer / patch helpersfastjson_pointer_get/_exists/_set(RFC 6901) andfastjson_merge_patch(RFC 7396). The compat harness againstphp-src/ext/json/tests/*.phptpasses every test for features fastjson mirrors;tests/upstream-json/.skiplistcategorizes the rest.
📦 Install
# PIE (PHP Foundation's extension installer; uses the composer.json # at the repo root with type: "php-ext") pie install iliaal/fastjson
On a minimal PHP image (for example php:8.x-cli from Docker Hub), install these build tools before running PIE:
# Debian/Ubuntu sudo apt install -y git bison libtool-bin # macOS brew install bison libtool
From source
git clone https://github.com/iliaal/fastjson.git cd fastjson phpize && ./configure --enable-fastjson make -j sudo make install echo 'extension=fastjson.so' | sudo tee /etc/php/conf.d/fastjson.ini
Windows binaries
Pre-built 64-bit DLLs for PHP 8.1 through 8.5 (TS/NTS) are attached to each GitHub release.
🛠️ Usage
$json = fastjson_encode(['hello' => 'world']); // string|false $data = fastjson_decode($json, associative: true); // mixed $ok = fastjson_validate($json); // bool if ($data === null && fastjson_last_error() !== 0) { fwrite(STDERR, fastjson_last_error_msg()); }
Function signatures match ext/json, so you can migrate a call site by replacing json_* with fastjson_*. PHP 8.4 property hooks and JsonSerializable work as they do in ext/json.
Encode flags: JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, JSON_FORCE_OBJECT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT, JSON_NUMERIC_CHECK, JSON_PRESERVE_ZERO_FRACTION, JSON_PARTIAL_OUTPUT_ON_ERROR, JSON_INVALID_UTF8_IGNORE, JSON_INVALID_UTF8_SUBSTITUTE, JSON_THROW_ON_ERROR.
Decode flags: JSON_OBJECT_AS_ARRAY, JSON_BIGINT_AS_STRING, JSON_INVALID_UTF8_IGNORE, JSON_INVALID_UTF8_SUBSTITUTE, JSON_THROW_ON_ERROR, and the fastjson-only FASTJSON_DECODE_RELAXED (tolerates the JSONC subset ext/json rejects: // and /* */ comments, trailing commas, and a leading UTF-8 BOM).
Validate flags: JSON_INVALID_UTF8_IGNORE (other bits raise ValueError per ext/json's contract). $depth is enforced on the success path like ext/json: documents nesting $depth or more containers deep validate false with a depth error.
File helpers: fastjson_file_decode() / fastjson_file_encode() read and write a JSON file in one call through the PHP streams layer (open_basedir and stream wrappers apply). fastjson_file_encode() opens the destination in write mode and does not replace it atomically; if a partial write would be unsafe, write to a temp file and rename it yourself. File I/O failures reuse FASTJSON_ERROR_SYNTAX to stay inside the JSON_ERROR_* code range. To tell a filesystem fault from a parse or encode failure, check fastjson_last_error_msg() for the Failed to ... file messages.
JSON Pointer and merge patch: fastjson_pointer_get() extracts one value by RFC 6901 JSON Pointer without materializing the rest of the document. fastjson_pointer_exists() reports whether a pointer resolves, so you can tell "present but null" from "absent". fastjson_pointer_set() sets one value by pointer and returns the re-serialized document; it splices the edit into the parsed document, so a single edit on a large document skips a full decode and re-encode. Formatting and escaping flags apply to the whole pointer-set output; value-transforming flags apply only to the replacement. Pointer-set rejects a target member that appears more than once in its object. fastjson_merge_patch() applies an RFC 7396 merge patch and collapses duplicate members in each merged object to the last value at the first insertion position. Merge-patch member matching uses the raw JSON key bytes. JSON_INVALID_UTF8_IGNORE and JSON_INVALID_UTF8_SUBSTITUTE sanitize only the materialized PHP result, so malformed and valid key spellings that sanitize to the same PHP key stay distinct during the merge.
Error location: fastjson_last_error_pos() returns the byte offset of the most recent parse error (-1 when none). fastjson_last_error_info() returns ['code', 'msg', 'pos', 'line', 'col'] (1-based line and column) in one call, so you can point at where malformed JSON broke. Under JSON_THROW_ON_ERROR the thrown class is JsonException when ext/json is loaded, Fastjson\JsonException otherwise.
CHANGELOG.md has the full feature list and the known divergences from ext/json output.
📊 Performance
Throughput vs ext/json on the full 14.8 MB / 15-file canonical corpus from simdjson_php's jsonexamples. i9-13950HX, release build of both PHP and fastjson (-O2, Debug Build => no):
| Operation | fastjson | ext/json | speedup |
|---|---|---|---|
| Decode (stdClass) | 428 MB/s | 165 MB/s | 2.59x |
| Decode (assoc array) | 410 MB/s | 172 MB/s | 2.39x |
| Encode | 748 MB/s | 135 MB/s | 5.56x |
| Validate | 994 MB/s | 197 MB/s | 5.04x |
A visual comparison against ext/json on PHP 8.4 is at iliaal.github.io/fastjson. bench/README.md and bench/baseline.md cover methodology, per-file numbers, small-corpus and per-call latency, and how to reproduce.
Memory tradeoff
Decode trades memory for speed: yyjson's two-stage parser holds the parsed document alongside the zval tree, so decode peaks at ~1.7x ext/json's heap. Encode writes directly into a smart_str with yyjson primitives, so its memory is close to ext/json (~1.1x). Validate peaks at ~101x ext/json, whose streaming validator uses a constant ~80 bytes; vendor patch P-002 already cuts yyjson's stock read path by 2.7x (see vendor/yyjson/PATCHES.md). If you validate giant inputs under a tight memory_limit, budget for this.
✨ What's in the box
- Bundled yyjson 0.12.0 (MIT) with five local patches (P-001 through P-005). Notes and the replayable series are in
vendor/yyjson/PATCHES.mdandvendor/yyjson/patches/. - yyjson allocates through Zend's
emalloc/erealloc/efree, so JSON allocations count againstmemory_limitand are freed at request end. FASTJSON_ERROR_*constants match theJSON_ERROR_*numeric values, so you can use either set. yyjson reports raw control-character and invalid-surrogate parse failures under the UTF-8 code; the aliases exist even where fastjson does not emit the more specific ext/json code.- 67-test compat harness rewritten from
php-src/ext/json/tests/*.phptruns alongside the native phpt suite.tests/upstream-json/.skiplistandtests/upstream-json/STATE.mdlist the upstream tests fastjson does not try to match byte-for-byte. - Depth and stack-overflow guards use
zend_call_stack_overflowedwhere Zend exposes it and conservative recursion caps elsewhere, so deeply nested input fails with an error instead of crashing the process.
Roadmap
-
fastjson_validatesuccess-path depth enforcement (allocation-free nesting scan; the validate-only fast path is kept) - Streaming / incremental decode and encode
🔗 Native PHP extensions
- php_excel: native Excel I/O via LibXL. 7-10× faster than PhpSpreadsheet, full XLS/XLSX with formulas, formatting, and styling.
- mdparser: native CommonMark + GFM markdown parser via md4c. 15-30× faster than pure-PHP libraries.
- php_clickhouse: native ClickHouse client speaking the wire protocol directly. Picks up where SeasClick left off.
- pdo_duckdb: PDO driver for DuckDB, analytical SQL in your PHP stack.
- phpser: decoder-optimized binary serializer for cache workloads. Faster than igbinary on packed numerics and DTO batches.
- fast_uuid: high-throughput UUID generation (v1/v4/v7), batched CSPRNG and SIMD hex formatter, ramsey-compatible API.
- fastchart: native chart-rendering extension. 38 chart types behind one fluent OO API, SVG-canonical with PNG/JPG/WebP and optional PDF output.
- statgrab: system statistics (CPU, memory, disk, network) via libstatgrab, no parsing /proc by hand.
- phonetic: native phonetic name matching (Double Metaphone, Beider-Morse, Daitch-Mokotoff, NYSIIS, Match Rating), the encoders PHP core lacks.
License
- Wrapper code (
fastjson*.c,php_fastjson.h) under BSD 3-Clause. - Bundled yyjson sources under MIT. Upstream LICENSE preserved verbatim and surfaced in Section 2 of the project LICENSE file.
Follow @iliaa on X • Blog • If this sped up your stack, ⭐ star it!
