kinetis / persistence
Request-scoped SQL transaction safety net, runtime-matched native DB drivers (mysqli async, ext-pgsql async, PDO), and the connection factory for Kinetis — MySQL and Postgres.
Requires
- php: ^8.4
- kinetis/framework: ^1.8.1
- revolt/event-loop: ^1.0.9
Requires (Dev)
- infection/infection: ^0.35.0
- kinetis/mcp: ^1.5.1
- kinetis/queue: ^1.3.1
- nyholm/psr7: ^1.8.2
- phpstan/phpstan: ^2.2.8
- phpunit/phpunit: ^13.3.3
- vimeo/psalm: ^6.17
Suggests
- ext-mysqli: Needed by the native MySQL driver (Kinetis\Persistence\Driver\MysqliAsyncClient) — the default under FrankenPHP worker mode via DB_DRIVER=auto.
- ext-pdo_mysql: Needed by the PDO MySQL driver (Kinetis\Persistence\Driver\PdoMysqlClient) — the default under PHP-FPM via DB_DRIVER=auto.
- ext-pdo_pgsql: Needed by the PDO Postgres driver (Kinetis\Persistence\Driver\PdoPgsqlClient) — the default under PHP-FPM via DB_DRIVER=auto.
- ext-pgsql: Needed by the native Postgres driver (Kinetis\Persistence\Driver\PgsqlAsyncClient) — the default under FrankenPHP worker mode via DB_DRIVER=auto.
- ext-sockets: Needed alongside ext-pgsql by the native Postgres driver (Kinetis\Persistence\Driver\PgsqlAsyncClient), which ends a connection's transport with socket_shutdown() to abandon a statement without blocking the event loop.
Provides
None
Conflicts
None
Replaces
None
README
kinetis/persistence
Request-scoped SQL transaction safety net and connection factory for Kinetis
Part of Kinetis, a non-blocking PHP framework for API-first applications, developed in the kinetis-dev/kinetis monorepo.
MySQL and Postgres through runtime-matched drivers — native
ext-mysqli/ext-pgsql async clients under a persistent worker, PDO
under boot-and-die — all presenting this package's own
Contract\SqlLink/Contract\SqlTransaction abstraction, so nothing
above the driver needs to know which one it's talking to.
use Kinetis\Persistence\Contract\SqlTransaction; use Kinetis\Persistence\SqlConnectionFactory; use Kinetis\Persistence\TransactionGuard; $db = SqlConnectionFactory::fromConfig($config); $guard = new TransactionGuard($logger); $guard->transaction($db, static function (SqlTransaction $tx): void { $tx->execute('UPDATE inventory SET stock = stock - 1 WHERE sku = ?', ['SKU-1']); });
SqlConnectionFactory::fromConfig() builds a runtime-matched driver
client from Kinetis\Config (the DB_* keys below, or their
DB_{NAME}_* named-connection equivalents), bound under
Contract\MysqlLink/Contract\PostgresLink automatically once
DB_CONNECTION is set — this package's bootstrap registers it before
the application's own bootstrap.php, which wins on the same binding.
TransactionGuard is the Kinetis-specific piece: request-scoped,
autowired fresh per RequestScope like any other unregistered class,
tracking every transaction it starts so a request that throws before
committing or rolling back doesn't leak an open transaction into
whatever the pooled connection is reused for next. Every entry point
that owns a RequestScope for one unit of work — HTTP, the CLI, MCP
over stdio, and a queue worker's jobs — wires rollbackDangling() into
that scope's disposal automatically whenever this package is installed;
it is a no-op for a unit of work that never opens a transaction.
Optional: an application with no database at all can skip this package
entirely — Kinetis\Http\Kernel degrades gracefully (class_exists()
check, no dispose hook registered) when it isn't installed.
Provides
Installing this package is what opts it in — it registers the
following automatically, through the extra.kinetis declaration in its
composer.json (see
kinetis.dev/docs/cli.html):
- Service binding: with
DB_CONNECTIONset, the default connection is built and bound under its dialect contract (Kinetis\Persistence\Contract\MysqlLinkorContract\PostgresLink) before your ownbootstrap.phpruns — your registration wins on the same binding. Inert whenDB_CONNECTIONis unset.
Nothing else — no commands, routes, middleware, event listeners, or MCP tools.
Configuration
Read from the environment (or .env) via Kinetis\Config. Every key
is scoped.
| Key | Default | Purpose |
|---|---|---|
DB_CONNECTION |
(required) | mysql or pgsql. |
DB_HOST |
127.0.0.1 |
Server host. |
DB_PORT |
3306 / 5432 |
Per dialect. |
DB_NAME |
app |
Database name. |
DB_USER |
app |
User. |
DB_PASSWORD |
(required) | Password. |
DB_DRIVER |
auto |
auto (native under FrankenPHP worker mode or RoadRunner, PDO otherwise), native, or pdo. |
DB_CHARSET |
utf8mb4 (MySQL) |
Connection charset. |
DB_COLLATION |
— | MySQL collation (SET NAMES ... COLLATE). |
DB_SSLMODE |
— | disable/require/verify-ca/verify-full on every driver; libpq additionally accepts allow/prefer. |
DB_SSL_CA |
— | CA bundle path for the verify modes. |
DB_SSL_CERT |
— | Client certificate for mutual TLS; requires DB_SSL_KEY. |
DB_SSL_KEY |
— | Client private key; requires DB_SSL_CERT. Postgres requires 0600 permissions. |
DB_CONNECT_TIMEOUT |
— | Seconds. |
DB_APP_NAME |
— | Postgres application_name. |
DB_COMPRESSION |
— | MySQL protocol compression. |
DB_MAX_CONNECTIONS |
8 |
Async drivers' pool width — per worker thread under FrankenPHP, per worker process under RoadRunner. |
DB_WARM_CONNECTIONS |
0 |
Connections opened at boot instead of first use — load-bearing for the mysqli driver under worker mode. |
Scoped keys follow the named-connection convention — the connection
name inserts after the first segment: DB_HOST + reporting → DB_REPORTING_HOST.
Full reference across every package:
kinetis.dev/docs/config.html.
Installation
composer require kinetis/persistence
Requires PHP 8.4+ and kinetis/framework,
plus the extension for the driver you use: ext-mysqli, ext-pgsql
(with ext-sockets), ext-pdo_mysql or ext-pdo_pgsql. Full documentation:
kinetis.dev/docs/persistence.html.
License
MIT — see LICENSE.