文字列がシリアル化されているかどうかを確認しますか?


回答:


191

私はunserializeそれを試してみると思います;-)

マニュアルの引用:

渡された文字列がシリアル化できない場合、FALSEが返され、E_NOTICEが発行されます。

そのため、戻り値がfalseかどうかを確認する必要があります===または!==、またはと等しい0nullまたは何かに問題がないことを確認するにはfalse

注意してください:@演算子を使用したい/必要があるかもしれません。

例えば ​​:

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

あなたを取得します:

not ok


編集:ああ、そして@ピーターが言ったように(彼に感謝します!)、ブールの表現をシリアル化解除しようとすると問題が発生する可能性がありますfalse :-(

したがって、シリアル化された文字列が「b:0;」に等しくないことを確認することも役立つ可能性があります。このような何かがうまくいくはずだと思います:

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

シリアル化を解除しようとする前にその特別なケースをテストすることは最適化です。


20
しかし、シリアル化されていない値がFALSEの値を持つブール値である場合はどうなりますか?
ピーター

1
@ピーター:素晴らしい発言; 私はそのケースに対処するための命題で私の回答を編集しました。ありがとう!
パスカルマルタン

ありがとう。:)私はこれがおそらく答えになるだろうと思っていました..パーサーに実際に処理を試みる前にシリアル化されているかどうかを確認する方法があるはずだと私には思えます。
ダン

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コアから便利な関数のアーカイブを作成します。
Amal Murali 2014

wordpressの関数リファレンスに最新のURL:developer.wordpress.org/reference/functions/is_serialized
セドリックFrançoys

18

Pascal 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;' 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ブロックを使用してください。
フランシスコ・ルス

マニュアルから@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には、内部で問題が発生したすべてをキャッチするcatch(\ Throwable $ e)があります。
Francisco Luz、

@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:これは楽しいです、私は認めざるを得ません-それについて考えなかったでしょう!そして、失敗させる方法も見つけられません^^よくできました!そして私の答えに対するコメントに感謝します:それがなければ、私はおそらくこの答えを見たことはなかったでしょう。
パスカルマルタン

$ a = 'bla'; $ b = 'b:0;'; これで$ aと$ bのシリアル化を解除してみてください。どちらも失敗しますが、$ bは失敗しません。
bardiir、2014

直前に失敗があった場合はそうではありません。$ php_errormsgには以前のシリアライゼーションエラーが含まれているため、falseをデシリアライズすると失敗します。
bardiir、2014年

そうですが、デシリアライズ$aとデシリアライズの中間でエラーチェックを行わない場合のみです$b。これは実用的なアプリケーション設計ではありません。
Peter Bailey

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
この正規表現は危険です。最初ではなく、$ value内のどこかにa:b:など)が存在する場合に正を返します。そして^、これは文字列の始まりを意味するものではありません。それは完全に誤解を招くものです。
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文字列にも当てはまりますね。したがって、文字列をun / serializedできるかどうかを判断することは信頼できません。
Gordon

本当かもしれませんが、私がそうであったように、代替がシリアル化されている場合、またはプレーンテキストの場合、それは魅力のように機能します。
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));
    }

?>

これは、指定された文字列がシリアライズされたように見える文字列であるかどうかを確認します-実際にはその文字列の有効性を確認しません。
16

-2

私はそのようにするのが好きです:

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

シリアル化された変数が配列である必要があるのはなぜですか?実際にはどのようなタイプでもかまいません。
Valerio Bozz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.