PHP RFC: Debugging PDO Prepared Statement Emulation
- Version: 0.3
- Date: 2016-10-17
- Author: Adam Baratz email@example.com
- Status: Implemented (in PHP 7.2)
- First Published at: https://wiki.php.net/rfc/debugging_pdo_prepared_statement_emulation
PDO is built on the concept of prepared statements. It expects individual database drivers to manage statement execution, but also allows prepared statements to be emulated. Emulation means that the pdo extension, using no third-party code, generates a query string for literal execution by the driver. It identifies parameters within the statement string and interpolates escaped values. You must use separate logging or tracing tools to debug this process.
If you're a PHP developer, this can add time to the development process. Depending on the environment, you may not have access to these tools. This can make it difficult to use the pdo_dblib driver, since the DB-Library API doesn't support prepared statements. The pdo_mysql and pdo_pgsql drivers offer the option to emulate. In the case of pdo_mysql, it's enabled by default. I've seen many developers write ad hoc parsers in userland as a workaround.
If you're a PHP internals developer, this prevents you from writing good .phpt tests against pdo_sql_parser.re. Without being able to inspect the raw query string, you can only test the effects of the queries it generates. For example, you might want to verify an int is quoted as 1 and not '1'. A database might cast the string to an int, doing the right thing with it, but obscuring that to the developer.
People who use emulated prepared statements should be able to debug them within userland, without using additional tools. PDO already provides some debug functionality in the form of
PDOStatement::debugDumpParams(). The goal would be to offer another slice on PDO internals, not to create another path for developers to communicate with a database. I propose a new method,
$db = new PDO(...); // works with statements without bound values $stmt = $db->query('SELECT 1'); var_dump($stmt->activeQueryString()); // => string(8) "SELECT 1" $stmt = $db->prepare('SELECT :string'); $stmt->bindValue(':string', 'foo'); // returns unparsed query before execution var_dump($stmt->activeQueryString()); // => string(14) "SELECT :string" // returns parsed query after execution $stmt->execute(); var_dump($stmt->activeQueryString()); // => string(11) "SELECT 'foo'"
Since this would be a debug tool, the method shouldn't affect the state of the
PDOStatement instance. For example, this method shouldn't trigger parsing or errors. You usually don't know something went wrong with the parsing until after execution, anyway. This means the method simply retrieves a value that already exists in memory.
Backward Incompatible Changes
Proposed PHP Version(s)
Next PHP 7.x.
It's been suggested that PDO shouldn't allow prepare statement emulation. Since the mssql extension was deprecated in PHP 7 in favor of pdo_dblib, I don't think this is possible.
It's been suggested that this change would turn PDO into a leaky (or leakier) abstraction. I'd counter that this is strictly a tool that allows tests to expand their code coverage. If emulated prepared statements are being kept in PDO, we should be able to test the >700 LoC in the associated query parser.
Proposed Voting Choices
This project requires a 50%+1 majority.
Patches and Tests
A working implementation with tests: https://github.com/php/php-src/pull/2159
Initial discussion of this proposal on the internals mailing list: http://marc.info/?l=php-internals&m=147638162506291&w=2
This feature was implemented in PHP 7.2 (83086d9a72675bad2b2560c6f427d0c1f1d1eba0).