ZF2-563: Zend\Log\Write\Db::mapEventIntoColumn creates non-existing database table column due to PHP type juggling
Description
Zend\Log\Writer\Db::mapEventIntoColumn creates new non-existing database table column due to PHP type juggling:
protected function mapEventIntoColumn(array $event, array $columnMap = null)
{
if (empty($event)) {
return array();
}
$data = array();
foreach ($event as $name => $value) {
if (is_array($value)) {
foreach ($value as $key => $subvalue) {
if (isset($columnMap[$name][$key])) { // bad check!
$data[$columnMap[$name][$key]] = $subvalue;
}
}
} elseif (isset($columnMap[$name])) {
$data[$columnMap[$name]] = $value;
}
}
return $data;
}
The isset($columnMap[$name][$key]) is not good, because a non-existing string value for $key enables PHP type juggling and thus making it 0. This occurs when $value is an array and $columnMap['extra'] contains a string.
A small example to make it more clear what is happening here:
<?php
$columnMap = array( 'extra' => 'extra' );
var_dump( isset( $columnMap['extra']['something'] ) ); // Output: boolean true
var_dump( $columnMap['extra']['something'] ); // Output: string 'e' (length=1)
Also it is impossible to store an array in a database table column without converting it to a string, so that must be done before the data can be used in a database query.
Comments
Posted by Martin_P (martin_p) on 2012-09-15T19:11:18.000+0000
Typo
Posted by BenoƮt Durand (intiilapa) on 2012-09-15T20:05:25.000+0000
Can you add your use case of extra as a string? The Logger can only send an array as extra.
Posted by Martin_P (martin_p) on 2012-09-15T20:23:10.000+0000
$extra is an array, that is right, but I am referring to $colomnMap['extra']. That is a string.
When $extra is an array, let's say: ``` The script now checks if $columnMap['extra']['line'] exists with isset() and this always returns true. Because of type juggling the string 'line' is converted to 0 (see my example). This way you get the first letter of 'extra', the 'e' and thus a non-existing database table column.
Posted by Martin_P (martin_p) on 2012-09-15T21:04:45.000+0000
When Zend\Logger\Logger is used as an exception handler, in Zend\Logger\Logger::registerExceptionHandler() the following happens:
So $extra is now an array with data from the exception. When I throw an exception the type juggling happens in the above mentioned methods.
Use case:
EDIT: Changed database table name, namespace and added create table definition for easier reproducing:
EDIT 2: Sorry, offcourse the error might be useful too: {quote}Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'e' in 'field list'' in some\folder\vendor\Zend\library\Zend\Db\Adapter\Driver\Pdo\Statement.php on line 220{quote}
Posted by Martin_P (martin_p) on 2012-09-15T22:44:01.000+0000
Looking at Zend\Log\Writer\Db::eventIntoColumn it seems the intended usage is this:
This still does not make any sense, because this creates random database table columns depending on the array keys of the extra array (see issue [#ZF2-562).
When you add the needed columns for logging the exception:
you also still get a notice and an unintended array to string conversion and thus data loss: {quote}Notice: Array to string conversion in some\folder\vendor\Zend\library\Zend\Db\Adapter\Driver\Pdo\Statement.php on line 258{quote}
Posted by Ralph Schindler (ralph) on 2012-10-08T20:12:25.000+0000
This issue has been closed on Jira and moved to GitHub for issue tracking. To continue following the resolution of this issues, please visit: https://github.com/zendframework/zf2/issues/2589