visorcraft/mongreldb-php
Composer 安装命令:
composer require visorcraft/mongreldb-php
包简介
Pure PHP client for MongrelDB - a fast embedded+server database with SQL, vector search, full-text search, and AI-native retrieval.
README 文档
README
MongrelDB PHP Client is the pure-PHP application-facing client for MongrelDB. It gives PHP 8.4+ applications a typed CRUD surface, a fluent query builder that pushes down to MongrelDB's native indexes, idempotent batch transactions, full SQL access, user/role/credentials management, and stored procedures - all over HTTP to a running mongreldb-server daemon.
No C extensions and no compilation step. Optional performance features (persistent cURL sharing) require PHP 8.5+ and degrade gracefully on 8.4.
Package
| Surface | Package | Install |
|---|---|---|
| PHP client | visorcraft/mongreldb-php |
composer require visorcraft/mongreldb-php |
Requirements
- PHP 8.4 or newer (PHP 8.5 supported with opt-in performance features)
- ext-curl (default HTTP transport) and ext-json (always required)
- A running
mongreldb-serverdaemon
What It Provides
- Typed CRUD over the Kit transaction endpoint:
put,upsert(insert-or-update on PK conflict),deleteby row id or primary key, with idempotency keys for safe retries. - Fluent query builder that pushes conditions down to the engine's specialized indexes for sub-millisecond lookups: bitmap equality/IN, learned-range, null checks, FM-index full-text search, HNSW vector similarity (
ann), and sparse vector match. - Idempotent batch transactions - all operations staged locally and committed atomically, with the engine enforcing unique, foreign key, and check constraints at commit time. Idempotency keys return the original response on duplicate commits, even after a crash.
- Full SQL access through the DataFusion-backed
/sqlendpoint: recursive CTEs, window functions,CREATE TABLE AS SELECT, materialized views, multi-statement execution, and themongreldb_fts_rankrelevance-scoring UDF. - Schema management: typed table creation, full schema catalog, and per-table descriptors.
- User/role/credentials management: Argon2id-hashed catalog users, roles,
GRANT/REVOKEtable-level permissions, daemon HTTP Basic + Bearer auth, and a client-side permission validator that blocks SQL injection in grant strings. - Stored procedures: install, list, drop, and call with typed arguments.
- Maintenance: compaction (all tables or per-table).
- Pluggable HTTP transport: cURL by default (with keep-alive connection pooling), a stream-based fallback, and a
TransportInterfacefor custom adapters (e.g. a Guzzle/PSR-18 bridge). - Typed exception hierarchy:
AuthException(401/403),NotFoundException(404),ConstraintException(409, with error code + op index),ConnectionException(network), andQueryException(everything else). - Robust JSON handling: malformed UTF-8 is substituted rather than rejecting the whole request; INF/NAN and recursive structures raise a clear
QueryExceptioninstead of corrupting data.
Examples
Runnable, commented examples live in examples/:
- Basic CRUD - connect, create a table, insert, query, count.
- Authentication - users, roles, table-level permissions, credential verification.
- Transactions - batch commits, idempotency keys, constraint handling.
- SQL queries - recursive CTEs, window functions, advanced SQL.
Quick Example
use Visorcraft\MongrelDB\Database; // Connect to a running mongreldb-server daemon $db = new Database('http://127.0.0.1:8453'); // Create a table $db->createTable('orders', [ ['id' => 1, 'name' => 'id', 'ty' => 'int64', 'primary_key' => true, 'nullable' => false], ['id' => 2, 'name' => 'customer', 'ty' => 'varchar','primary_key' => false, 'nullable' => false], ['id' => 3, 'name' => 'amount', 'ty' => 'float64','primary_key' => false, 'nullable' => false], ]); // Insert rows $db->put('orders', [1 => 1, 2 => 'Alice', 3 => 99.50]); $db->put('orders', [1 => 2, 2 => 'Bob', 3 => 150.00]); // Upsert (insert or update on PK conflict) $db->upsert('orders', [1 => 1, 2 => 'Alice', 3 => 120.00], updateCells: [3 => 120.00]); // Query with a native index condition (learned-range index) $rows = $db->query('orders') ->where('range', ['column' => 3, 'min' => 100.0]) ->projection([1, 2]) ->limit(100) ->execute(); echo $db->count('orders'); // 2 // Run SQL $db->sql("UPDATE orders SET amount = 200.0 WHERE customer = 'Bob'");
Authentication
// Bearer token (--auth-token mode) $db = new Database('http://127.0.0.1:8453', token: 'my-secret-token'); // HTTP Basic (--auth-users mode) $db = new Database('http://127.0.0.1:8453', username: 'admin', password: 's3cret');
Batch transactions
Operations are staged locally and committed atomically. The engine enforces unique, foreign key, and check constraints at commit time.
$txn = $db->beginTransaction(); $txn->put('orders', [1 => 10, 2 => 'Dave', 3 => 50.00]); $txn->put('orders', [1 => 11, 2 => 'Eve', 3 => 75.00]); $txn->deleteByPk('orders', 2); try { $txn->commit(); // atomic - all or nothing echo "Staged {$txn->count()} operations\n"; } catch (\Visorcraft\MongrelDB\Exceptions\ConstraintException $e) { echo "Constraint violated: {$e->errorCode} - {$e->getMessage()}\n"; $txn->rollback(); } // Idempotent commit - safe to retry; daemon returns the original response $txn = $db->beginTransaction(); $txn->put('orders', [1 => 20, 2 => 'Frank', 3 => 100.00]); $txn->commit(idempotencyKey: 'order-20-create');
Native query builder
Conditions push down to the engine's specialized indexes. The builder accepts
friendly aliases that are translated to the server's on-wire keys: column
(-> column_id), min/max (-> lo/hi). The canonical keys are also
accepted directly.
// Bitmap equality (low-cardinality columns) $db->query('orders')->where('bitmap_eq', ['column' => 2, 'value' => 'Alice'])->execute(); // Range query (learned-range index) $db->query('orders') ->where('range', ['column' => 3, 'min' => 50.0, 'max' => 150.0]) ->limit(100)->execute(); // Full-text search (FM-index) $db->query('documents') ->where('fm_contains', ['column' => 2, 'pattern' => 'database performance']) ->limit(10)->execute(); // Vector similarity search (HNSW) $db->query('embeddings') ->where('ann', ['column' => 2, 'query' => [0.1, 0.2, 0.3], 'k' => 10]) ->execute(); // Check whether a result was capped by the limit $query = $db->query('orders')->where('range', ['column' => 3, 'min' => 0])->limit(100); $rows = $query->execute(); if ($query->truncated()) { // result set hit the limit; more matches exist on the server }
SQL
$db->sql("INSERT INTO orders (id, customer, amount) VALUES (99, 'Zoe', 999.0)"); $db->sql("CREATE TABLE archive AS SELECT * FROM orders WHERE amount > 500"); // Recursive CTEs and window functions $db->sql("WITH RECURSIVE r(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM r WHERE n<10) SELECT n FROM r"); $db->sql("SELECT id, ROW_NUMBER() OVER (PARTITION BY customer ORDER BY amount DESC) FROM orders");
User & role management
$db->createUser('admin', 's3cret-pw'); $db->setUserAdmin('admin', true); $db->createRole('analyst'); $db->grantPermission('analyst', 'select:orders'); // validated client-side $db->grantRole('alice', 'analyst'); if ($db->verifyUser('alice', 'alice-pw')) { echo "Authenticated\n"; } print_r($db->users()); // ['admin', 'alice'] print_r($db->roles()); // ['analyst']
Performance: persistent cURL sharing (PHP 8.5+)
By default the client pools keep-alive connections within a single PHP request. On PHP 8.5+, opt in to sharing DNS resolution, TLS sessions, and the connection pool across requests (e.g. between PHP-FPM invocations) for further latency reductions on the same daemon host:
// Enable with sensible defaults (DNS + TLS sessions + connection pool) $db = new Database('http://127.0.0.1:8453', persistentSharing: true); // Or pass an explicit list of CURL_LOCK_DATA_* constants use Visorcraft\MongrelDB\Transport\CurlTransport; $transport = new CurlTransport(persistentSharing: [\CURL_LOCK_DATA_DNS]);
Off by default; silently degrades to per-request pooling on PHP 8.4.
CURL_LOCK_DATA_COOKIE is rejected - it would leak cookies across requests and
is unsafe for a stateless database client.
Custom HTTP transport
use Visorcraft\MongrelDB\Transport\CurlTransport; $transport = new CurlTransport(timeout: 60, connectTimeout: 5); $client = new MongrelDB('http://127.0.0.1:8453', token: 'secret', transport: $transport); $db = new Database(client: $client);
Error handling
use Visorcraft\MongrelDB\Exceptions; try { $db->put('orders', [1 => 1]); // duplicate PK } catch (Exceptions\ConstraintException $e) { echo "Constraint: {$e->errorCode}\n"; // UNIQUE_VIOLATION } catch (Exceptions\AuthException $e) { echo "Not authorized: {$e->getMessage()}\n"; } catch (Exceptions\NotFoundException $e) { echo "Not found: {$e->getMessage()}\n"; } catch (Exceptions\ConnectionException $e) { echo "Can't reach daemon: {$e->getMessage()}\n"; } catch (Exceptions\MongrelDBException $e) { echo "Error: {$e->getMessage()}\n"; }
API reference
Database class
| Method | Description |
|---|---|
health(): bool |
Check daemon health |
tables(): array |
List table names |
createTable(string $name, array $columns): int |
Create a table |
dropTable(string $name): void |
Drop a table |
count(string $table): int |
Row count |
put(string $table, array $cells, ?string $key): array |
Insert a row |
upsert(string $table, array $cells, ?array $update, ?string $key): array |
Upsert a row |
delete(string $table, int $rowId): void |
Delete by row ID |
deleteByPk(string $table, mixed $pk): void |
Delete by primary key |
query(string $table): QueryBuilder |
Start a native query |
sql(string $sql): array |
Execute SQL |
schema(): array |
Full schema catalog |
schemaFor(string $table): array |
Single table schema |
createUser(string $user, string $pw): void |
Create a user |
dropUser(string $user): void |
Drop a user |
alterPassword(string $user, string $pw): void |
Change a password |
verifyUser(string $user, string $pw): bool |
Verify credentials |
setUserAdmin(string $user, bool $isAdmin): void |
Grant/revoke admin |
users(): array |
List usernames |
createRole(string $name): void |
Create a role |
dropRole(string $name): void |
Drop a role |
roles(): array |
List role names |
grantRole(string $user, string $role): void |
Grant role to user |
revokeRole(string $user, string $role): void |
Revoke role from user |
grantPermission(string $role, string $perm): void |
Grant permission |
revokePermission(string $role, string $perm): void |
Revoke permission |
procedures(): array |
List procedures |
procedure(string $name): array |
Get a procedure |
createProcedure(array $proc): array |
Install/replace a procedure |
dropProcedure(string $name): void |
Drop a procedure |
callProcedure(string $name, array $args): mixed |
Call a procedure |
compact(): array |
Compact all tables |
compactTable(string $name): array |
Compact one table |
beginTransaction(): Transaction |
Start a batch |
QueryBuilder class
| Method | Description |
|---|---|
where(string $type, array $params): static |
Add a native condition |
projection(array $columnIds): static |
Set column projection |
limit(int $limit): static |
Set row limit |
build(): array |
Build the request payload |
execute(): array |
Run the query |
Transaction class
| Method | Description |
|---|---|
put(string $table, array $cells): static |
Stage an insert |
upsert(string $table, array $cells, ?array $update): static |
Stage an upsert |
delete(string $table, int $rowId): static |
Stage a delete |
deleteByPk(string $table, mixed $pk): static |
Stage a delete by PK |
commit(?string $key): array |
Commit atomically |
rollback(): void |
Discard all operations |
count(): int |
Number of staged operations |
Building and testing
The test suite uses PHPUnit 12 and is fully offline - it does not require a running daemon (a mock transport intercepts all HTTP calls).
composer install composer test # runs the full suite vendor/bin/phpunit # equivalent
The suite covers 273 tests across SQL-injection hardening, JSON edge cases (INF/NAN, malformed UTF-8, recursion), transport behavior, transaction state machines, and the optional persistent-sharing feature.
Contributing
Contributions are welcome. Please:
- Open an issue first for non-trivial changes.
- Add focused tests near your change - the suite must stay green.
- Keep PHP 8.4 as the minimum supported version (PHP 8.5-only features must
degrade gracefully, detected at runtime via
function_exists/defined). - Match the existing style: strict types,
declare(strict_types=1), tabs,readonlyproperties where applicable, and#[\Override]on overridden methods.
License
Dual-licensed under the MIT License or the Apache License, Version 2.0, at your option. See LICENSE for the full text of both licenses.
SPDX-License-Identifier: MIT OR Apache-2.0
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-07