techone / database
PDO database library
Requires
- php: >=8.0
- ext-pdo: *
Requires (Dev)
- php-coveralls/php-coveralls: ^2.7
- phpunit/phpunit: ^9.6
This package is auto-updated.
Last update: 2026-08-30 07:05:15 UTC
README
A lightweight PDO database library for PHP.
The currently supported databases are:
| type | database | PDO driver |
|---|---|---|
| mysql | MySQL | pdo_mysql |
| pgsql | PostgreSQL | pdo_pgsql |
| sqlite | SQLite | pdo_sqlite |
| sqlsrv | SQL Server | pdo_sqlsrv |
Requirements
- PHP >= 8.0
- ext-pdo
- The PDO driver for your database, e.g.
pdo_mysql,pdo_pgsqlorpdo_sqlite. For SQL Server, install the pdo_sqlsrv extension (not bundled with PHP).
Installation
Use Composer
composer require leeqvip/database
Usage
Quick start
require_once './vendor/autoload.php'; use Leeqvip\Database\Manager; $config = [ 'type' => 'mysql', // mysql, pgsql, sqlite, sqlsrv 'hostname' => '127.0.0.1', 'database' => 'test', 'username' => 'root', 'password' => 'abc-123', 'hostport' => '3306', 'charset' => 'utf8mb4', ]; $manager = new Manager($config); $connection = $manager->getConnection(); // lazily creates and caches one Connection // Returns all matching rows as an array of associative arrays $rows = $connection->query('SELECT * FROM `users` WHERE `id` = :id', ['id' => 1]); // Returns the number of affected rows $count = $connection->execute( 'UPDATE `users` SET `name` = :name WHERE `id` = :id', ['name' => 'joker', 'id' => 1] );
Both named parameters and positional placeholders are supported. Numeric keys in the bind array are converted to 1-based positional parameters:
$rows = $connection->query('SELECT * FROM `users` WHERE `id` = ?', [1]);
Configuration
Only type is required; everything else has a sensible default.
| key | description | default |
|---|---|---|
type |
Connector name: mysql, pgsql, sqlite, sqlsrv |
(required) |
hostname |
Server host | 127.0.0.1 |
hostport |
Server port | 3306 |
database |
Database name. For SQLite, the path to the database file | '' |
username |
Username | '' |
password |
Password | '' |
charset |
MySQL connection charset | — |
socket |
MySQL unix socket; takes precedence over hostname/hostport |
— |
dsn |
Full DSN string; bypasses the auto-generated DSN | — |
params |
PDO attributes, merged over the defaults below | — |
The default PDO attributes (MySQL and SQLite) are:
[
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false,
]
Note that PDO::ATTR_EMULATE_PREPARES is disabled by default, so queries use native prepared statements where the driver supports them. (The pgsql and sqlsrv connectors use the same defaults minus PDO::ATTR_EMULATE_PREPARES.)
SQLite
SQLite only needs the path to the database file:
$connection = (new Manager([ 'type' => 'sqlite', 'database' => '/path/to/database.sqlite', ]))->getConnection();
Custom DSN
If the auto-generated DSN is not enough, pass a complete one and it will be used as-is:
$config = [ 'type' => 'mysql', 'dsn' => 'mysql:unix_socket=/tmp/mysql.sock;dbname=test;charset=utf8mb4', 'username' => 'root', 'password' => 'abc-123', ];
Raw PDO access
$pdo = $connection->getPdo(); // lazily connects on first access $connection->connect(); // or connect explicitly $statement = $connection->getPdo()->prepare('SELECT VERSION()'); $statement->execute();
You may also skip the Manager entirely and use Leeqvip\Database\Connection directly with the same config array.
Transactions
$connection->beginTransaction(); try { $connection->execute( 'UPDATE `accounts` SET `balance` = `balance` - :amount WHERE `id` = :id', ['amount' => 100, 'id' => 1] ); $connection->execute( 'UPDATE `accounts` SET `balance` = `balance` + :amount WHERE `id` = :id', ['amount' => 100, 'id' => 2] ); $connection->commit(); } catch (\Throwable $e) { $connection->rollBack(); throw $e; }
Or use the transaction() helper, which commits automatically when the callback returns and rolls back and re-throws when it throws. The callback receives the connection:
use Leeqvip\Database\Connection; $balance = $connection->transaction(function (Connection $db) { $db->execute( 'UPDATE `accounts` SET `balance` = `balance` - :amount WHERE `id` = :id', ['amount' => 100, 'id' => 1] ); return $db->query('SELECT `balance` FROM `accounts` WHERE `id` = :id', ['id' => 1])[0]['balance']; });
Transactions can be nested. The inner ones are simulated with savepoints, so an inner rollBack() only undoes its own work and leaves the outer transaction intact. This also applies to transaction() calls inside a transaction() callback.
Two things to be aware of:
- Don't call
commit()orrollBack()manually inside atransaction()callback. If the callback rolls back and then returns,transaction()throws aLogicExceptionwhen it tries to commit; and anything the callback already committed manually cannot be undone by an outerrollBack(). - Some statements end the transaction implicitly, e.g. DDL statements like
ALTER TABLEcommit on MySQL. The library does not detect this; instead its internal level is reset after everycommit()/rollBack(), so it never gets stuck and the database reports the mismatch on the next statement that needs a transaction.
Exceptions
- A missing
typeor an unknown connector throwsInvalidArgumentExceptionwhen the connection object is created (i.e. ingetConnection()). - Calling
commit()orrollBack()without an active transaction throwsLogicException. - Connection and query failures throw
PDOException.
Testing
composer install
vendor/bin/phpunit
License
This project is licensed under the Apache 2.0 license.