Connections
This guide explains how Pionia Shop on port 8000 keeps one PDO alive per worker while Pionia Shop reads and writes products, projects, and customers. ConnectionManager pools connections across FPM requests and RoadRunner iterations.
What you will learn
- Resolve named connections from
settings.inisections - Choose
Connection::connect()vsopen()for apps and tests - Avoid per-request
disconnect()under RoadRunner
- Configuration —
[db]and named sections - RoadRunner — worker lifecycle on port 8000
How it works
Boot → scan settings.ini for [db], [db_pgsql], …
Request → connectionManager()->connection('default') → reuse PDO
Worker shutdown → disconnect() once (not per HTTP request)ConnectionManager
connectionManager() returns a process-scoped pool (Pionia\Porm\ConnectionManager). Each named connection opens once per PHP process and is reused across HTTP requests (FPM) or RoadRunner worker iterations.
$pdo = connectionManager()->connection('default');
connectionManager()->connection('db_pgsql');Helpers:
| Method | Purpose |
|---|---|
connection($name) | Get or create pooled Connection |
register($name, $connection) | Inject a connection (tests, custom DSN) |
disconnect($name?) | Drop pooled handle(s) — worker shutdown only |
has($name) | Whether a pool entry exists |
Do not call disconnect() between HTTP requests in FPM or RoadRunner. PDO stays alive for performance; only disconnect on worker shutdown.
Connection::connect() vs open()
| API | Pooling | Typical use |
|---|---|---|
Connection::connect('default') | Yes — via connectionManager() | Application code, table() |
Connection::open([...]) | No — new instance | Tests, one-off configs |
table('tasks', null, 'db_pgsql') resolves the third argument through the manager:
- Registered name (
register()) settings.inisection ([db_pgsql])- Inline config array (hashed key in the pool)
Configuration discovery
At boot, Pionia scans environment/settings.ini for sections that define both a driver (database_type / type) and database_name / database. Mark one section with default = 1.
[db]
database_type = sqlite
database_name = database.sqlite3
default = 1
[db_pgsql]
database_type = pgsql
database_name = pionia-shop
host = 127.0.0.1
username = app
port = 5432
; Password — use environment/.env (see below), not a committed literal# environment/.env (gitignored)
DB_PGSQL_PASSWORD=your-local-passwordWire the password into settings.ini via your bootstrap or read env('DB_PGSQL_PASSWORD') when building the connection in tests. Never commit real database credentials to the repository or copy them from documentation examples.
Pass the section name to table():
table('projects', null, 'db_pgsql')->all();Tests
Register an in-memory SQLite connection without touching settings.ini:
use Pionia\Porm\Driver\Connection;
$conn = Connection::open([
'type' => 'sqlite',
'database' => ':memory:',
'allow_object_cast' => false, // default — see security note below
]);
connectionManager()->register('default', $conn);RoadRunner
Workers boot once; ConnectionManager keeps PDO open. On shutdown, the framework may call connectionManager()->disconnect() — do not replicate that per request.
Connection info
table('products')->info(); // driver metadata from Piql
Query logging: set logging on the connection section or LOG_QUERIES=true in the environment.
[Object] column casts
Selecting payload[Object] runs unserialize() on the column value. This is disabled by default. Enable only for trusted data:
[db]
allow_object_cast = 1Or per connection: Connection::open([..., 'allow_object_cast' => true]) / PORM_ALLOW_OBJECT_CAST=true.
Related: Getting started · RoadRunner.
Common mistakes
- Calling
disconnect()after every Pionia Shop API request — destroys pooling gains on port 8000 workers. - Using
Connection::open()in production services — bypasses the pool; usetable()/connect()instead. - Enabling
[Object]casts for convenience — never on columns populated from client JSON in Pionia Shop apps. - Mismatching connection names — third
table()argument must match thesettings.inisection name exactly (db_pgsql, notpgsql).
What’s next
Configuration
Define [db] and named sections.
Performance
Pooling plus query patterns.
RoadRunner
Worker boot and shutdown hooks.