Проверить, сериализована ли строка?

Ответы:

191

Я бы сказал, попробуйте unserialize;-)

Цитирование руководства:

В случае, если переданная строка не является сериализуемой, возвращается FALSE и выдается E_NOTICE.

Итак, вы должны проверить, является ли возвращаемое значение falseили нет ===или !==, чтобы убедиться, что у вас нет проблем с 0или nullили с чем-то, что равно 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: отличное замечание; Я отредактировал свой ответ предложением разобраться с этим делом; Спасибо !
Паскаль МАРТИН
Спасибо. :) Я предположил, что это, вероятно, будет ответом. Мне кажется, что должен быть способ выяснить, сериализован ли он, прежде чем заставить парсер попытаться его обработать.
Dang
1
Влияет ли этот метод на производительность при работе с большими объемами данных?
pie6k
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.
Амаль Мурали
Последний URL-адрес ссылки на функцию
WordPress
18

Оптимизация ответа Паскаля МАРТИНА

/**
 * Check if a string is serialized
 * @param string $string
 */
public static function is_serial($string) {
    return (@unserialize($string) !== false);
}
SoN9ne
источник
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
@ (В операторе) не рекомендуется. Вместо этого используйте try catch block.
Франциско Луз
@FranciscoLuz из руководства php.net/manual/en/function.unserialize.php In case the passed string is not unserializeable, FALSE is returned and E_NOTICE is issued. Мы не можем отловить ошибку E_NOTICE, поскольку она не является сгенерированным исключением.
Хазем Нур
@HazemNoor Я проверил его с PHP 7, и он пойман. Кроме того, в PHP 7 есть catch (\ Throwable $ e), который ловит все, что идет не так, как надо.
Франциско Луз
@FranciscoLuz, как вы поймали E_Notice в PHP 7?
user427969
13

Несмотря на отличный ответ Паскаля МАРТИНА, мне было любопытно, если бы вы могли подойти к этому по-другому, поэтому я сделал это просто как умственное упражнение

<?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: это весело, я должен признать - не подумал бы об этом! И я тоже не нахожу способа заставить его потерпеть неудачу ^^ Отличная работа! И спасибо за комментарий к моему ответу: без него я бы, наверное, не увидел этот ответ.
Паскаль МАРТИН
$ a = 'bla'; $ b = 'b: 0;'; Попробуйте десериализовать $ a затем $ b с этим, оба потерпят неудачу, в то время как $ b не должен.
Бардиир
Нет, если произошел сбой прямо перед этим. Поскольку $ php_errormsg по-прежнему будет содержать ошибку сериализации, и, как только вы десериализуете false, произойдет сбой.
Бардиир
Да, но только если вы не проверяете ошибки между десериализацией $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);
}
RossW
источник
1
Это регулярное выражение опасно, оно возвращает положительное значение, когда a:(или b:т. Д.) Присутствует где-то внутри $ value, а не в начале. И ^здесь не означает начало строки. Это вводит в заблуждение.
Денис Чмель
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;
}

}
Björn3
источник
5
ну, это будет верно для многих строк JSON, не так ли? Поэтому невозможно определить, может ли строка быть де-сериализированной.
Гордон
Возможно, это правда, но если альтернатива сериализирована, или просто текст, как это было для меня, это работает как шарм.
Björn3
1
@ Björn3 «Ну, это работает для меня в данном конкретном случае» - это действительно плохой менталитет при кодировании. Есть много разработчиков, которые ленивы или не думают о будущем, как это, и это создает кошмар позже, когда другие разработчики должны работать со своим кодом или пытаться что-то изменить, и вдруг ничего больше не работает должным образом.
BadHorsie
Создание полностью надежного кода (если это вообще возможно) не всегда является целью или наилучшей практикой. Не тогда, когда это происходит в течение времени. Это верно только с точки зрения программистов. В реальной жизни есть много обстоятельств, где быстрый и грязный является предпочтительным способом.
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))):
degers
источник
Почему сериализованная переменная должна быть массивом? Это действительно может быть любого типа.
Валерио Бозз