sshilko / php-sql-mydb
Simple yet powerful PHP wrapper for MySQL
Requires
- php: >=8.3
- ext-mysqli: *
- ext-mysqlnd: *
- psr/log: ^1
Requires (Dev)
Suggests
- ext-ast: Performance boost for phan
- ext-pcntl: Real signal dispatch for query termination (a no-op polyfill is bundled)
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-25 01:01:50 UTC
README
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. 🐬
Table of contents
- 🎯 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
🎯 Why MyDb? Pros and cons
| ✅ Pros | ⚠️ Cons |
|---|---|
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.
✨ Highlights
- 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
📦 Installation
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.
🏷️ Versioning / Releases
| 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
🚀 Quick start
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.
🔌 Everything is injectable
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.
🛡️ Safe defaults
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
⚡ Performance
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
🔧 Reliability
- 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
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.
🧩 Compatibility
- PHP >= 8.3
- MySQL 8.0 (
mysqli+mysqlnd) and 5.7
💡 Best use cases
- 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
🚫 Out of scope
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.
💭 Why this library exists
- 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
🤝 Contributing
Please read the contributing document first. All quality gates run in
Docker; see CONTRIBUTING for the supported workflow.
👤 Authors and license
- Sergei Shilko contact@sshilko.com
Licensed under the MIT License. See also AUTHORS.