MyDb is a small, production-hardened PHP client for MySQL. It speaks raw SQL at
native mysqli/mysqlnd speed β no ORM, no query-language abstraction, no framework
lock-in. You get a clean, fully typed API wrapped around opinionated defaults that make
MySQL behave like a traditional SQL database: strict by default, explicit transactions,
sanely bounded timeouts, and utf8mb4/UTC everywhere.
Think of it as a thin, well-tested layer between your PHP code and MySQL β nothing more, nothing less.
Simple to learn, fast to run, safe by default β the SQL client you stop thinking about. π¬
- π― Why MyDb? Pros and cons
- β¨ Highlights
- π¦ Installation
- π·οΈ Versioning / Releases
- π Quick start
- π Everything is injectable
- π‘οΈ Safe defaults
- β‘ Performance
- π§ Reliability
- β Code quality
- π§© Compatibility
- π‘ Best use cases
- π« Out of scope
- π Why this library exists
- π€ Contributing
- π€ Authors and license
| β Pros | |
|---|---|
Zero runtime dependencies beyond psr/log β deterministic, auditable installs with a tiny vendor/ footprint |
A small project with a single maintainer; you are closer to the code than with a big ecosystem framework |
Native mysqlnd speed β no query parsing, no abstraction layers between you and the driver |
Raw SQL is on you: you write and maintain the queries, and you own escaping (the escape() helper, or prepare()/execute() for user values) |
| Fully typed and deeply analyzed β 100% Psalm type inference, six static analyzers green in CI | Requires PHP >= 8.3; older LTS runtimes are not supported |
| Unit-tested against a real MySQL 8.0 server β 284 tests, 877 assertions, high coverage | MySQL 8.0 only today; MariaDB is not yet compatible |
Opinionated safe defaults β TRADITIONAL SQL mode, autocommit = 0, explicit timeouts, utf8mb4, UTC |
Defaults are deliberate; atypical setups may want to tune MydbOptions first |
| Production-ready reliability β signal trapping, connection retry, commit on graceful shutdown | Prepared statements are opt-in: server-side binding is available on demand for user values; the raw path keeps the raw speed |
| Interface-driven design β every collaborator (logging, options, mysqli, environment, query builder, listener) is swappable and mockable | DML helpers cover the common cases; complex statements are written as raw SQL by design |
| Raw SQL, no DSL β zero learning curve if you already know SQL, and portable across MySQL deployments | Manual result mapping; there is no ORM to do it for you |
If you want an ORM, a query-language abstraction, or a full framework, look at Doctrine or Eloquent. If you want a minimal, fast, predictable SQL client that gets out of your way, MyDb is for you.
- Raw SQL, first-class β
select(),query(),command(),insert(),update(),delete(),replace()plus helper methods for common bulk work - Prepared statements, opt-in β
prepare()/execute()for parameterized execution with mysqli server-side binding; the raw fast path stays untouched - Helper DML β array-based
insertOne(),insertMany(),updateWhere(),deleteWhere(),replaceOne()withMydbExpressionsupport for raw SQL fragments - Friendly transactions β
autocommit = 0, explicitcommit()/rollback(), and an automatic commit on graceful shutdown so you never lose half a transaction - Readonly connections β a first-class mode with
READ COMMITTEDisolation and read-only InnoDB optimizations - Strict by default β
TRADITIONALSQL mode ("give an error instead of a warning"), strict MySQL error reporting, and PSR-3 logging of everything unusual - Explicit timeouts β connect, read, server-side SELECT, and idle timeouts so one slow query cannot hang your process forever
- Signal aware β traps
SIGTERM/SIGINT/SIGHUP, stops in-flight queries, and respects client disconnects under php-fpm - Zero-friction integration β PSR-4
sql\namespace, PSR-3 logging, constructor injection, no run-time or compile-time steps
composer require sshilko/php-sql-mydb
Requires ext-mysqli and ext-mysqlnd (bundled with the standard PHP distribution).
Composer installs the latest stable release, currently the 2.x line.
| Version | Status | Released | PHP |
|---|---|---|---|
| 1.0.0 | Legacy release | 2022-12-04 | 7.4 β 8.2 |
| 2.0.0 | β Latest stable release | 2024-01-03 | 8.0 β 8.2 |
| 3.x (current) | π In development, not released yet | β | >= 8.3 |
What's new and improved in 3.x:
- Upgraded to PHP 8.3 β strict types, typed properties, constructor promotion, and union types throughout the codebase
ext-pcntlis now optional: a bundled no-op polyfill keeps Windows and non-pcntl environments working (a hard requirement in2.x)- New
MydbFactoryfor assembling connections with library defaults, including dedicated read-only options (createReadonlyOptions()) - Focused on MySQL 8.0 (MySQL 5.7 support deprecated)
- Psalm infers 100% of types with zero errors, and a taint-analysis pass was added
use sql\MydbCredentials;
use sql\MydbFactory;
use sql\MydbLogger;
$credentials = new MydbCredentials('127.0.0.1', 'root', 'secret', 'app', 3306);
$mydb = (new MydbFactory())->create($credentials, new MydbLogger());
$mydb->beginTransaction();
$id = $mydb->insertOne(['name' => 'Ada Lovelace'], 'users'); // '9' β auto-increment id
$mydb->insertMany([['Grace Hopper'], ['Linus Torvalds']], ['name'], 'users');
$rows = $mydb->select('SELECT id, name FROM users ORDER BY id ASC');
$mydb->commitTransaction();Read-only replicas get a dedicated factory helper:
$options = (new MydbFactory())->createReadonlyOptions(); // READ COMMITTED, autocommit on
$replica = (new MydbFactory())->create($credentials, new MydbLogger(), $options);A complete runnable example lives in examples/example.php.
MyDb is assembled from small, interface-driven parts β and every single one can be
swapped, extended, or mocked through MydbFactory::create(). Omit any dependency and
the factory fills in production defaults, so flexibility costs nothing when you do not
need it:
$mydb = (new MydbFactory())->create(
credentials: $credentials, // MydbCredentialsInterface
logger: new MydbLogger(), // Psr\Log\LoggerInterface
options: $myOptions, // MydbOptionsInterface
mysqli: $myMysqli, // MydbMysqliInterface
environment: $myEnvironment, // MydbEnvironmentInterface
queryBuilder: $myQueryBuilder, // MydbQueryBuilderInterface
listener: $myListener, // MydbListenerInterface
);- Tracing and debugging with a listener β implement
MydbListenerInterface(or extendMydbListener) and subscribe to lifecycle events: query begin/end and connection begin/end. Every event carries metadata β the SQL text, success flag, host, and database β and returningfalsestops dispatch. The built-inInternalListenerlogs connection events via your PSR-3 logger; query events are left for your own listener. A minimal SQL tracer:
use sql\MydbEvent\InternalQueryBegin;
use sql\MydbEventMetadataInterface;
use sql\MydbListener;
final class SqlTracer extends MydbListener
{
protected function onEvent(MydbEventMetadataInterface $event): ?bool
{
if (InternalQueryBegin::class === $event->getEventName()) {
var_dump($event->getEventMetadata()['sql'] ?? null);
}
return null; // keep dispatching
}
}
$mydb = (new MydbFactory())->create($credentials, listener: new SqlTracer());- Your own query builder β
MydbQueryBuilderInterfaceis the single place where SQL for the helper methods is constructed (buildWhere,buildInsertMany,buildUpdateWhere,buildDeleteWhere, escaping). Bring your own implementation to customize quoting, add dialect behavior, or go further withMydbExpression. - Environment dependencies are abstracted β
MydbEnvironmentInterfacewraps the global PHP surface the library touches:set_error_handler,error_reporting,ini_set,ignore_user_abort,mysqlndnetwork timeouts, and signal trapping. The library never reaches into globals directly, which makes it fully testable and predictable on any platform. - Even
mysqliitself is interfaced βMydbMysqliInterfaceis a complete facade overmysqli/mysqli_result: connecting, driver options, result reading, transactions, and warnings. Use your own implementation to run against an alternative transport or to test without a live MySQL server.
Every connection is set up to be strict, explicit, and deterministic:
TRADITIONALand strict SQL modes β MySQL returns an error instead of a warningautocommit = 0with explicitcommit()/rollback()onMydb- Commit is attempted on graceful shutdown (
SIGTERM,SIGINT,SIGHUP), so in-flight transactions are not silently dropped utf8mb4character set andUTCtimezone on the sessionmysqlierror reporting configured so silent data loss is not possible- Connection retry for resilient reconnect
MyDb is tuned for high-throughput, low-latency workloads:
MYSQLI_OPT_NET_READ_BUFFER_SIZEandMYSQLI_OPT_NET_CMD_BUFFER_SIZEincreased- Resultsets moved to PHP userspace memory with
MYSQLI_STORE_RESULT_COPY_DATA fetch_all()frommysqlndfor fast, allocation-friendly result materialization- Async command execution for parallel pipelines
READ COMMITTEDscan/read-only InnoDB optimizations on readonly connections- No overhead from ORM mapping or query abstraction
- Connect timeout of 5 seconds, client-side read timeout of 90 seconds for any query, server-side SELECT timeout of 89 seconds, and a 7200-second idle timeout
mysqlnd.net_read_timeouthonoured on every querypcntlsignal dispatch (SIGTERM,SIGINT,SIGHUP) cancels in-flight queries and shuts down cleanly; a no-op polyfill is bundled for Windows- php-fpm client disconnect (
ignore_user_abort) is respected - Every failure path raises typed exceptions under
sql\MydbExceptionβ no silentfalse
Code quality is not a checklist item here; it is the build. Every commit is verified by:
- Unit tests against a real MySQL 8.0 server β >=280 tests, >=870 assertions, with coverage badges generated on CI
- Psalm β 100% of types inferred, zero errors, plus a taint-analysis pass and an auto-fix (alter) pass for type annotations
- PHPStan at the highest level
- Phan with
ext-ast, PHPMD, PHPCS (PSR-12), PHPCPD for duplication, and PDepend for architecture metrics - PSR-12 formatting enforced by
phpcs/phpcbfin pre-commit hooks and CI
The codebase is deliberately small: the complete library is ~26 source files, each with a single responsibility, typed properties, constructor promotion, and PSR-12 formatting. It is easy to read end-to-end.
- PHP >= 8.3
- MySQL 8.0 (
mysqli+mysqlnd) and 5.7
- High-performance, low-latency, data-intensive applications
- Services that favor hand-written SQL over ORM magic
- Codebases that want a thin, auditable database layer without framework lock-in
- Projects that want parameterized execution for user-controlled values at high
concurrency (
prepare()/execute()are built in) and no extra dependencies - Easy drop-in integration into existing applications
MyDb deliberately stays a component, not a framework. To keep focus and the codebase minimal, it does not provide:
- Input validation or an API facade
- Object-relational mapping (ORM)
- Active record
- Repository pattern
- Data import/export
Please reuse existing solutions that best fit your requirements.
- MySQL is fast, reliable, and scalable β and so is the PHP runtime
- Value developers' time; do not add complexity where it is not needed
- Measure application performance with real-world datasets and organic load
- Optimize for real bottlenecks and remember that there is no
NoSQLsilver bullet - Do not optimize prematurely β CPU and memory are cheap
- Focus on architecture, learn from others, and improve over time
Please read the contributing document first. All quality gates run in
Docker; see CONTRIBUTING for the supported workflow.
- Sergei Shilko contact@sshilko.com
Licensed under the MIT License. See also AUTHORS.