Periksa untuk melihat apakah suatu string diserialisasi?

Jawaban:

191

Saya akan mengatakan, cobalah unserialize;-)

Mengutip manual:

Jika string yang diteruskan tidak dapat dibatalkan, FALSE dikembalikan dan E_NOTICE dikeluarkan.

Jadi, Anda harus memeriksa apakah nilai pengembaliannya falseatau tidak (dengan ===atau !==, untuk memastikan tidak memiliki masalah dengan 0atau nullatau apa pun yang setara dengan false, saya katakan) .

Berhati-hatilah dengan pemberitahuan: Anda mungkin ingin / perlu menggunakan operator @ .

Misalnya :

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

Akan membuat Anda:

not ok


EDIT: Oh, dan seperti yang dikatakan @Peter (terima kasih padanya!), Anda mungkin akan mendapat masalah jika Anda mencoba untuk membatalkan registrasi representasi boolean false :-(

Jadi, memeriksa bahwa string serial Anda tidak sama dengan " b:0;" mungkin membantu juga; sesuatu seperti ini harus melakukan trik, saya kira:

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

menguji kasus khusus itu sebelum mencoba unserialize akan menjadi optimasi - tetapi mungkin tidak berguna, jika Anda tidak sering memiliki nilai serial yang salah.

Pascal MARTIN
sumber
20
Tetapi bagaimana jika nilai unserialized adalah boolean dengan nilai FALSE?
Peter
1
@ Peter: komentar luar biasa; Saya telah mengedit jawaban saya dengan proposisi untuk menangani kasus itu; terima kasih!
Pascal MARTIN
Terima kasih. :) Saya berasumsi ini mungkin akan menjadi jawabannya .. Sepertinya saya harus ada cara untuk mencari tahu apakah itu serial sebelum benar-benar memaksa parser untuk mencoba memprosesnya.
Dang
1
Apakah metode ini memiliki dampak yang wajar pada kinerja dengan potongan data yang lebih besar?
pie6k
2
PENTING: Jangan pernah unserialize data pengguna mentah karena dapat digunakan sebagai vektor serangan. OWASP: PHP_Object_Injection
ArtBIT
56

Saya tidak menulis kode ini, sebenarnya dari WordPress. Kupikir aku akan memasukkannya untuk siapa pun yang tertarik, itu mungkin berlebihan tetapi bekerja :)

<?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
sumber
1
Saya pada dasarnya membutuhkan regex untuk melakukan deteksi dasar, saya akhirnya menggunakan:^([adObis]:|N;)
farinspace
5
Versi WordPress saat ini agak lebih canggih: codex.wordpress.org/Function_Reference/…
ChrisV
3
+1 untuk memberi kredit. Saya tidak tahu WordPress memiliki ini bawaan. Terima kasih atas idenya - Sekarang saya akan melanjutkan dan membuat arsip fungsi yang bermanfaat dari WordPress Core.
Amal Murali
Referensi fungsi URL to wordpress terbaru: developer.wordpress.org/reference/functions/is_serialized
Cédric Françoys
18

Mengoptimalkan respons Pascal MARTIN

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

Jika $ string adalah falsenilai serial , yaitu fungsi $string = 'b:0;' SoN9ne kembali false, itu salah

jadi fungsinya akan

