检查字符串是否已序列化?


Answers:


191

我会说,尝试unserialize一下;-)

引用手册:

如果传递的字符串不是不可序列化的,则返回FALSE并发出E_NOTICE。

所以,你必须检查返回值是false或不是(有===!==,以确保不会有任何问题0null或任何等于false,我会说)

只是当心通知:您可能希望/需要使用@运算符

例如 :

$str = 'hjkl';
$data = @unserialize($str);
if ($data !== false) {
    echo "ok";
} else {
    echo "not ok";
}

会给你:

not ok


编辑:哦,就像@Peter说的(感谢他!),如果您试图反序列化布尔型false的表示,则可能会遇到麻烦:-(

因此,检查您的序列化字符串不等于“ b:0;”也可能会有所帮助;我想像这样的东西应该可以解决问题:

$data = @unserialize($str);
if ($str === 'b:0;' || $data !== false) {
    echo "ok";
} else {
    echo "not ok";
}

在尝试反序列化之前测试该特殊情况将是一种优化-但是,如果您经常没有错误的序列化值,那么可能就没那么有用了。


20
但是,如果未序列化的值是值为FALSE的布尔值怎么办?
彼得

1
@Peter:很好的评论; 我已经修改了答案以解决这个案件; 谢谢 !
Pascal MARTIN

谢谢。:)我以为这可能是答案。.在我看来,应该有一种方法在实际迫使解析器尝试对其进行处理之前,先找出它是否已序列化。
当当

1
此方法对更大数据量的性能是否有合理影响?
pie6k 2013年

2
重要信息:永远不要反序列化原始用户数据,因为它可以用作攻击媒介。OWASP:PHP_Object_Injection
ArtBIT

56

我没有编写此代码,实际上是来自WordPress的。以为我会把它包括给任何有兴趣的人,这可能有点过分了,但是它是可行的:)

<?php
function is_serialized( $data ) {
    // 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 ( !preg_match( '/^([adObis]):/', $data, $badions ) )
        return false;
    switch ( $badions[1] ) {
        case 'a' :
        case 'O' :
        case 's' :
            if ( preg_match( "/^{$badions[1]}:[0-9]+:.*[;}]\$/s", $data ) )
                return true;
            break;
        case 'b' :
        case 'i' :
        case 'd' :
            if ( preg_match( "/^{$badions[1]}:[0-9.E-]+;\$/", $data ) )
                return true;
            break;
    }
    return false;
}

1
我基本上需要一个正则表达式来进行基本检测,最终我使用了:^([adObis]:|N;)
farinspace

5
当前的WordPress版本是较为复杂:codex.wordpress.org/Function_Reference/...
ChrisV

3
+1表示赠送金额。我不知道WordPress具有此内置功能。感谢您的想法-现在,我将继续从WordPress Core创建有用功能的档案。
阿马尔·穆拉利


18

优化Pascal MARTIN的响应

/**
 * Check if a string is serialized
 * @param string $string
 */
public static function is_serial($string) {
    return (@unserialize($string) !== false);
}

16

如果$ string是一个序列化false值,即$string = 'b:0;' SoN9ne的函数返回false,那就错了

所以功能是

/**
 * Check if a string is serialized
 *
 * @param string $string
 *
 * @return bool
 */
function is_serialized_string($string)
{
    return ($string == 'b:0;' || @unserialize($string) !== false);
}

2
交换这些测试的顺序将更有效。
artfulrobot

不建议使用@(at运算符)。请改用try catch块。
弗朗西斯科·卢兹

来自手册php.net/manual/en/function.unserialize.php的@FranciscoLuz In case the passed string is not unserializeable, FALSE is returned and E_NOTICE is issued. 我们无法捕获E_NOTICE错误,因为它不是引发的异常。
Hazem Noor

@HazemNoor我用PHP 7对其进行了测试,但确实被捕获了。另外,在PHP 7中,有catch(\ Throwable $ e)可以捕获所有在后台出错的内容。
弗朗西斯科·卢兹

@FranciscoLuz您是如何在PHP 7中捕获E_Notice的?
user427969

13

尽管Pascal MARTIN给出了很好的回答,但我很好奇您是否可以采用另一种方法,所以我这样做只是出于心理锻炼

<?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 );
}

它实际上有效。唯一需要注意的是,由于$ php_errormsg的工作方式,如果您已经注册了错误处理程序,它可能会中断。


1
+1:这很有趣,我不得不承认-不会考虑的!而且我也找不到使它失败的方法^^做得好!感谢您对我的答案的评论:没有它,我可能不会看到这个答案。
Pascal MARTIN

$ a ='bla'; $ b ='b:0;'; 尝试用$ a然后$ b取消序列化,两者都将失败,而$ b不应该。
bardiir 2014年

如果之前有故障,则不会。因为$ php_errormsg仍将包含之前的序列化错误,并且一旦将反序列化为false,它将失败。
bardiir 2014年

是的,但前提是您不对反序列化$a和反序列化之间进行错误检查$b,这不是实际的应用程序设计。
彼得·贝利

11
$data = @unserialize($str);
if($data !== false || $str === 'b:0;')
    echo 'ok';
else
    echo "not ok";

正确处理的情况serialize(false)。:)


3

内置功能

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

1
这个正则表达式很危险,当a:(或b:等)出现在$ value内某处而不是开始时,它返回正数。而^这里并不意味着开头的字符串的。这完全是误导。
Denis Chmel

3

有WordPress解决方案:(详细信息在这里)

    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;
    }

2
/**
 * 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;
}

}

5
好吧,这对于许多JSON字符串同样适用,不是吗?因此,确定字符串是否可以反序列化是不可靠的。
戈登

可能是正确的,但是如果对我来说替代方法是序列化的,或者只是纯文本,它就像一种魅力。
Björn3

1
@Björn3“在这种情况下,它对我有用”是编码时的一种非常糟糕的心态。有很多开发人员像这样懒惰或不具有前瞻性,这会在以后导致其他开发人员不得不使用其代码或尝试更改某些内容而突然无法正常工作的噩梦。
BadHorsie 2014年

编写完全可靠的代码(即使可能的话)也不总是目标或最佳实践。不是随着时间的推移而来的。仅从程序员的角度来看这是正确的。在现实生活中,很多情况下,快速和肮脏是首选方法。
Björn3

1

这对我来说很好

<?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));
    }

?>

请记住,这会检查给定的字符串是否为看起来是序列化的字符串-它实际上不会检查该字符串的有效性。
eithed

-2

我更喜欢这样:

 if (is_array(unserialize($serialized_string))):

为什么序列化变量应该是数组?它实际上可以是任何类型。
Valerio Bozz
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.