# ezSQL: a PHP CRUD layer that hides the driver behind one interface

> ezSQL is a PHP library that presents a single CRUD interface across mysqli, pgsql, sqlsrv, sqlite3 and PDO, with shortcut query methods, a table-definition DSL and a global function set. Version 5 renamed most of the public surface, so the library is best evaluated as a codebase to adopt fresh rather than one to upgrade in place.

**ezSQL/ezsql** — PHP class to make interacting with a database ridiculusly easy

- Repository: https://github.com/ezSQL/ezsql
- Website: http://ezsql.github.io/ezsql
- Stars: 865 · Forks: 278
- Language: PHP
- License: LGPL-3.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/ezsql-ezsql

## What the library is, and the version story behind its API

ezSQL describes itself as a class to make it very easy to deal with database connections, and an universal interchangeable CRUD system. The repository description puts it more plainly: a PHP class to make interacting with a database ridiculously easy.

The universal part is the point. The usage example shows a single initialize call where the driver is a parameter:

```php
require 'vendor/autoload.php';

// **** is one of mysqli, pgsql, sqlsrv, sqlite3, or Pdo.
use ezsql\Database;

$db = Database::initialize('****', [$dsn_path_user, $password, $database, $other_settings], $optional_tag);
```

Behind that call the library picks the right database class, and the README shows the equivalent explicit form, constructing a Config object and then the driver-specific class yourself. The five drivers are mysqli, pgsql, sqlsrv, sqlite3 and PDO.

The version history explains why the API looks the way it does, and the README is unusually open about it. This is version 5, which will break users of version 4. Version 4 has many modern programming practices which will break users of version 3. Version 3 broke version 2.1.7 in one major way, requiring PHP 5.6, which dropped the mysql extension support, and apart from that nothing changed in terms of using the library, only additional features were added.

That sequence is the single most important thing to know when evaluating the library. Three major versions, each breaking the one before, with the current one being version 5. The changes in version 5 are listed in detail: the global functions live in ezFunctions.php and now use a namespace, so using them requires an import statement at the top of the file. Class properties that were accessible through magic get and set methods are now PSR-1 camelCase. select was renamed to dbSelect on ez_mysqli. The selecting method was renamed to select and its behaviour changed.

The library also follows PSR-2, PSR-4 and PSR-11 conventions, and mostly PSR-1, which the README notes is still a work in progress.

## The factory and dependency injection combination

The library has a Database class, described as a combination of the Factory pattern with a Dependency Injection container hosting.

That combination is worth understanding, because it explains a lot about the shape of the code. A factory means you ask for a database by name and get the right implementation back, rather than newing up a driver-specific class and hoping you picked the right one. The dependency injection part means the configuration object is constructed separately and passed in, so the settings can be built from wherever your application happens to keep them, including a config file, an environment variable or a test fixture.

The explicit form in the README shows the same idea with the container doing the work:

```php
use ezsql\Config;
use ezsql\Database\ez_****;

$settings = new Config('****', [$dsn_path_user, $password, $database, $other_settings]);

$db = new ez_****($settings);
```

The README also notes that the library assumes the developer is using some sort of IDE with intellisense enabled, and that the comments and doc-block area will hold any missing documentation. That is an unusual admission, and it tells you where to look when a method's behaviour is unclear: the doc-block, not a separate manual.

For additional examples the README points at the phpunit tests, and makes a point about them that matters more than usual. The tests are fully functional integration tests, meaning they are live database tests, with no mocks. That is a deliberate design decision with real consequences. A test suite that talks to an actual database catches things mocks never would, such as a SQL dialect mistake or a column type mismatch, but it also means you need a database running to run the tests, and the suite is slower than a mocked one.

The library's own history shows the same instinct about moving with the ecosystem: version 3 dropped the mysql extension because PHP 5.6 required it.

## The shortcut query methods and the table DSL

The core of the API is a set of shortcut methods that build SQL for you.

The list includes having, groupBy, union, unionAll, orderBy, limit, where and select, along with create_select and others. The interesting ones are the joins, which all share a shape:

```php
innerJoin(string $leftTable = null, string $rightTable = null,
    string $leftColumn = null, string $rightColumn = null, string $tableAs = null, $condition = EQ);

leftJoin(string $leftTable = null, string $rightTable = null,
    string $leftColumn = null, string $rightColumn = null, string $tableAs = null, $condition = EQ);
```

rightJoin and fullJoin follow the same signature. The joins are expressed as method calls with a default condition of EQ, which is what makes the whole thing read like a query rather than like a string builder.

The table definition DSL is the other half. There is a create method that takes a table name and a set of schema calls, along with column, primary, index and drop:

