Most changes are to provide better compatability with PHP 8.4 and above.
With PHP 8.4 introducing Pdo::connect()
as a way of creating driver specific connections.
We have introducing our ExtendedPdo::connect()
which uses the underlining PDO features then with all our
normal added features.
// does not connect to the database
$pdo = ExtendedPdo::connect(
'mysql:host=localhost;dbname=test',
'username',
'password'
);
// automatically connects
$pdo->exec('SELECT * FROM test');
// explicitly forces a connection
$pdo->establishConnection();
Most changes are to provide better typing and compatability with PHP 8.1 and above.
The main change is the deprecation of ExtendedPdo::connect()
and will be changed in future versions starting with 6.x
Older Code would look like ...
// does not connect to the database
$pdo = new ExtendedPdo(
'mysql:host=localhost;dbname=test',
'username',
'password'
);
// automatically connects
$pdo->exec('SELECT * FROM test');
// explicitly forces a connection
$pdo->connect();
... and now needs to be changed to ExtendedPdo::establishConnection()
// does not connect to the database
$pdo = new ExtendedPdo(
'mysql:host=localhost;dbname=test',
'username',
'password'
);
// automatically connects
$pdo->exec('SELECT * FROM test');
// explicitly forces a connection
$pdo->establishConnection();
The vast majority of changes and breaks from the 2.x version are "under the
hood," so to speak. The main functionality methods (query()
, exec()
,
perform()
, fetch*()
, yield*()
, etc.) are unchanged and work just the same
as in 2.x.
The remaining changes and breaks can be categorized as:
- the rebuilder and parsers;
- profiling and logging;
- instantiation; and
- miscellaneous.
The 2.x Rebuilder has been redone entirely, and now provides separate parsers for the separate database drivers (mysql, pgsql, sqlite, and sqlsrv). This originated from #104, along with #107 and #111, all of which are now resolved. It took a great deal of time and effort to complete, with several approaches attempted.
Given this code ...
$stm = "SELECT * FROM test WHERE foo IN (:foo)";
$sth = $pdo->perform($stm, [
'foo' => ['bar', 'baz', 'dib'];
]);
echo $sth->queryString;
... 2.x would quote and replace the array values directly into the query:
SELECT * FROM test WHERE foo IN ('bar', 'baz', 'dib')
Now, under 3.x, the placeholder is expanded to match the number of array keys, so that there are multiple placeholders:
SELECT * FROM test WHERE foo IN (:foo_0, :foo_1, :foo_2)
The array values passed to the query will also be bound individually. (If you
profile the query, you will see :foo_0
(et al.) in the bindings.)
Given this code ...
$stm = 'SELECT * FROM test WHERE foo = ? AND bar = ?';
$sth = $pdo->perform($stm, [
'foo_value',
'bar_value',
]);
echo $sth->queryString;
... the 2.x rebuilder would leave ?
placeholders alone:
SELECT * FROM test WHERE foo = ? AND bar = ?
Now, under 3.x, sequential placeholders are converted to named placeholders; the name corresponds to the sequential position:
SELECT * FROM test WHERE foo = :__1 AND bar = :__2
The sequential values passed to the query will be bound to the named replacements.
This helps to correct errors associated with binding sequential and named placeholders together, and in identifiying bound values in longer query strings.
With 3.x, you can use the same placeholder multiple times. Given this code ...
$stm = 'SELECT * FROM test WHERE foo = :val OR bar = :val';
$sth = $pdo->perform($stm, [
'val' => 'whatever',
]);
echo $sth->queryString;
... the 3.x parser modifies repeated placeholders by suffixing them each time they reappear, and binds the needed values automatically:
SELECT * FROM test WHERE foo = :val OR bar = :val__1
You can inject your own parsers as well via ExtendedPdo::setParser()
. See
the src/Parser/
directory for examples of the existing parsers.
The 2.x version used a custom profiler system, retaining profiles as array constructs and returning them as such. You needed to inject it yourself.
use Aura\Sql\Profiler;
$pdo->setProfiler(new Profiler());
// ...
// query(), fetch(), beginTransaction()/commit()/rollback() etc.
// ...
// retrieve the profile information as a series of arrays
$profiles = $pdo->getProfiler()->getProfiles();
Under 3.x, the profiler interface itself remains custom, but it is now backed with the PSR-3 logger interface. This means you can use any PSR-3 implementation to capture profiler information. This means that the profiler emits strings, rather than arrays, for the logger to capture.
A profiler is now automatically set on the ExtendedPdo instance, and uses an in-memory logger by default for debugging purposes.
// no need to set a profiler, but you do need to activate it:
$pdo->getProfiler()->setActive();
// ...
// query(), fetch(), beginTransaction()/commit()/rollback() etc.
// ...
// retrieve the profiler logs from the default MemoryLogger
$messages = $pdo->getProfiler()->getLogger()->getMessages();
You can set the log message format and log level through the profiler, to capture just the information you want.
You can set your own profiler and backing logger with the setProfiler()
method.
use Aura\Sql\Profiler\Profiler;
$myLogger = new Psr3LoggerImplementation();
$pdo->setProfiler(new Profiler($myLogger));
Finally, under 2.x, ExtendedPdo would profile every function call. Unless you
examined very carefully, a call to prepare()
followed by perform()
looked
like 2 executions of the same query. To make it easier to examine logs, the 3.x
version does not profile every function call (e.g., prepare()
and prepareWithValues()
are no longer logged).
Under 2.x you would do this:
$pdo = new ExtendedPdo(
'pgsql:host=localhost;dbname=test',
'username',
'password',
array(), // driver options as key-value pairs
array() // attributes as key-value pairs
);
It appears the last argument ended up being extraneous. The 3.x ExtendedPdo changes the last argument to this:
$pdo = new ExtendedPdo(
'pgsql:host=localhost;dbname=test',
'username',
'password',
[], // driver options as key-value pairs
[] // queries to execute after connection
);
This lets you execute queries at connection time; e.g., to make connection configuration changes that cannot be made as driver options/attributes. For example:
$pdo = new ExtendedPdo(
'pgsql:host=localhost;dbname=test',
'username',
'password',
[
PDO::ATTR_CASE => PDO::CASE_NATURAL,
],
[
"SET NAMES 'utf8'",
]
)
-
PHP 5.6 is now the minimum required PHP version, though using the latest PHP version is recommended.
-
3.x (as with previous versions) starts PDO in
ERRMODE_EXCEPTION
when no error mode is explicitly specified.. However, thesqlsrv
driver balks at this, so it is started inERRMODE_WARNING
instead. -
The
ExtendedPdo::yield*()
methods now use theyield
keyword instead of returningIterator
instances. -
1.x had a
quoteName()
method to quote identifier names. This was removed in 2.x, when that (and related functionality) were split off to SqlQuery. The functionality is added back in 3.x asquoteName()
andquoteSingleName()
as a convenience, though it is less robust than in SqlQuery. -
Some drivers cause PDO to make additional methods available (e.g.,
sqlite
andpgsql
). The 3.xExtendedPdo
now proxies all unknown method calls to the underlying PDO instance to make those methods available, if they exist. -
When dumping an ExtendedPdo object, the username and password are omitted. This should help keep unexpected output of stack traces from revealing credentials.