Überprüfen Sie, ob eine Zeichenfolge serialisiert ist.

Antworten:

191

Ich würde sagen, versuch unserializees ;-)

Zitat des Handbuchs:

Falls die übergebene Zeichenfolge nicht unserialisierbar ist, wird FALSE zurückgegeben und E_NOTICE ausgegeben.

Sie müssen also überprüfen, ob der Rückgabewert ist falseoder nicht (mit ===oder !==, um sicherzugehen, dass Sie kein Problem mit 0oder nulloder etwas haben, das gleich ist false, würde ich sagen) .

Beachten Sie nur den Hinweis: Möglicherweise möchten / müssen Sie den Operator @ verwenden .

Zum Beispiel :

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

Kriege dich :

not ok


EDIT: Oh, und wie @Peter sagte (danke an ihn!), Könnten Sie in Schwierigkeiten geraten, wenn Sie versuchen, die Darstellung eines booleschen Falsches zu unserialisieren :-(

Daher kann b:0;es auch hilfreich sein, zu überprüfen, ob Ihre serialisierte Zeichenfolge nicht gleich " " ist. so etwas sollte den Trick machen, nehme ich an:

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

Das Testen dieses Sonderfalls vor dem Versuch, unserialisiert zu werden, wäre eine Optimierung - aber wahrscheinlich nicht so nützlich, wenn Sie nicht oft einen falsch serialisierten Wert haben.

Pascal MARTIN
quelle
20
Was aber, wenn der unserialisierte Wert ein Boolescher Wert mit dem Wert FALSE ist?
Peter
1
@ Peter: ausgezeichnete Bemerkung; Ich habe meine Antwort mit einem Vorschlag bearbeitet, um diesen Fall zu behandeln. Vielen Dank !
Pascal MARTIN
Vielen Dank. :) Ich nahm an, dass dies wahrscheinlich die Antwort sein würde. Es scheint mir nur, dass es eine Möglichkeit geben sollte, herauszufinden, ob es serialisiert ist, bevor der Parser tatsächlich gezwungen wird, zu versuchen, es zu verarbeiten.
Dang
1
Hat diese Methode einen angemessenen Einfluss auf die Leistung bei größeren Datenmengen?
Pie6k
2
WICHTIG: Niemals unserialisieren Sie rohe Benutzerdaten, da diese als Angriffsvektor verwendet werden können. OWASP: PHP_Object_Injection
ArtBIT
56

Ich habe diesen Code nicht geschrieben, er stammt eigentlich aus WordPress. Ich dachte, ich würde es für alle Interessierten einschließen, es könnte übertrieben sein, aber es funktioniert :)

<?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;
}
Brandon Wamboldt
quelle
1
Ich brauchte im Grunde eine Regex, um eine grundlegende Erkennung ^([adObis]:|N;)
durchzuführen. Am
5
Die aktuelle WordPress-Version ist etwas ausgefeilter: codex.wordpress.org/Function_Reference/…
ChrisV
3
+1 für die Vergabe von Credits. Ich wusste nicht, dass WordPress dies eingebaut hat. Vielen Dank für die Idee - ich werde jetzt ein Archiv nützlicher Funktionen aus dem WordPress Core erstellen.
Amal Murali
Neueste URL zur Referenz der WordPress-Funktion: developer.wordpress.org/reference/functions/is_serialized
Cédric Françoys
18

Optimierung der Reaktion von Pascal MARTIN

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

Wenn der $ string ein serialisierter falseWert ist, $string = 'b:0;' gibt die Funktion von SoN9ne zurückfalse , ist dies falsch

so wäre die Funktion

/**
 * Check if a string is serialized
 *
 * @param string $string
 *
 * @return bool
 */
function is_serialized_string($string)
{
    return ($string == 'b:0;' || @unserialize($string) !== false);
}
Hazem Noor
quelle
2
Ein Austausch der Reihenfolge dieser Tests wäre effizienter.
Artfulrobot
Das @ (am Operator) sollte nicht empfohlen werden. Verwenden Sie stattdessen try catch block.
Francisco Luz
@FranciscoLuz aus dem Handbuch php.net/manual/en/function.unserialize.php In case the passed string is not unserializeable, FALSE is returned and E_NOTICE is issued. Wir können den E_NOTICE-Fehler nicht abfangen, da es sich nicht um eine ausgelöste Ausnahme handelt.
Hazem Noor
@HazemNoor Ich habe es mit PHP 7 getestet und es wird erwischt. Außerdem gibt es in PHP 7 catch (\ Throwable $ e), das alles auffängt, was unter der Haube schief geht.
Francisco Luz
@FranciscoLuz Wie haben Sie E_Notice in PHP 7 gefangen?
user427969
13

