Перевірте, чи є рядок серіалізованим?


Відповіді:


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


EDIT: О, і як сказав @Peter (спасибі йому!), Ви можете зіткнутися з проблемою, якщо ви намагатиметесь несеріалізувати подання булевого помилкового :-(

Отже, перевірка того, що ваша серіалізована рядок не дорівнює " b:0;", також може бути корисною; щось подібне повинно зробити трюк, я думаю:

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

тестування цього особливого випадку перед спробою зняти серіалізацію було б оптимізацією - але, мабуть, це не корисно, якщо ви часто не маєте помилкового серіалізованого значення.


20
Але що робити, якщо несеріалізоване значення булеве значення зі значенням FALSE?
Пітер

1
@ Петер: відмінне зауваження; Я відредагував свою відповідь із пропозицією розібратися у цій справі; Дякую !
Паскаль МАРТИН

Дякую. :) Я припускав, що це, мабуть, буде відповіддю. Просто мені здається, що повинен бути спосіб дізнатися, чи є він серіалізованим, перш ніж насправді змусити парсер спробувати його обробити.
Данг

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 Core.
Амаль Муралі

Остання URL посилання на функцію wordpress: developer.wordpress.org/reference/functions/is_serialized
Cédric Françoys

18

Оптимізація відповіді Паскаля MARTIN

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

16

Якщо рядок $ - це серіалізоване falseзначення, тобто повертається функція $string = 'b:0;' SoN9nefalse , це неправильно

тому функція була б

/**
 * 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

@ (У оператора) слід не рекомендувати. Використовуйте замість цього спробувати блок catch.
Франциско Луз

@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, оскільки вона не є викинутим винятком.
Hazem Noor

@HazemNoor Я перевірив це на PHP 7, і він все-таки потрапляє. Також у PHP 7 є улов (\ Throwable $ e), який ловить усе, що йде не так під кришкою.
Франциско Луз

@FranciscoLuz як ти зловив E_Notice в PHP 7?
користувач427969

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 не повинно.
bardiir

Ні, якщо раніше був збій. Оскільки $ php_errormsg все ще буде містити помилку серіалізації від раніше, і як тільки ви дезаріалізуватимете false, вона вийде з ладу.
bardiir

Так, але тільки якщо ви не перевіряєте помилки між десяриалізацією $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, а не на початку. І ^тут не означає початку рядка. Це абсолютно вводить в оману.
Денис Чмель

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

Створення цілком міцного коду (якщо це навіть було можливо) - це не завжди мета та найкраща практика. Не тоді, коли йдеться про витрату часу. Це справедливо лише з точки зору програмістів. У реальному житті існує багато обставин, коли швидкий і брудний - найкращий спосіб.
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));
    }

?>

Будь ласка, майте на увазі, що це перевіряє, чи задана рядок є серіалізованим виглядом - вона фактично не перевіряє дійсність цієї рядка.
вийшов

-2

Я вважаю за краще це робити так:

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

Чому серіалізована змінна повинна бути масивом? Це дійсно може бути будь-якого типу.
Валеріо Бозз
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.