This is something that's been bugging me for awhile, and I never really bothered to dig in and see what I could do to work around it, until now.
unserializing an unserializable string
The easiest, and most popular way to check if a string is serialized is simple.
PHP:
-
/**
-
* Is a string serialized?
-
*
-
* @param string
-
* @return boolean
-
*/
-
function isSerialized($str)
-
{
-
if ($str === 0)
-
{
-
return false;
-
}
-
else
-
{
-
-
}
-
}
Quite a few very popular frameworks and apps do it this way, try to just simply pass the string to the unserialize method, make sure it isn't the boolean false, and prepend the method with an @ to suppress notice-level error output. Don't get me wrong, it works. But if you're logging your notice-level errors, then any time you're trying to check if a string is serialized in this way, it logs the notice that's generated. Storing data as a serialized hash in a key-value database (Tokyo Cabinet — which we've chosen to use for Nomcat, BerkeleyDB, CouchDB, Redis, etc.) magnifies the scenario and takes this wee-little_old Notice, and makes your logs fill up really fast. Since error logs are for tracking issues that a developer should really concern themselves with, logging all of these notices just wouldn't do, it's simply a waste of space and processing time to handle them over and over and over again.
As we've been building Nomcat over at NOM, we're often faced with dozens of reads from the data storage layer with each view that's generated, and recently while testing, we've run into log files that look something like this.
CODE:
-
ERROR - 2009-06-04 01:49:15 --> Severity: Notice --> unserialize(): Error at offset 0 of 1 bytes /path/to/application/libraries/App_Model.php 432
-
... a couple hundred lines ...
-
ERROR - 2009-06-04 01:51:19 --> Severity: Notice --> unserialize(): Error at offset 0 of 1 bytes /path/to/application/libraries/App_Model.php 432
just set_error_handler to an empty method
Initially I tried a few different options to work around this annoying little problem.
- Prepending the
unserialize method with @ to suppress output, but it doesn't prevent logging.
- Wrapping in a try-catch block doesn't do the trick
- Wrapping it with
error_reporting(0) and error_reporting(2) doesn't do the trick either
- But creating an method to handle the error, have it to nothing, and wrapping the
unserialize line with set_error_handler and restore_error_handler does the trick!
This is what I came up with.
PHP:
-
/**
-
* Is a string serialized?
-
*
-
* @param string
-
* @return boolean
-
*/
-
function isSerialized($str)
-
{
-
if ($str === 0)
-
{
-
return false;
-
}
-
else
-
{
-
-
-
-
return $return;
-
}
-
}
-
-
/**
-
* dummy error handler set up to obsorb any errors thrown by isSerialized
-
* @return
-
* @param object $errno
-
* @param object $errstr
-
*/
-
function unserialize_handler($errno, $errstr)
-
{
-
// don't do anything
-
}
If you noticed that I'm passing an array like this: set_error_handler(array('App_Model','unserialize_handler'));, you've got a good eye. This is how you can specify in which class to find the method you want to use as a callback, neither $this->methodname or Classname::methodname will work. However if you aren't writing object oriented code, you can simply pass the name of the method you want to use to handle the error as a string like this: set_error_handler('unserialize_handler');.
Let me know how it works for you.