Simple, file-based object/document-database using JSON
Latest commit 73b4781 Jun 5, 2015 @mindplay-dk minor BC break: getId() now returns NULL if the given document was no…
…t found (previously would thrown an exception)
Failed to load latest commit information.


Simple, file-based object/document-database using JSON-files for persistence.

Build Status

Scrutinizer Code Quality

Code Coverage


Object-graphs are serialized and stored as JSON-documents, in individual files - the JSON representations are (optionally, by default) indented and formatted in a human-readable and CVS-friendly way.

Object-keys map directly to folders and file-names - for example, an object stored as foo/bar is saved as {database name}/foo/bar.json.

Write and delete-operations are committed using basic transaction semantics, with simple database-level locking to prevent simultaneous writes, using early error-detection and automatic roll-back on failure.

Please see "Limitations" below for additional technical details.


The API consists of two classes:

DocumentStore represents the "connection" to a data-store: a root-folder containing one or more databases.

DocumentSession represents a session with one specific database inside a data-store - it manages the loading and saving of objects, and attempts to do so in a transactional and safe manner, e.g. committing all save/delete operations atomically.


Create a DocumentStore with FilePersistence and point it to an existing folder:

$store = new DocumentStore(new FilePersistence($db_path));

Ask the DocumentStore to create a DocumentSession:

$session = $store->openSession();

This will lock the store in shared mode, until you close() the session. (it will also automatically close if it falls out of scope.)

Now create objects of any class, and store them:

$a = new Foo;
$a->bar = 'Hello, World.';

$session->store($a, 'foo/bar');

Note that the state of the object has been captured in-memory, but the serialized object does not get written to underlying storage until changes are committed.

Alternatively, you can store an object with a generated UUID under a parent ID:

$a = new Foo;
$a->bar = 'Hello again!';

$id = $session->append($a, 'foo', $uuid);

var_dump($uuid); // "029d97a2-7676-45b1-9d49-353bec0d71c0"
var_dump($id);   // "foo/029d97a2-7676-45b1-9d49-353bec0d71c0"

Load objects from the database into the current session:

$b = $session->load('foo/baz');

Delete unwanted objects:


Call commit() to persist all the pending store/delete-operations:


Finally, you should close() to explicitly release the lock:


The DocumentSession API also provides a few other operations:

  • exists($id) indicates whether a document with the given ID exists in the store.

  • contains($id) indicates whether the session contains a document with a given ID.

  • getId($object) provides the document ID of an object in the session.

  • evict($id) evicts the object/document with the given ID from the session.

  • flush() evicts all objects/documents and pending operations from the session.


Using individual, flat files for data-storage is not fast - this library (by design) is optimized for consistent storage, quick and easy implementation, human-readable and VCS-compatible file-based storage, in applications where speed is not a critical factor.

The JsonSerializer itself has an important limitation: it is designed to store self-contained object-graphs only - it does not support shared or circular object-references. This is by design, and in-tune with good DDD design practices: an object-graph with shared or circular references does not have clear transaction boundaries and cannot be stored in a predictable and consistent way.

This library does not synthesize object keys - which means you must assign a key when you store a new object. Again, this is by design.

More detailed background on limitations and technical decisions here.