```php
// Creates an database table
create('profile',
    // and with database column name, datatype
    // data types are global CONSTANTS
    // SEQUENCE|AUTO is placeholder tag, to be replaced with the proper SQL drivers auto number sequencer word.
    column('id', INTR, 11, AUTO, PRIMARY), // mysqli
    column('name', VARCHAR, 50, notNULL),
    column('email', CHAR, 25, NULLS),
    column('phone', TINYINT)
);
```

Two details there are worth flagging. The data types are global constants rather than strings, so the type names are checked by the interpreter rather than by a typo check. And the AUTO and SEQUENCE tags are placeholders that get replaced with the proper auto-number sequencer word for whichever SQL driver is in use, which is how one schema definition can target MySQL and PostgreSQL.

Version 5 added a related convenience. The table name with prefix can be preset and stored with tableSetup(name, prefix), or set with setTable(name) and setPrefix(append), and the selecting and new inserting methods can then be called without a table name. If nothing has been preset, false is returned. The README notes that this feature will be added to all the database CRUD access methods, with an ing ending added to each method name, and that the global functions taking a table name were removed in favour of the preset-table versions.

## Prepared statements, certificates and the utility methods

Two areas of the API deserve attention because they are the ones that decide whether you would use the library at all.

The first is query preparation, which is off by default:

```php
prepareOn(); // When activated will use prepare statements for all shortcut SQL Methods calls.
prepareOff(); // When off shortcut SQL Methods calls will use vendors escape routine instead. This is the default behavior.
```

So out of the box the shortcut methods escape values using the vendor's own routine rather than using bound parameters. That is the performance-friendly choice for the common case and the weaker one for correctness, since escaping and binding fail in different ways. Turn prepareOn on and every shortcut method uses prepared statements.

The second is the TLS material handling, which is a set of general methods added since version 2.1.7:

```php
create_cache(string $path = null);
secureSetup(string $key = 'certificate.key',
    string $cert = 'certificate.crt',
    string $ca = 'cacert.pem',
    string $path = '.'._DS
);
secureReset();
create_certificate(string $privatekeyFile = certificate.key,
    string $certificateFile = certificate.crt,
    string $signingFile = certificate.csr,
    string $ssl_path = null, array $details = [commonName => localhost]
);
```

The defaults line up with the files in the repository root: a cacert.pem and an openssl.cnf are both committed there, which suggests the certificate generation path is meant to work against them out of the box.

There are also two general helpers, to_string for arrays with a separator and clean for strings. The clean one was renamed in version 5, from cleanInput to clean_string, and createCertificate was renamed to create_certificate. Those renames are the pattern: version 5 moved a number of methods to snake_case while moving the class properties to camelCase, which is an odd-looking mixture and is one of the reasons a version 4 codebase does not port mechanically.

The README also mentions a new global get_results function for returning result sets in different formats, which is the kind of helper that tells you the library is used with template output in mind.

## Packaging, tests and the shape of the repository

Installation is a single Composer command, composer require ezsql/ezsql, and the distribution is on Packagist.

The repository is a PHP library with lib/ holding the code, tests/ holding the phpunit suite, and a phpunit.xml.dist. There is a CHANGELOG.md and a CONTRIBUTORS.md, a WIKI.md, and a composer.json.

Two directories are worth noting. The unsupported/ directory suggests the library keeps code around for drivers or features that are not supported in the current version, which is a useful convention when you want to know what was dropped and why rather than finding out from a fatal error. And the cacert.pem and openssl.cnf at the root are part of the TLS support path described above.

The CI badges cover Windows, Linux and macOS, so the library is tested on all three, which matches the driver coverage: an abstraction over mysqli, pgsql, sqlsrv, sqlite3 and PDO is only meaningful if the tests actually run on the platforms where those drivers live.

There is also a documentation wiki at the repository's wiki page, and the README is honest that it is not completely finished. That is a caveat worth carrying into an evaluation, because a library whose API is a large set of shortcut methods is exactly the kind of thing where the wiki is where you check whether a method does what you expect.

The licence is LGPL-3.0, which means the library can be used inside a larger application, with the copyleft applying to the library itself rather than to your code, though that is a question for your own legal review rather than for a README.

## Limits: global functions, magic constants and a long rename history

The limits are visible in what the README itself emphasizes.

The first is the global function surface. The shortcut methods are callable as global functions, which is convenient in a template file and uncomfortable in a large codebase, and version 5 requires an explicit import for each one you use:

```php
use function ezsql\functions\where;
// Or
use function ezsql\functions\{
    getInstance,
    selecting,
    inserting,
};
```