Trotz der hervorragenden Antwort von Pascal MARTIN war ich neugierig, ob Sie dies anders angehen könnten, also tat ich dies nur als mentale Übung

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

Und es funktioniert tatsächlich. Die einzige Einschränkung ist, dass es wahrscheinlich kaputt geht, wenn Sie einen registrierten Fehlerbehandler haben, weil $ php_errormsg funktioniert .

Peter Bailey
quelle
1
+1: Das macht Spaß, muss ich zugeben - hätte nicht darüber nachgedacht! Und ich finde auch keinen Weg, es zum Scheitern zu bringen ^^ Gute Arbeit! Und danke für den Kommentar zu meiner Antwort: Ohne ihn hätte ich diese Antwort wahrscheinlich nicht gesehen.
Pascal MARTIN
$ a = 'bla'; $ b = 'b: 0;'; Versuchen Sie, $ a und dann $ b damit zu unserialisieren. Beide schlagen fehl, während $ b dies nicht tun sollte.
Bardiir
Nicht, wenn es vorher einen Fehler gab. Da $ php_errormsg immer noch den Serialisierungsfehler von zuvor enthält und wenn Sie false deserialisieren, schlägt er fehl.
Bardiir
Ja, aber nur, wenn Sie zwischen Deserialisierung $aund Deserialisierung keine Fehlerprüfung durchführen $b, was kein praktisches Anwendungsdesign ist.
Peter Bailey
11
$data = @unserialize($str);
if($data !== false || $str === 'b:0;')
    echo 'ok';
else
    echo "not ok";

Behandelt den Fall von richtig serialize(false). :) :)

Chaos
quelle
3

in eine Funktion einbauen

function isSerialized($value)
{
   return preg_match('^([adObis]:|N;)^', $value);
}
RossW
quelle
1
Diese Regex ist gefährlich und wird positiv zurückgegeben, wenn a:(oder b:usw.) irgendwo innerhalb von $ value vorhanden ist, nicht am Anfang. Und ^hier heißt das nicht, dass eine Zeichenfolge beginnt. Es ist total irreführend.
Denis Chmel
3

Es gibt WordPress-Lösung: (Detail ist hier)

    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;
    }
genial
quelle
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
quelle
5
Nun, dies würde auch für viele JSON-Strings zutreffen, nicht wahr? Es ist daher nicht zuverlässig zu bestimmen, ob die Zeichenfolge nicht / serialisiert werden kann.
Gordon
Könnte wahr sein, aber wenn die Alternative serialisiert ist oder einfach nur Text, wie es für mich war, funktioniert es wie ein Zauber.
Björn3
1
@ Björn3 "Nun, es funktioniert in diesem speziellen Fall für mich" ist eine wirklich schlechte Mentalität beim Codieren. Es gibt viele Entwickler, die faul oder nicht zukunftsorientiert sind, und es ist später ein Albtraum, wenn andere Entwickler mit ihrem Code arbeiten oder versuchen müssen, etwas zu ändern, und plötzlich funktioniert nichts mehr richtig.
BadHorsie
Es ist nicht immer das Ziel oder die beste Vorgehensweise, vollständig soliden Code zu erstellen (falls dies überhaupt möglich war). Nicht, wenn es zeitaufwändig ist. Dies gilt nur aus Sicht des Programmierers. Im wirklichen Leben gibt es viele Umstände, in denen schnell und schmutzig der bevorzugte Weg ist.
Björn3
1

Das funktioniert gut für mich

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

?>
Daniel Lichtenberg
quelle
Beachten Sie bitte, dass bei dieser Überprüfung überprüft wird, ob es sich bei der angegebenen Zeichenfolge um eine Zeichenfolge handelt, die nach Serialisierung aussieht. Die Gültigkeit dieser Zeichenfolge wird jedoch nicht überprüft.
Am
-2

Ich mache es lieber so:

 if (is_array(unserialize($serialized_string))):
Degers
quelle
Warum sollte die serialisierte Variable ein Array sein? Es kann wirklich von jedem Typ sein.
Valerio Bozz