SimpleDB is a small PHP class that provides a straightforward API for storing
and retrieving JSON documents. Each record is stored as an individual .json
file, and each storage folder acts like a table or collection.
It is intended for small applications and simple datasets that do not require the indexing, transactions, or scaling features of a relational database.
- PHP 5.5 or newer
- A writable storage directory
- A cryptographically secure random source provided by
random_bytes()or OpenSSL
A currently supported PHP release is strongly recommended.
The default structure after running setup and creating a collection is:
PROJECT ROOT
├── models
│ └── Simple_DB.php
└── json
├── .htaccess
└── COLLECTIONS
└── RECORDS.json
When Simple_DB.php is stored in models/, the class uses the json/
directory at the same level as models/. It creates the JSON root and selected
collection directory when necessary. The PHP process must have permission to
create and write files there.
public static function setup(
$baseDirectory = null,
$replaceHtaccess = false
);setup() prepares the shared JSON storage root before any collections are
created. It is a static method, so call it on Simple_DB rather than on a
database instance.
After including or autoloading the class, run:
$jsonRoot = Simple_DB::setup();This creates the default json/ directory and, when one does not already
exist, writes json/.htaccess containing:
# Generated by SimpleDB.
Require all deniedThe returned value is the resolved absolute path to the JSON root. Setup only needs to be run during installation or deployment, not before every request.
Then construct collections normally:
$simpleDB = new Simple_DB('car');The constructor creates the collection directory because setup() does not
know which collection names the application will use.
| Parameter | Default | Description |
|---|---|---|
$baseDirectory |
null |
JSON root directory. null selects json/ beside the models/ directory. |
$replaceHtaccess |
false |
When true, deliberately replaces an existing regular .htaccess file with SimpleDB's Apache rule. |
Pass the same custom root to setup and the constructor when storing data somewhere else:
$jsonRoot = Simple_DB::setup(
'/absolute/path/to/json'
);
$simpleDB = new Simple_DB('car', $jsonRoot);This creates the root if necessary, creates its .htaccess, and stores car
records in /absolute/path/to/json/car/.
For safety, setup preserves an existing .htaccess without changing or
validating its rules. To deliberately replace it with SimpleDB's Apache 2.4
rule, pass true as the second argument:
Simple_DB::setup(
'/absolute/path/to/json',
true
);Replacing the file removes all existing rules from it. The routine refuses to
read through or replace a symbolic-link .htaccess and throws
RuntimeException when setup cannot be completed. Only use replacement when
discarding the existing rules is intentional.
On success, setup() returns the resolved absolute JSON root path as a string.
It throws:
InvalidArgumentExceptionwhen the base-directory argument is invalid.RuntimeExceptionwhen it cannot create or resolve the directory, encounters an unsafe.htaccesstarget, or cannot write the protection file.
The generated .htaccess rule applies to Apache 2.4. Nginx, IIS, and other web
servers ignore it and require equivalent server configuration.
The constructor can still be used without calling setup():
$simpleDB = new Simple_DB('car');This creates json/car/, but it does not create .htaccess. Run setup()
first when the storage root needs Apache protection. Collection names may
contain letters, numbers, underscores, and hyphens.
Pass a JSON root directory as the optional second constructor argument:
$simpleDB = new Simple_DB(
'car',
'/absolute/path/to/json'
);The custom path is the JSON root, not the individual collection directory. The
example above stores records in /absolute/path/to/json/car/.
The constructor throws InvalidArgumentException for an invalid collection
name or path argument and RuntimeException when it cannot create or resolve
the storage directories.
The four primary methods resemble common HTTP operations:
$simpleDB->get($id);
$simpleDB->post($content);
$simpleDB->put($id, $content);
$simpleDB->delete($id);Retrieve every record in the collection:
$cars = $simpleDB->get();This returns an associative array keyed by document ID. Each value is a decoded
object. It returns false when no readable records exist.
Retrieve one record by ID:
$car = $simpleDB->get($id);This returns the decoded record or false when the ID is invalid, missing, or
unreadable. The .json extension is optional when supplying an ID.
Create a record:
$id = $simpleDB->post([
'manufacturer' => 'Honda',
'model' => 'Civic',
'color' => 'blue'
]);post() accepts a string, array, or object. The content is stored as a JSON
object. On success it returns a cryptographically random 32-character
hexadecimal ID; otherwise it returns false.
The generated filename is ID.json.
Replace an existing record:
$updatedCar = $simpleDB->put($id, [
'manufacturer' => 'Honda',
'model' => 'Civic',
'color' => 'red'
]);put() only updates an existing document. It does not create records with
custom IDs. It returns the updated content as an object or false when the ID
does not exist, the ID is invalid, JSON encoding fails, or the record cannot be
written.
Delete a record:
$deleted = $simpleDB->delete($id);The ID is required. The method returns true when the record is deleted and
false when it does not exist or cannot be deleted.
query() finds records whose stored fields match all supplied criteria. It
accepts a URL query string, associative array, or object.
$cars = $simpleDB->query(
'color=blue&manufacturer=Honda'
);$cars = $simpleDB->query([
'color' => 'blue',
'manufacturer' => 'Honda'
]);$cars = $simpleDB->query((object) [
'color' => 'blue',
'manufacturer' => 'Honda'
]);Nested arrays and objects are supported:
$cars = $simpleDB->query([
'manufacturer' => 'Honda',
'specifications' => [
'doors' => 4,
'transmission' => 'automatic'
]
]);Queries use these rules:
- Every supplied field must match.
- Field names must match actual fields; arbitrary text is not searched.
- Scalar values are compared as strings without regard to letter case.
- Partial string matches are not performed.
- Nested criteria are matched recursively.
The result is an associative array keyed by document ID. query() returns
false when no records match or the criteria are invalid.
Return the first document ID from a result array:
$cars = $simpleDB->query(['model' => 'Civic']);
$carId = $simpleDB->returnSingleId($cars);This returns the first key or false when the supplied value is not a non-empty
array. It is intended for queries expected to return one record.
For multiple records, use their array keys:
$cars = $simpleDB->query(['color' => 'blue']);
if ($cars !== false) {
foreach ($cars as $carId => $car) {
// $carId is the document ID.
}
}Return a record's last-modified time as a Unix timestamp:
$time = $simpleDB->timestamp($id);The method returns an integer timestamp or false when the ID is invalid,
missing, or unreadable.
Retrieve one record as its literal JSON string:
$json = $simpleDB->getJSON($id);Calling getJSON() without an ID returns an associative array in which every
document ID maps to its raw JSON string. It returns false when no readable
records exist.
List the valid JSON record filenames in the current collection:
$files = $simpleDB->getFiles();The method returns a sorted array of filenames. Non-JSON files, invalid record names, directories, symbolic links, and internal lock files are excluded.
Methods normally report operational failures by returning false. Retrieve a
description immediately afterward with:
$car = $simpleDB->get($id);
if ($car === false) {
$message = $simpleDB->getLastError();
}getLastError() returns the most recent error message or null. Operations
that access or modify storage clear the previous error before they begin.
SimpleDB uses a collection-level lock and atomic file replacement to prevent cooperating PHP requests from reading partially written records or corrupting files during simultaneous operations. It also validates collection names and record IDs and rejects record-file symbolic links.
These protections do not turn the class into a transactional database. If the application requires transactions, indexes, relationships, large datasets, or heavy concurrent traffic, use SQLite or a database server instead.
The safest arrangement is to place the JSON root outside the web server's public document directory:
$simpleDB = new Simple_DB(
'car',
'/srv/my-application-data/json'
);If the json/ directory must be inside an Apache 2.4 document root, place this
in json/.htaccess:
Require all deniedThe one-time Simple_DB::setup() routine creates this file automatically when
one does not already exist.
This only works when Apache is configured to honor .htaccess overrides for
that directory. The older Order, Allow, and Deny directives are
deprecated. Other web servers, including Nginx and IIS, require equivalent
server-specific configuration.
Do not rely on unpredictable filenames to protect sensitive data.
Compared with the original implementation:
- Newly created IDs are secure 32-character hexadecimal strings.
put()updates existing records only.query()accepts query strings, arrays, and objects.- Queries support nested fields and perform exact field comparisons.
- Writes are locked and published atomically.
- Invalid IDs and symbolic-link records are rejected.
getJSON()correctly returns its result.getLastError()provides failure details.setup()can create the JSON root and Apache protection file.
Existing IDs created by the original uniqid() implementation remain valid as
long as they contain only letters, numbers, underscores, or hyphens.
SimpleDB is distributed under the MIT License included in Simple_DB.php.