The group import syntax helps when you use several of them, but it is still an import list you have to maintain.

The second is the naming approach inside the DSL. Data types are global constants such as INTR, VARCHAR, CHAR, TINYINT, and modifiers are constants like PRIMARY, notNULL and NULLS. That is consistent with the era the library comes from and it does make the schema definitions readable, but it also means a global constant namespace and no way to use a type the library does not know about without dropping to raw SQL.

The third is the release history as a risk. Three major versions, each documented as breaking the one before, with the current one at version 5. The version 5 changes are large enough that they touch imports, method names, property naming conventions and behaviour, and the README states plainly that version 5 will break users of version 4. Adopting it in an existing codebase is a migration, not an upgrade.

The fourth is that the shortcut layer covers the common cases rather than all of them. There is no indication of a query builder that exposes arbitrary SQL, of parameterised input on the shortcuts unless you turn prepareOn on, or of a way to get a plain statement object for something the library does not model. The escape-or-prepare choice is a single global switch, not per call.

The fifth is that the documentation is acknowledged to be unfinished, and the primary documentation source is the doc-blocks in an IDE, which works well for someone with the right tooling and poorly for someone reading on a phone.

## ezSQL against PDO alone and against an ORM

Two comparisons make the choice clearer.

The first is against using PDO directly. PDO already gives you one interface across the same driver family, with prepared statements as the default rather than as an opt-in, and it is part of PHP itself, so it has no maintenance burden of its own. The difference is what ezSQL adds on top. You get shortcut methods that build the SQL for you, a table-definition DSL that speaks in global type constants, driver-specific SQL emitted for you from one schema, and a certificate setup path for encrypted connections. The cost is that you now have two layers, PDO under the hood and the shortcuts over it, and a dependency to keep current.

For a small project with straightforward queries, PDO on its own is usually the better answer, because it is simpler and prepared statements are the default rather than a flag you have to remember to set. The shortcut layer earns its keep when you have many similar queries across several drivers, or when you want the same schema definition to produce different SQL per backend.

The second comparison is against an ORM. The difference in approach is where the shape of the data lives. An ORM gives you objects, with identity mapping, lazy loading and relationships, and it takes over the schema. ezSQL gives you rows and lets you write the SQL, with helpers that remove the boilerplate but do not take over the model. A project with a rich domain and many relationships is better served by an ORM. A project with reporting queries and a handful of well-understood tables is better served by ezSQL, because the SQL stays visible and the driver stays swappable.

What neither comparison changes is the migration cost. If you already have a working data layer, adopting a library to make it easier is a project with a real payoff only if the pain is the boilerplate rather than the SQL itself.

## Conclusion

Adopt ezSQL when you want one database API across several PHP drivers and you are starting a project rather than maintaining one. Do not adopt it for an existing version 4 codebase without budgeting for the rename work, since version 5 breaks version 4 users by design. Verify first that the driver you use is one of mysqli, pgsql, sqlsrv, sqlite3 or PDO, that the shortcuts you plan to use exist in the version you install, and that your licensing review accepts LGPL-3.0 for a library you intend to ship inside a product.

## FAQ

### Which database drivers does ezSQL support?

The README lists mysqli, pgsql, sqlsrv, sqlite3 and PDO. You select the driver when initializing the Database class, either with Database::initialize or by constructing the driver-specific class with a Config object, and the library emits the appropriate SQL for the backend you chose.

### How do I install ezSQL and create a database connection?

Install with composer require ezsql/ezsql, then require the Composer autoloader and call Database::initialize with the driver name, the settings array holding the connection details and password, and the database name, plus an optional tag. The README also shows the equivalent explicit form using a Config object and the driver-specific class.

### What changed between ezSQL version 4 and version 5?

The README lists the changes and says version 5 will break version 4 users. Global functions moved into a namespace and need explicit imports, class properties previously accessed through magic get and set are now PSR-1 camelCase, select was renamed to dbSelect on ez_mysqli, selecting was renamed to select with changed behaviour, the selecting and inserting methods can be called without a table name, cleanInput was renamed to clean_string and createCertificate to create_certificate, and a global get_results function was added.

## Sources

- [ezSQL/ezsql on GitHub](https://github.com/ezSQL/ezsql)
- [License: LGPL-3.0](https://github.com/ezSQL/ezsql/blob/master/LICENSE)
- [Project website](http://ezsql.github.io/ezsql)
- [README](https://github.com/ezSQL/ezsql/blob/master/README.md)
- [Releases](https://github.com/ezSQL/ezsql/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ezsql-ezsql
