When I'm executing the following statement in Magento with a really large $sku
, the execution terminates without any errors thrown what so ever. There are no errors in either Magento's, Apache's or PHP's error logs.
Mage::getModel('catalog/product')->loadByAttribute('sku', $sku);
Question: How do I catch the error?
I've tried to set custom error handlers, and for testing purposes I've also managed to trigger error situations where each of the error handler functions are invoked. But when running the previously mentioned Magento code with a large $sku
, none of the error handling functions are executed.
error_reporting( -1 );
set_error_handler( array( 'Error', 'captureNormal' ) );
set_exception_handler( array( 'Error', 'captureException' ) );
register_shutdown_function( array( 'Error', 'captureShutdown' ) );
For completeness, this is the $sku
I'm passing to loadByAttribute()
. (The sku is invalid, but that is not the issue)
1- 9685 0102046|1- 9685 1212100|1- 9685 1212092|1- 9685 1212096|1- 9685 1102100|1- 9685 1102108|1- 9685 1102112|1- 9685 1102092|1- 9685 0102048|1- 9685 0102054|1- 9685 0102056|1- 9685 0102058|1- 9685 1212104|1- 9685 1212108|1- 9685 0212058|1- 9685 0104050|1- 9685 0212050|1- 9685 0212056|1- 9685 0212044|1- 9685 0212048|1- 9685 0212052|1- 9685 0212054|1- 9685 1102104|1- 9685 1102124
Any insight into this matter is much appreciated!
Update: Upon further investigation, this is the exact point in the code where execution terminates. when the foreach is executed I guess Magento goes into MySQL world and starts loading up data from the database.
\Mage\Catalog\Model\Abstract.php
public function loadByAttribute($attribute, $value, $additionalAttributes = '*')
{
$collection = $this->getResourceCollection()
->addAttributeToSelect($additionalAttributes)
->addAttributeToFilter($attribute, $value)
->setPage(1,1);
foreach ($collection as $object) { // <--------------- HERE
return $object;
}
return false;
}
Note, I'm ONLY interested in finding out how to properly CATCH these kinds of errors, not "fix" the logic. This is so that I can present a proper error message to the user. The example above with the malformed sku is contrived and I have no desire to make my Magento app work with those erroneous skus.
UPDATE: CAUSE OF ERROR
I traced the load()
call that is triggered by the foreach
, and it boils down to a bug Zend / PHP.
I traced the call to \www\lib\Zend\Db\Statement.php _stripQuoted($sql)
, and the following statement: $sql = preg_replace("/$q($qe|\\\\{2}|[^$q])*$q/", '', $sql);
(on line 204 in Magento 1.7.0.2).
When preg_replace
is run with the SQL generated by the load()
statement, the program just terminates. There is no trace in either apache's or php's log. The log works, because they get filled with other stuff.
I found this bug report on the issue. There they indicate it is a segmentation fault. I guess there is no way to recover from that (?).
Since the question was explicitly about how to catch the error condition and a lot of the advice here was more about sanitizing the data I don't feel there is a clear cut "winner" for the bounty. But Franklin P Strube at least hinted at doing check deeper down the stack, so he gets it.
Thank you for all you help guys. If you have any advice on how to handle segfaults please do share =).
You don't need to register your own error handlers, Magento has already done that for you. All you need to do is few steps:
Turn on error reporting by uncommenting next line in index.php:
#ini_set('display_errors', 1);
Make sure logging is turned on: go to admin panel System->Configuration->Developer->Log Settings, set Enabled to Yes. Now you can check the Magento logs in var/log
from Magento root.
Any php error should fall to php Fatal Error or into the logs.
I believe the script may be terminating deeper in the stack than you indicate. When a collection begins the "foreach" loop, it must first call the "load" function, which is specifically where it builds the SQL.
You can try logging Mage::log((string)$collection->getSelect());
to see if the SQL query has issues.
Or, are you able to sanitize the sku before filtering? Perhaps using an observer?
Don't forget to set the log path in your php.ini, .htaccess or using ini_set.
.htaccess example:
php_flag log_errors on
php_value error_log /var/www/PHP_errors.log
.
If Magento is using PDO, PDO should normally be set into exception mode.
This is what I'm using for PHP 5.2.x:
error_reporting(-1);
ini_set("display_errors", "On");
function errhandler($nSeverity, $strMessage, $strFilePath, $nLineNumber){
error_log(PHP_EOL.date("Y-m-d H:m:s", time())." ".$strFilePath."#".$nLineNumber.": ".$strMessage.PHP_EOL);
throw new ErrorException($strMessage, /*nExceptionCode*/ 0, $nSeverity, $strFilePath, $nLineNumber);
}
set_error_handler("errhandler", -1);
this problem occurs when PHP run out of memory,
increase allowed memory size from php.ini or increase memory limit on fly,
put this ini_set('memory_limit', '512M');
In index.php, move the following line to the top of the index.php file:
ini_set('display_errors', 1);
I have a feeling your issue is not flagging this trigger because of a permissions issue. I run these two commands to ensure permissions are set correctly on files/dirs.
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
First two questions :
exit
in some part of the php code, for example, will not raise an error and yet the script will be terminated."I guess Magento goes into MySQL world and starts loading up data from the database"
. Have you checked what happens on your MySQL server ? Does the SQL query really get sent ? If it is sent, is it treated, errored, or stalled ?I would suggest using a debugger to step through the PHP code, starting from the foreach ( $collection
line. I don't know what editor/IDE you are using, you can setup Netbeans and XDebug to allow setting breakpoints and stepping through the code.
I don't think it is the foreach
that kills the script.
Try disassembling the call immediately before:
public function loadByAttribute($attribute, $value, $additionalAttributes = '*')
{
$res1 = $this->getResourceCollection();
$res2 = $res1->addAttributeToSelect($additionalAttributes);
$res3 = $res2->addAttributeToFilter($attribute, $value);
$collection = $res3->setPage(1,1);
foreach ($collection as $object)
return $object;
}
return false;
}
Otherwise, you might want to simply throw an exception if the SKU length is excessive, or if the SKU fails some sanitization check.
I have the same error for a customer in Magento 1.7.
I have a sql insert statement wich saves the sidebar cart html in a field, and in that statement I used addslashes for that html.
When there are 1 to 5 products in that html, the save statement executes succesfully.
When there are more than 5 products, so that means the html is longer but the part for each product is the same, I run also in this error.
The program just stops at $sql = preg_replace("/$q($qe|\\{2}|[^$q])*$q/", '', $sql); and no errors are visible.
It seems to be a memory problem, with 6 products I cross a limit in size (now 7000 characters), so the provider has to change php settings I guess ..