Skip to content

Storh

File-first records for PHP: JSONC documents, append-only segmented logs, and log-backed queues with UUIDv7 ids.

Terminal
composer require storh/storh

Latest release v0.5.2 MIT PHP 8.2+

In practice

The quick start in two parts: a document in a storage root, then a log entry and a queue job beside it.

One root, three engines

StorageRoot::resolve() normalizes a base directory and a namespace into a root, and each engine creates its own directory under it on first use.

Every engine returns StorageRecord objects with id() and data(). The queue appends each claim and completion to its event log.

Documents
use Storh\DocStore;
use Storh\Queue;
use Storh\RecordQuery;
use Storh\SegmentedLog;
use Storh\StorageRoot;

$root = StorageRoot::resolve(__DIR__ . '/var/storh', 'demo');

$docs = new DocStore($root, 'pages');
$home = $docs->put([
    'kind' => 'page',
    'title' => 'Home',
]);

$loaded = $docs->get($home->id());

JSONC Document Store

One JSONC file per record, UUIDv7 ids, UUID-tail sharding, atomic replacement, and streaming field filters.

Segmented Log

Append-only segments of length- and checksum-prefixed JSON lines, sparse indexes, cursor and time-range reads, crash recovery, and compaction.

Log Queue

Jobs move through pending, processing, and done states using compact append-only queue events.

UUIDv7 Records

Sortable, time-addressable ids for stable cursors, bounded scans, and shard-friendly storage paths.

Atomic Writes

Temporary-file writes and atomic renames keep records whole. After a crashed writer, the next open sweeps its orphaned temp files.

Torn-Write Recovery

Segment reads validate length and checksum fields; recovery truncates partial trailing lines.

File-First

No database server, no framework adapter, no Composer runtime packages; callers pass a directory.

Reads JSONC

Record files may carry comments and trailing commas. storh writes compact single-line JSON, so comments last until the next write.

Query Builder

Fluent queries such as where('status')->in([...]) on DocStore and SegmentedLog; DocStore uses secondary indexes where they exist.

Secondary Indexes

Equality, unique and range indexes stored as files beside the collection, derived from the records and rebuildable at any time.

Schemas

Optional collection schemas validate every write and declare which fields get indexes.

SQL Mirror

Push collections into SQLite or MySQL for joins, search and reporting while the files stay canonical. Needs ext-pdo or ext-mysqli only when used.

Why Storh

A directory, not a server

You pass a base directory and storh writes there. It needs PHP 8.2 and ext-json, with no Composer runtime packages and no framework adapter.

One record shape, three layouts

Documents, logs and queues all store a UUIDv7 id plus an array of data. Choose the disk layout per workload and keep all three under one root.

Checks you can run

verify() checks indexes and log state against a full scan of the files. repair() quarantines corrupt records and truncates torn log tails, and the CLI prints JSON for cron jobs and health checks.

Benchmarks with their setup

The README publishes medians for 50,000 records per engine on an Apple M1 Pro with PHP 8.5, such as 5.6 µs per point read. Every write in those runs is fsynced, so write rates depend on the filesystem.

Compared with

storh is file-first storage rather than a relational query engine, so these tools do some jobs it leaves out.

Storh compared with other tools
SQLite Good atA self-contained SQL engine in a single file, with serializable ACID transactions that hold through a program crash or power failure. Its docs present it as a replacement for ad hoc data files. Difference with Storhstorh keeps one JSON file per record and its core needs no PDO driver. It has no multi-record transactions, and its docs send that work to a real database. It can push collections into SQLite for joins and reporting while the files stay canonical.
MySQL Good atA relational database served by a multithreaded SQL server, with client programs and libraries, administrative tools and a wide range of APIs. Difference with Storhstorh runs inside the PHP process against a directory, so there is no server to operate. For joins, search and reporting it mirrors collections into MySQL, also through an existing mysqli handle, and the files remain the source of truth.
SleekDB Good atA flat-file document database in PHP with no third-party dependencies that stores schema-free data as plain JSON files. Its query API includes joins, text search, group by and having, and filters on nested properties. Difference with Storhstorh pairs its document store with an append-only segmented log and a log-backed queue under one root, and fsyncs every write before reporting it stored. Joins are not part of storh; it pushes collections into SQLite or MySQL for them.

Questions and answers

Short answers from the Storh documentation.

Start with the quick start, then use Choosing an engine to match DocStore, SegmentedLog or Queue to your workload.