/**
 * 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
sumber
2
Menukar urutan tes ini akan lebih efisien.
artfulrobot
@ (Pada operator) harus berkecil hati. Gunakan try catch block sebagai gantinya.
Francisco Luz
@FranciscoLuz dari manual php.net/manual/en/function.unserialize.php In case the passed string is not unserializeable, FALSE is returned and E_NOTICE is issued. Kami tidak dapat menangkap kesalahan E_NOTICE karena itu bukan pengecualian.
Hazem Noor
@HazemNoor Saya mengujinya dengan PHP 7 dan itu tertangkap. Juga, di PHP 7, ada catch (\ Throwable $ e) yang menangkap semua yang salah di bawah tenda.
Francisco Luz
@FranciscoLuz bagaimana Anda menangkap E_Notice dalam PHP 7?
user427969
13

Terlepas dari jawaban Pascal MARTIN yang luar biasa, saya ingin tahu apakah Anda dapat mendekati ini dengan cara lain, jadi saya melakukan ini hanya sebagai latihan 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 );
}

Dan itu benar-benar berfungsi. Satu-satunya peringatan adalah bahwa kemungkinan akan rusak jika Anda memiliki penangan kesalahan terdaftar karena cara $ php_errormsg bekerja .

Peter Bailey
sumber
1
+1: Yang ini menyenangkan, harus saya akui - tidak akan memikirkannya! Dan saya juga tidak menemukan cara untuk membuatnya gagal ^^ Kerja bagus! Dan terima kasih atas komentar atas jawaban saya: tanpanya, saya mungkin tidak akan melihat jawaban ini.
Pascal MARTIN
$ a = 'bla'; $ b = 'b: 0;'; Cobalah untuk membatalkan registrasi $ a lalu $ b dengan ini, keduanya akan gagal sedangkan $ b tidak seharusnya.
bardiir
Tidak jika ada kegagalan sebelumnya. Karena $ php_errormsg akan tetap mengandung kesalahan serialisasi dari sebelum dan setelah Anda membatalkan false maka itu akan gagal.
bardiir
Ya, tetapi hanya jika Anda tidak melakukan kesalahan-periksa di antara deserializing $adan deserializing $b, yang bukan desain aplikasi praktis.
Peter Bailey
11
$data = @unserialize($str);
if($data !== false || $str === 'b:0;')
    echo 'ok';
else
    echo "not ok";

Menangani kasus dengan benar serialize(false). :)

kekacauan
sumber
3

membangun fungsi

function isSerialized($value)
{
   return preg_match('^([adObis]:|N;)^', $value);
}
RossW
sumber
1
Regex ini berbahaya, mengembalikan positif ketika a:(atau b:dll) ada di suatu tempat di dalam nilai $, bukan di awal. Dan di ^sini bukan berarti awal dari sebuah string. Benar-benar menyesatkan.
Denis Chmel
3

Ada solusi WordPress: (detail ada di sini)

    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;
    }
berbakat
sumber
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
sumber
5
baik, ini akan memberikan kebenaran untuk banyak string JSON juga, bukan? Jadi tidak dapat diandalkan untuk menentukan apakah string dapat un / serial.
Gordon
Mungkin benar, tetapi jika alternatifnya adalah serial, atau hanya teks biasa, seperti bagi saya, itu bekerja seperti pesona.
Björn3
1
@ Björn3 "Yah, ini berfungsi untuk saya dalam kasus khusus ini" adalah mentalitas yang sangat buruk untuk dimiliki ketika coding. Ada banyak pengembang yang malas atau tidak berpikiran maju seperti ini dan itu membuat mimpi buruk di kemudian hari ketika pengembang lain harus bekerja dengan kode mereka atau mencoba mengubah sesuatu dan tiba-tiba tidak ada yang berfungsi dengan baik lagi.
BadHorsie
Membuat kode yang lengkap (jika itu mungkin) tidak selalu menjadi tujuan atau praktik terbaik. Tidak ketika datang pada expence waktu. Ini hanya benar dari sudut pandang programmer. Dalam kehidupan nyata ada banyak situasi di mana cepat dan kotor adalah cara yang disukai.
Björn3
1

Ini berfungsi baik untuk saya

<?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
sumber
Harap perhatikan ini memeriksa jika string yang diberikan adalah string yang tampak bersambung - itu tidak akan benar-benar memeriksa validitas string tersebut.
eithed
-2

Saya lebih suka melakukannya seperti itu:

 if (is_array(unserialize($serialized_string))):
merosot
sumber
Mengapa variabel serial harus berupa array? Itu bisa benar-benar dari jenis apa pun.
Valerio Bozz