Check to see if a string is serialized? - php

What's the best way to determine whether or not a string is the result of the serialize() function?
https://www.php.net/manual/en/function.serialize

I'd say, try to unserialize it ;-)
Quoting the manual :
In case the passed string is not
unserializeable, FALSE is returned and
E_NOTICE is issued.
So, you have to check if the return value is false or not (with === or !==, to be sure not to have any problem with 0 or null or anything that equals to false, I'd say).
Just beware the notice : you might want/need to use the # operator.
For instance :
$str = 'hjkl';
$data = #unserialize($str);
if ($data !== false) {
echo "ok";
} else {
echo "not ok";
}
Will get you :
not ok
EDIT : Oh, and like #Peter said (thanks to him!), you might run into trouble if you are trying to unserialize the representation of a boolean false :-(
So, checking that your serialized string is not equal to "b:0;" might be helpful too ; something like this should do the trick, I suppose :
$data = #unserialize($str);
if ($str === 'b:0;' || $data !== false) {
echo "ok";
} else {
echo "not ok";
}
testing that special case before trying to unserialize would be an optimization -- but probably not that usefull, if you don't often have a false serialized value.

From WordPress core functions:
<?php
function is_serialized( $data, $strict = true ) {
// If it isn't a string, it isn't serialized.
if ( ! is_string( $data ) ) {
return false;
}
$data = trim( $data );
if ( 'N;' === $data ) {
return true;
}
if ( strlen( $data ) < 4 ) {
return false;
}
if ( ':' !== $data[1] ) {
return false;
}
if ( $strict ) {
$lastc = substr( $data, -1 );
if ( ';' !== $lastc && '}' !== $lastc ) {
return false;
}
} else {
$semicolon = strpos( $data, ';' );
$brace = strpos( $data, '}' );
// Either ; or } must exist.
if ( false === $semicolon && false === $brace ) {
return false;
}
// But neither must be in the first X characters.
if ( false !== $semicolon && $semicolon < 3 ) {
return false;
}
if ( false !== $brace && $brace < 4 ) {
return false;
}
}
$token = $data[0];
switch ( $token ) {
case 's':
if ( $strict ) {
if ( '"' !== substr( $data, -2, 1 ) ) {
return false;
}
} elseif ( false === strpos( $data, '"' ) ) {
return false;
}
// Or else fall through.
case 'a':
case 'O':
return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
case 'b':
case 'i':
case 'd':
$end = $strict ? '$' : '';
return (bool) preg_match( "/^{$token}:[0-9.E+-]+;$end/", $data );
}
return false;
}

Optimizing Pascal MARTIN's response
/**
* Check if a string is serialized
* #param string $string
*/
public static function is_serial($string) {
return (#unserialize($string) !== false);
}

If the $string is a serialized false value, ie $string = 'b:0;'
SoN9ne's function returns false, it's wrong
so the function would be
/**
* Check if a string is serialized
*
* #param string $string
*
* #return bool
*/
function is_serialized_string($string)
{
return ($string == 'b:0;' || #unserialize($string) !== false);
}

Despite Pascal MARTIN's excellent answer, I was curious if you could approach this another way, so I did this just as a mental exercise
<?php
ini_set( 'display_errors', 1 );
ini_set( 'track_errors', 1 );
error_reporting( E_ALL );
$valueToUnserialize = serialize( false );
//$valueToUnserialize = "a"; # uncomment this for another test
$unserialized = #unserialize( $valueToUnserialize );
if ( FALSE === $unserialized && isset( $php_errormsg ) && strpos( $php_errormsg, 'unserialize' ) !== FALSE )
{
echo 'Value could not be unserialized<br>';
echo $valueToUnserialize;
} else {
echo 'Value was unserialized!<br>';
var_dump( $unserialized );
}
And it actually works. The only caveat is that it will likely break if you have a registered error handler because of how $php_errormsg works.

$data = #unserialize($str);
if($data !== false || $str === 'b:0;')
echo 'ok';
else
echo "not ok";
Correctly handles the case of serialize(false). :)

build in to a function
function isSerialized($value)
{
return preg_match('^([adObis]:|N;)^', $value);
}

There is WordPress solution: (detail is here)
function is_serialized($data, $strict = true)
{
// if it isn't a string, it isn't serialized.
if (!is_string($data)) {
return false;
}
$data = trim($data);
if ('N;' == $data) {
return true;
}
if (strlen($data) < 4) {
return false;
}
if (':' !== $data[1]) {
return false;
}
if ($strict) {
$lastc = substr($data, -1);
if (';' !== $lastc && '}' !== $lastc) {
return false;
}
} else {
$semicolon = strpos($data, ';');
$brace = strpos($data, '}');
// Either ; or } must exist.
if (false === $semicolon && false === $brace)
return false;
// But neither must be in the first X characters.
if (false !== $semicolon && $semicolon < 3)
return false;
if (false !== $brace && $brace < 4)
return false;
}
$token = $data[0];
switch ($token) {
case 's' :
if ($strict) {
if ('"' !== substr($data, -2, 1)) {
return false;
}
} elseif (false === strpos($data, '"')) {
return false;
}
// or else fall through
case 'a' :
case 'O' :
return (bool)preg_match("/^{$token}:[0-9]+:/s", $data);
case 'b' :
case 'i' :
case 'd' :
$end = $strict ? '$' : '';
return (bool)preg_match("/^{$token}:[0-9.E-]+;$end/", $data);
}
return false;
}

/**
* some people will look down on this little puppy
*/
function isSerialized($s){
if(
stristr($s, '{' ) != false &&
stristr($s, '}' ) != false &&
stristr($s, ';' ) != false &&
stristr($s, ':' ) != false
){
return true;
}else{
return false;
}
}

This works fine for me
<?php
function is_serialized($data){
return (is_string($data) && preg_match("#^((N;)|((a|O|s):[0-9]+:.*[;}])|((b|i|d):[0-9.E-]+;))$#um", $data));
}
?>

I would just try to unserialize it. This is how i would solve it
public static function is_serialized($string)
{
try {
unserialize($string);
} catch (\Exception $e) {
return false;
}
return true;
}
Or more like a helper function
function is_serialized($string) {
try {
unserialize($string);
} catch (\Exception $e) {
return false;
}
return true;
}

The mentionned WordPress function does not really detect arrays (a:1:{42} is considered to be serialized) and falsely returns true on escaped strings like a:1:{s:3:\"foo\";s:3:\"bar\";} (although unserialize does not work)
If you use the #unserialize way on the other side WordPress for example adds an ugly margin at the top of the backend when using define('WP_DEBUG', true);
A working solution that solves both problems and circumvents the stfu-operator is:
function __is_serialized($var)
{
if (!is_string($var) || $var == '') {
return false;
}
set_error_handler(function ($errno, $errstr) {});
$unserialized = unserialize($var);
restore_error_handler();
if ($var !== 'b:0;' && $unserialized === false) {
return false;
}
return true;
}

see the wordpress function is_serialized
function is_serialized( $data, $strict = true ) {
// If it isn't a string, it isn't serialized.
if ( ! is_string( $data ) ) {
return false;
}
$data = trim( $data );
if ( 'N;' === $data ) {
return true;
}
if ( strlen( $data ) < 4 ) {
return false;
}
if ( ':' !== $data[1] ) {
return false;
}
if ( $strict ) {
$lastc = substr( $data, -1 );
if ( ';' !== $lastc && '}' !== $lastc ) {
return false;
}
} else {
$semicolon = strpos( $data, ';' );
$brace = strpos( $data, '}' );
// Either ; or } must exist.
if ( false === $semicolon && false === $brace ) {
return false;
}
// But neither must be in the first X characters.
if ( false !== $semicolon && $semicolon < 3 ) {
return false;
}
if ( false !== $brace && $brace < 4 ) {
return false;
}
}
$token = $data[0];
switch ( $token ) {
case 's':
if ( $strict ) {
if ( '"' !== substr( $data, -2, 1 ) ) {
return false;
}
} elseif ( false === strpos( $data, '"' ) ) {
return false;
}
// Or else fall through.
case 'a':
case 'O':
return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
case 'b':
case 'i':
case 'd':
$end = $strict ? '$' : '';
return (bool) preg_match( "/^{$token}:[0-9.E+-]+;$end/", $data );
}
return false;
}

Related

How to extract the body part of a function?

The function extractBody() extracts the body part of a function:
$data = '
<?php
function my_function($param){
if($param === true){
// This is true
}else if($param === false){
// This is false
}else{
// This is not
}
}
?>
';
function extractBody($functionName, $data) {
$c = preg_match_all("/function\s+".$functionName."\s*\((?<param>[^\)]*)\)\s*(?<body>\{(?:[^{}]+|(?&body))*\})/", $data, $matches);
return $c > 0 ? $matches['body'] : null;
}
$body =extractBody("my_function", $data);
var_dump($body);
result: The variable $body contains
if($param === true){
// This is true
}else if($param === false){
// This is false
}else{
// This is not
}
Now I need a second function to work with lambda functions (function is assigned to a variable)
$data2 = '
<?php
$my_function = function($param){
if($param === true){
// This is true
}else if($param === false){
// This is false
}else{
// This is not
}
}
?>
';
function extractBody2($functionName, $data) {
$c = preg_match_all("/".$functionName."\s+=\s+function\s+\s*\((?<param>[^\)]*)\)\s*(?<body>\{(?:[^{}]+|(?&body))*\})/", $data, $matches);
return $c > 0 ? $matches['body'] : null;
}
$body2 =extractBody2("my_function", $data2);
var_dump($body2);
Unfortunately, I'm not a regex specialist and I get NULL back.
I think the error must be somewhere here: "/".$functionName."\s+=\s+
regex101 didn't reveal any issues though.
This works for me:
function extractBody2($functionName, $data) {
$c = preg_match_all("/\\$".$functionName."\s+=\s+function\s*\((?<param>[^\)]*)\)\s*(?<body>\{(?:[^{}]+|(?&body))*\})/", $data, $matches);
return $c > 0 ? $matches['body'] : null;
}

Error on unserialize()

I'm trying to fix a PHP error on unserialize(). I know we can suppress it with # but can it be possible to fix that error without suppressing.
Here is the table & my code:
TABLE: 'config'
id c_key c_value
1 facebook a:1:{i:0;s:8:"Newsfeed";}
2 mg_notification_msv to.aaaa.org
public function db_get_config($key, $default = null)
{
if (!empty($key)) {
// $record = $this->db_get_record($this->tables['config'], array('c_key' => $key));
$record = $this->db->from($this->tables['config'])->where(array('c_key' => $key))->limit(1)->get()->row_array();
if (!empty($record)) {
$value = unserialize($record['c_value']); // Message: unserialize(): Error at offset 0 of 15 bytes
if ($value === false) {
$value = $record['c_value'];
}
return $value;
}
}
return $default;
}
When passing $key as facebook or mg_notification_msv, the function returns array(1) { [0]=> string(8) "Newsfeed" } or string(15) "to.aaaa.org" on var_dump().
This returns the error:
A PHP Error was encountered
Severity: Notice Message: unserialize(): Error at offset 0 of 15 bytes
Filename: models/common_model.php
Line Number: xxxx
Is there any way to fix this PHP error?
Use of strlen() on $record['c_value'] doesn't help me.
It's probably throwing that error because when you try to unserialize the value of mg_notification_msv it's trying to unserialize a string that wasn't serialized before (to.aaaa.org is not a serialized string)
You can try to unserialize only if the string is unserializable:
if (#unserialize($record['c_value']) !== false ) {
$value = unserialize($record['c_value']);
} else {
$value = $record['c_value'];
}
I tried with WP is_serialized() & possible resolved my issue.
<?php
public function db_get_config($key, $default = null)
{
if (!empty($key)) {
// $record = $this->db_get_record($this->tables['config'], array('c_key' => $key));
$record = $this->db->from($this->tables['config'])->where(array('c_key' => $key))->limit(1)->get()->row_array();
if (!empty($record)) {
$e = $this->is_serialized($record['c_value']); // WP support function
if ($e === true) {
$value = unserialize($record['c_value']);
if ($value === false) {
$value = $record['c_value'];
}
} else {
$value = $record['c_value'];
}
return $value;
}
}
return $default;
}
private function is_serialized( $data, $strict = true )
{
// if it isn't a string, it isn't serialized.
if ( ! is_string( $data ) ) {
return false;
}
$data = trim( $data );
if ( 'N;' == $data ) {
return true;
}
if ( strlen( $data ) < 4 ) {
return false;
}
if ( ':' !== $data[1] ) {
return false;
}
if ( $strict ) {
$lastc = substr( $data, -1 );
if ( ';' !== $lastc && '}' !== $lastc ) {
return false;
}
} else {
$semicolon = strpos( $data, ';' );
$brace = strpos( $data, '}' );
// Either ; or } must exist.
if ( false === $semicolon && false === $brace )
return false;
// But neither must be in the first X characters.
if ( false !== $semicolon && $semicolon < 3 )
return false;
if ( false !== $brace && $brace < 4 )
return false;
}
$token = $data[0];
switch ( $token ) {
case 's' :
if ( $strict ) {
if ( '"' !== substr( $data, -2, 1 ) ) {
return false;
}
} elseif ( false === strpos( $data, '"' ) ) {
return false;
}
// or else fall through
case 'a' :
case 'O' :
return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
case 'b' :
case 'i' :
case 'd' :
$end = $strict ? '$' : '';
return (bool) preg_match( "/^{$token}:[0-9.E-]+;$end/", $data );
}
return false;
}
?>

Alternative to array_column function used as search

Search all navigation items:
$navitems_locations_array = array_unique(array_column($projects, 'location'));
asort($navitems_locations_array);
This works on my localhost but not on live. My server doesn't support it. And I'm using lower version of PHP.
And this is the error:
Fatal error: Call to undefined function array_column()
Any alternative with the same output?
Here is my sample arrray list:
Array( [ name ] => P1 [ description ] => Lorem Ipsum [ location ] => air city[ type ] => high rise[ status ] => new [ tags ] => [ page_url ] => project-1[ image ] => projects/images/p1/project-image.jpg[ timeline ] => )
Try this hope it will help you out. Here we are using array_map for gathering locations.
$locations=array();
array_map(function($value) use (&$locations) {
$locations[]=$value["location"];//we are gathering index location
}, $yourArray);//add your array here in place of $yourArray.
print_r(array_unique($locations));
It is usually useful to read User Contributed Notes on the PHP Manual.
For example below the array_column article, there are several variants of implementations for versions lower than PHP 5.5. Here is the most popular one:
if(!function_exists("array_column"))
{
function array_column($array,$column_name)
{
return array_map(function($element) use($column_name){return $element[$column_name];}, $array);
}
}
But it doesn't support $index_key, so you can find another one there, which does:
if (!function_exists('array_column')) {
function array_column($input, $column_key, $index_key = null) {
$arr = array_map(function($d) use ($column_key, $index_key) {
if (!isset($d[$column_key])) {
return null;
}
if ($index_key !== null) {
return array($d[$index_key] => $d[$column_key]);
}
return $d[$column_key];
}, $input);
if ($index_key !== null) {
$tmp = array();
foreach ($arr as $ar) {
$tmp[key($ar)] = current($ar);
}
$arr = $tmp;
}
return $arr;
}
}
You can find a couple more alternatives in the User Contributed Notes.
Add your own function array_column if you PHP version does not support it:
if (!function_exists('array_column')) {
function array_column($input = null, $columnKey = null, $indexKey = null)
{
$argc = func_num_args();
$params = func_get_args();
if ($argc < 2) {
trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
return null;
}
if (!is_array($params[0])) {
trigger_error(
'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
E_USER_WARNING
);
return null;
}
if (!is_int($params[1])
&& !is_float($params[1])
&& !is_string($params[1])
&& $params[1] !== null
&& !(is_object($params[1]) && method_exists($params[1], '__toString'))
) {
trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
return false;
}
if (isset($params[2])
&& !is_int($params[2])
&& !is_float($params[2])
&& !is_string($params[2])
&& !(is_object($params[2]) && method_exists($params[2], '__toString'))
) {
trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
return false;
}
$paramsInput = $params[0];
$paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
$paramsIndexKey = null;
if (isset($params[2])) {
if (is_float($params[2]) || is_int($params[2])) {
$paramsIndexKey = (int) $params[2];
} else {
$paramsIndexKey = (string) $params[2];
}
}
$resultArray = array();
foreach ($paramsInput as $row) {
$key = $value = null;
$keySet = $valueSet = false;
if ($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
$keySet = true;
$key = (string) $row[$paramsIndexKey];
}
if ($paramsColumnKey === null) {
$valueSet = true;
$value = $row;
} elseif (is_array($row) && array_key_exists($paramsColumnKey, $row)) {
$valueSet = true;
$value = $row[$paramsColumnKey];
}
if ($valueSet) {
if ($keySet) {
$resultArray[$key] = $value;
} else {
$resultArray[] = $value;
}
}
}
return $resultArray;
}
}
Reference:

Check if array key exists [duplicate]

I have a multidimensional array. I need a function that checks if a specified key exists.
Let's take this array
$config['lib']['template']['engine'] = 'setted';
A function should return true when I call it with:
checkKey('lib','template','engine');
//> Checks if isset $config['lib']['template']['engine']
Note that my array isn't only 3 dimensional. It should be able to check even with only 1 dimension:
checkKey('genericSetting');
//> Returns false becase $c['genericSetting'] isn't setted
At the moment I am using an awful eval code, I would like to hear suggest :)
function checkKey($array) {
$args = func_get_args();
for ($i = 1; $i < count($args); $i++) {
if (!isset($array[$args[$i]]))
return false;
$array = &$array[$args[$i]];
}
return true;
}
Usage:
checkKey($config, 'lib', 'template', 'engine');
checkKey($config, 'genericSetting');
I created the following two functions to do solve the same problem you are having.
The first function check is able to check for one/many keys at once in an array using a dot notation. The get_value function allows you to get the value from an array or return another default value if the given key doesn't exist. There are samples at the bottom for basic usage. The code is mostly based on CakePHP's Set::check() function.
<?php
function check($array, $paths = null) {
if (!is_array($paths)) {
$paths = func_get_args();
array_shift($paths);
}
foreach ($paths as $path) {
$data = $array;
if (!is_array($path)) {
$path = explode('.', $path);
}
foreach ($path as $i => $key) {
if (is_numeric($key) && intval($key) > 0 || $key === '0') {
$key = intval($key);
}
if ($i === count($path) - 1 && !(is_array($data) && array_key_exists($key, $data))) {
return false;
}
if (!is_array($data) || !array_key_exists($key, $data)) {
return false;
}
$data =& $data[$key];
}
}
return true;
}
function get_value($array, $path, $defaultValue = FALSE) {
if (!is_array($path))
$path = explode('.', $path);
foreach ($path as $i => $key) {
if (is_numeric($key) && intval($key) > 0 || $key === '0')
$key = intval($key);
if ($i === count($path) - 1) {
if (is_array($array) && array_key_exists($key, $array))
return $array[$key];
else
break;
}
if (!is_array($array) || !array_key_exists($key, $array))
break;
$array = & $array[$key];
}
return $defaultValue;
}
// Sample usage
$data = array('aaa' => array(
'bbb' => 'bbb',
'ccc' => array(
'ddd' => 'ddd'
)
));
var_dump( check($data, 'aaa.bbb') ); // true
var_dump( check($data, 'aaa.bbb', 'aaa.ccc') ); // true
var_dump( check($data, 'zzz') ); // false
var_dump( check($data, 'aaa.bbb', 'zzz') ); // false
var_dump( get_value($data, 'aaa.bbb', 'default value') ); // "bbb"
var_dump( get_value($data, 'zzz', 'default value') ); // "default value"

Check if a "run-time" multidimensional array key exists

I have a multidimensional array. I need a function that checks if a specified key exists.
Let's take this array
$config['lib']['template']['engine'] = 'setted';
A function should return true when I call it with:
checkKey('lib','template','engine');
//> Checks if isset $config['lib']['template']['engine']
Note that my array isn't only 3 dimensional. It should be able to check even with only 1 dimension:
checkKey('genericSetting');
//> Returns false becase $c['genericSetting'] isn't setted
At the moment I am using an awful eval code, I would like to hear suggest :)
function checkKey($array) {
$args = func_get_args();
for ($i = 1; $i < count($args); $i++) {
if (!isset($array[$args[$i]]))
return false;
$array = &$array[$args[$i]];
}
return true;
}
Usage:
checkKey($config, 'lib', 'template', 'engine');
checkKey($config, 'genericSetting');
I created the following two functions to do solve the same problem you are having.
The first function check is able to check for one/many keys at once in an array using a dot notation. The get_value function allows you to get the value from an array or return another default value if the given key doesn't exist. There are samples at the bottom for basic usage. The code is mostly based on CakePHP's Set::check() function.
<?php
function check($array, $paths = null) {
if (!is_array($paths)) {
$paths = func_get_args();
array_shift($paths);
}
foreach ($paths as $path) {
$data = $array;
if (!is_array($path)) {
$path = explode('.', $path);
}
foreach ($path as $i => $key) {
if (is_numeric($key) && intval($key) > 0 || $key === '0') {
$key = intval($key);
}
if ($i === count($path) - 1 && !(is_array($data) && array_key_exists($key, $data))) {
return false;
}
if (!is_array($data) || !array_key_exists($key, $data)) {
return false;
}
$data =& $data[$key];
}
}
return true;
}
function get_value($array, $path, $defaultValue = FALSE) {
if (!is_array($path))
$path = explode('.', $path);
foreach ($path as $i => $key) {
if (is_numeric($key) && intval($key) > 0 || $key === '0')
$key = intval($key);
if ($i === count($path) - 1) {
if (is_array($array) && array_key_exists($key, $array))
return $array[$key];
else
break;
}
if (!is_array($array) || !array_key_exists($key, $array))
break;
$array = & $array[$key];
}
return $defaultValue;
}
// Sample usage
$data = array('aaa' => array(
'bbb' => 'bbb',
'ccc' => array(
'ddd' => 'ddd'
)
));
var_dump( check($data, 'aaa.bbb') ); // true
var_dump( check($data, 'aaa.bbb', 'aaa.ccc') ); // true
var_dump( check($data, 'zzz') ); // false
var_dump( check($data, 'aaa.bbb', 'zzz') ); // false
var_dump( get_value($data, 'aaa.bbb', 'default value') ); // "bbb"
var_dump( get_value($data, 'zzz', 'default value') ); // "default value"

Categories