-
Notifications
You must be signed in to change notification settings - Fork 0
FAQ
Short answers to questions that have come up more than once. For anything that doesn't fit here, open a discussion or an issue.
Yes. DBAL has no runtime dependencies beyond ext-pdo. The other
InitORM packages (query-builder, database, orm) depend on DBAL —
not the other way around.
Because PDO is the only first-party PHP abstraction that gives you the same surface across MySQL, PostgreSQL, SQLite, and a handful of other engines. DBAL leans on that uniformity rather than re-implementing it.
No. That is the deliberate scope of
initorm/query-builder.
DBAL only runs strings.
Not in this package. PDO is blocking by design. If you run inside Swoole/ReactPHP, look for the runtime's native driver — it will give you better throughput than wrapping PDO.
Connection is lazy on purpose. Building a Connection is cheap;
opening a TCP/SSL connection to the database is not. The lazy default
lets you construct connections eagerly in your container without
paying for the ones you don't use.
If you want to validate credentials at boot, call getPDO():
$db = new Connection([...]);
$db->getPDO(); // throws ConnectionException if anything is wrong$db->disconnect();
$db->setDatabase('other');
$db->getPDO();setDatabase() throws while the connection is open — that's why the
disconnect() call comes first. See Connection.
clone() the primary connection, change the host, and route on the
caller side:
$primary = new Connection($writeCfg);
$replica = $primary->clone()->setHost('replica.internal');DBAL itself does not pick replicas automatically — that's a routing concern best handled by your application or a SQL-aware proxy (ProxySQL, PgBouncer).
Persistent connections are a sharp tool. They survive across requests under most SAPIs, which means an aborted transaction leaks to the next request that picks the connection up. They also keep prepared-statement caches alive across migrations. See Transactions for the failure modes.
Yes — set the dsn credential and DBAL will not touch it:
new Connection([
'dsn' => 'mysql:host=db;dbname=shop;charset=utf8mb4',
]);This is how you reach drivers DBAL doesn't know about (Oracle, IBM DB/2, MS SQL …).
numRows() forwards to PDOStatement::rowCount(). The PHP manual says
this clearly: "For most databases, PDOStatement::rowCount() does not
return the number of rows affected by a SELECT statement."
Use a separate SELECT COUNT(*), or count rows() after the fetch.
Not through Connection::query() — DBAL is named-only. If you need
positional binding, drop down to getPDO():
$stmt = $db->getPDO()->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$id]);PDO does not support array binding. Build the placeholders yourself:
$ids = [1, 2, 3];
$marks = implode(',', array_fill(0, count($ids), '?'));
$stmt = $db->getPDO()->prepare("SELECT * FROM users WHERE id IN ($marks)");
$stmt->execute($ids);Or, with named placeholders:
$ids = [1, 2, 3];
$keys = array_map(static fn(int $i) => ":id{$i}", array_keys($ids));
$params = array_combine($keys, $ids);
$db->query('SELECT * FROM users WHERE id IN (' . implode(',', $keys) . ')', $params);A higher layer (a query builder) is a better place to abstract this.
DBAL has no special API. Build a single multi-row statement:
$rows = [['n' => 'a', 'e' => 'a@x'], ['n' => 'b', 'e' => 'b@x']];
$parts = [];
$args = [];
foreach ($rows as $i => $r) {
$parts[] = "(:n{$i}, :e{$i})";
$args["n{$i}"] = $r['n'];
$args["e{$i}"] = $r['e'];
}
$db->query('INSERT INTO users (name, email) VALUES ' . implode(',', $parts), $args);Again, this is exactly the kind of thing
initorm/query-builder
exists to write for you.
2.x behaviour. The return type is non-nullable array. See
Migration from 1.x.
$mapper = $db->query('SELECT * FROM events ORDER BY id');
$mapper->asAssoc();
while (($row = $mapper->row()) !== null) {
handle($row);
}
$mapper->closeCursor();PDO buffers based on driver attributes. For MySQL, also consider
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false in your options
credential.
Yes — that is how PDO::FETCH_CLASS works. Properties are written
before the constructor runs. If your constructor needs the raw input,
use FETCH_CLASS | FETCH_PROPS_LATE via the raw statement:
$stmt = $db->getPDO()->prepare('SELECT * FROM users');
$stmt->execute();
$users = $stmt->fetchAll(\PDO::FETCH_CLASS | \PDO::FETCH_PROPS_LATE, User::class);Yes — it is an in-memory append-only list with no rotation. Drain it yourself in long-running workers, or leave the buffer off in production.
It is a single entry point for critical failures. PSR-3 callers can log at any level themselves once they have the message. The minimal API keeps the credential resolution logic readable; richer loggers are better assembled in user code.
Almost never. SQLite in-memory is fast enough (sub-millisecond), and real PDO behaviour catches binding bugs that mocks happily ignore. See Testing Your Application.
The dialects are different — see the table in Testing Your Application. Run an integration suite against the real engine in CI as well.
8.0, 8.1, 8.2, 8.3, 8.4 in CI. Older PHP is not supported and not tested.
Yes. Breaking changes wait for the next major. The 1.x → 2.x bump collected several breaking fixes; the 2.x line will be additive only.
The 1.x branch is in maintenance-only mode. Security fixes are considered case-by-case for one minor cycle after 2.0 ships. Plan to upgrade.
InitORM DBAL · MIT · maintained by Muhammet ŞAFAK · part of the InitORM stack
Getting Started
Core
Cross-Cutting
Reference
Upgrading
Project