Verifique se uma sequência é serializada?


Respostas:


191

Eu diria, tente unserialize;-)

Citando o manual:

Caso a sequência passada não seja desserializável, FALSE será retornado e E_NOTICE será emitido.

Portanto, você deve verificar se o valor de retorno é falseou não (com ===ou !==, para ter certeza de não ter nenhum problema com 0ou nullou qualquer coisa que seja igual a false, eu diria) .

Apenas tome cuidado com o aviso: você pode querer / precisar usar o operador @ .

Por exemplo :

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

Você receberá:

not ok


EDIT: Ah, e como @ Peter disse (graças a ele!), Você pode ter problemas se estiver tentando desserializar a representação de um falso booleano :-(

Portanto, verificar se sua string serializada não é igual a " b:0;" também pode ser útil; algo assim deve funcionar, suponho:

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

testar esse caso especial antes de tentar desserializar seria uma otimização - mas provavelmente não é útil se você não tiver um valor serializado falso com frequência.


20
Mas e se o valor não serializado for um booleano com um valor FALSE?
— Peter Peter

1
@ Peter: excelente observação; Eu editei minha resposta com uma proposição para lidar com esse caso; obrigado !
— Pascal MARTIN

Obrigado. :) Eu assumi que essa provavelmente seria a resposta .. Parece-me que deveria haver uma maneira de descobrir se ele foi serializado antes de forçar o analisador a tentar processá-lo.
— Dang

1
Esse método tem algum impacto razoável no desempenho com dados maiores?
— pie6k

2
IMPORTANTE: nunca desserialize dados brutos do usuário, pois eles podem ser usados ​​como um vetor de ataque. OWASP: PHP_Object_Injection
— ArtBIT 01/09/17

56

Eu não escrevi esse código, é do WordPress, na verdade. Pensei em incluí-lo para qualquer pessoa interessada, pode ser um exagero, mas funciona :)

<?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
Eu basicamente precisava de um regex para fazer uma detecção básica, e acabei usando:^([adObis]:|N;)
— farinspace 10/10

5
A versão atual do WordPress é um pouco mais sofisticada: codex.wordpress.org/Function_Reference/…
— ChrisV

3
+1 para dar créditos. Eu não sabia que o WordPress tinha esse recurso embutido. Obrigado pela ideia - agora vou criar um arquivo de funções úteis a partir do WordPress Core.
— Amal Murali

Referência mais recente da URL para a função wordpress: developer.wordpress.org/reference/functions/is_serialized
— Cédric Françoys

18

Otimizando a resposta de Pascal MARTIN

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

16

Se a string $ é um falsevalor serializado , ou seja , a função do $string = 'b:0;' SoN9ne retorna false, está errado

então a função seria

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

2
Trocar a ordem desses testes seria mais eficiente.
— Artfulrobot

O @ (no operador) deve ser desencorajado. Use try catch block em vez disso.
— Francisco Luz

@FranciscoLuz do manual php.net/manual/en/function.unserialize.php In case the passed string is not unserializeable, FALSE is returned and E_NOTICE is issued. Não podemos pegar o erro E_NOTICE, pois não é uma exceção lançada.
— Hazem Noor

@HazemNoor Eu testei com o PHP 7 e ele foi pego. Além disso, no PHP 7, existe o catch (\ Throwable $ e) que captura tudo o que dá errado sob o capô.
— Francisco Luz

@FranciscoLuz como você pegou o E_Notice no PHP 7?
— user427969

13

Apesar da excelente resposta de Pascal MARTIN, fiquei curioso para saber se você poderia abordar isso de outra maneira, então fiz isso apenas como um exercício mental

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

E realmente funciona. A única ressalva é que provavelmente será interrompida se você tiver um manipulador de erros registrado por causa do funcionamento do $ php_errormsg .


1
+1: Este é divertido, eu tenho que admitir - não teria pensado nisso! E também não encontro um meio de fazê-lo falhar ^^ Bom trabalho! E obrigado pelo comentário sobre a minha resposta: sem ela, eu provavelmente não teria visto essa resposta.
— Pascal MARTIN

$ a = 'bla'; $ b = 'b: 0;'; Tente desserializar $ a então $ b com isso, ambos falharão enquanto $ b não deve.
— bardiir

Não se houve uma falha logo antes. Como o $ php_errormsg ainda conterá o erro de serialização de antes e depois que você desserializar false, ele falhará.
— bardiir

Sim, mas apenas se você não verificar o erro entre desserializar $ae desserializar $b, o que não é um design de aplicativo prático.
— Peter Bailey

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

Lida corretamente com o caso de serialize(false). :)


3

construir em uma função

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

1
Esse regex é perigoso, está retornando positivo quando a:(ou b:etc) está presente em algum lugar dentro do valor $, não no começo. E ^aqui não significa começo de uma string. É totalmente enganador.
— Denis Chmel

3

Existe a solução WordPress: (o detalhe está aqui)

    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
bem, isso daria certo para muitas strings JSON também, não? Portanto, não é confiável determinar se a sequência pode ser des / serializada.
— Gordon

Pode ser verdade, mas se a alternativa for serializada, ou apenas texto simples, como era para mim, funcionará como um encanto.
— Björn3

1
@ Björn3 "Bem, funciona para mim neste caso específico" é uma mentalidade muito ruim de se ter ao codificar. Existem muitos desenvolvedores que são preguiçosos ou não pensam nisso, e isso cria um pesadelo mais tarde, quando outros desenvolvedores precisam trabalhar com seu código ou tentar mudar alguma coisa e, de repente, nada funciona mais.
— BadHorsie 18/03/2014

Criar código completamente sólido (se isso fosse possível) nem sempre é o objetivo ou a melhor prática. Não quando se trata de uma despesa de tempo. Isso é verdade apenas da perspectiva dos programadores. Na vida real, existem muitas circunstâncias em que a maneira mais rápida e suja é a preferida.
— Björn3

1

Este trabalho é bom para mim

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

?>

Lembre-se de verificar se a sequência especificada é de aparência serializada - na verdade, ela não verifica a validade dessa sequência.
— eithed 8/08/16

-2

Eu prefiro fazer dessa maneira:

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

Por que a variável serializada deve ser uma matriz? Pode realmente ser de qualquer tipo.
— Valerio Bozz
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.