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.ini sections
  • Choose Connection::connect() vs open() for apps and tests
  • Avoid per-request disconnect() under RoadRunner
Before you start

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:

MethodPurpose
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()

APIPoolingTypical use
Connection::connect('default')Yes — via connectionManager()Application code, table()
Connection::open([...])No — new instanceTests, one-off configs

table('tasks', null, 'db_pgsql') resolves the third argument through the manager:

  1. Registered name (register())
  2. settings.ini section ([db_pgsql])
  3. 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-password

Wire 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 = 1

Or 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; use table() / 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 the settings.ini section name exactly (db_pgsql, not pgsql).

What’s next

Configuration

Define [db] and named sections.

Performance

Pooling plus query patterns.

RoadRunner

Worker boot and shutdown hooks.