Kiểm tra xem một chuỗi được nối tiếp?


Câu trả lời:


191

Tôi muốn nói, hãy thử unserializenó ;-)

Trích dẫn hướng dẫn sử dụng:

Trong trường hợp chuỗi đã truyền không thể truy cập được, FALSE được trả về và E_NOTICE được cấp.

Vì vậy, bạn phải kiểm tra xem giá trị trả về có falsehay không (với ===hoặc !==, để chắc chắn không có bất kỳ vấn đề nào với 0hoặc nullhoặc bất cứ điều gì tương đương với false, tôi sẽ nói) .

Chỉ cần lưu ý thông báo: bạn có thể muốn / cần sử dụng toán tử @ .

Ví dụ :

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

Sẽ có em :

not ok


EDIT: Ồ, và như @Peter đã nói (cảm ơn anh ấy!), Bạn có thể gặp rắc rối nếu bạn đang cố gắng hủy xác nhận đại diện của một boolean false :-(

Vì vậy, kiểm tra xem chuỗi nối tiếp của bạn không bằng " b:0;" cũng có thể hữu ích; một cái gì đó như thế này nên thực hiện các mẹo, tôi cho rằng:

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

kiểm tra trường hợp đặc biệt đó trước khi cố gắng hủy kích hoạt sẽ là một tối ưu hóa - nhưng có lẽ không hữu ích, nếu bạn thường không có giá trị nối tiếp sai.


20
Nhưng điều gì sẽ xảy ra nếu giá trị không xác định là giá trị boolean với giá trị FALSE?
Peter

1
@Peter: nhận xét xuất sắc; Tôi đã chỉnh sửa câu trả lời của mình với một đề xuất để giải quyết trường hợp đó; cảm ơn !
Pascal MARTIN

Cảm ơn. :) Tôi cho rằng đây có lẽ sẽ là câu trả lời .. Dường như với tôi rằng nên có một cách để tìm hiểu xem nó có được nối tiếp hay không trước khi thực sự buộc trình phân tích cú pháp cố gắng xử lý nó.
Đăng

1
Phương pháp này có bất kỳ tác động hợp lý nào đến hiệu suất với các phần dữ liệu lớn hơn không?
pie6k

2
QUAN TRỌNG: Không bao giờ hủy kích hoạt dữ liệu người dùng thô vì nó có thể được sử dụng làm vectơ tấn công. OWASP: PHP_Object_Injection
ArtBIT

56

Tôi đã không viết mã này, nó thực sự từ WordPress. Nghĩ rằng tôi bao gồm nó cho bất cứ ai quan tâm, nó có thể là quá mức cần thiết nhưng nó hoạt động :)

<?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
Về cơ bản tôi cần một regex để thực hiện một phát hiện cơ bản, cuối cùng tôi đã sử dụng:^([adObis]:|N;)
farinspace

5
Phiên bản WordPress hiện nay là hơi phức tạp hơn: codex.wordpress.org/Function_Reference/...
ChrisV

3
+1 để cung cấp tín dụng. Tôi không biết WordPress có tích hợp sẵn này. Cảm ơn ý tưởng - Bây giờ tôi sẽ tiếp tục và tạo một kho lưu trữ các chức năng hữu ích từ WordPress Core.
Amal Murali

Tham chiếu chức năng URL mới nhất của wordpress: developer.wordpress.org/reference/fifts/is_serialized
Cédric Françoys 22/2/19

18

Tối ưu hóa phản hồi của Pascal MARTIN

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

16

Nếu chuỗi $ là một falsegiá trị được tuần tự hóa , tức là $string = 'b:0;' hàm của SoN9ne trả về false, thì nó đã sai

vì vậy chức năng sẽ là

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

2
Trao đổi thứ tự của các thử nghiệm này sẽ hiệu quả hơn.
artfulrobot

@ (Tại nhà điều hành) không được khuyến khích. Sử dụng thử khối bắt thay thế.
Francisco Luz

@FranciscoLuz từ hướng dẫn php.net/manual/en/function.unserialize.php In case the passed string is not unserializeable, FALSE is returned and E_NOTICE is issued. Chúng ta không thể bắt lỗi E_NOTICE vì nó không phải là một ngoại lệ ném.
Hazem Noor

@HazemNoor Tôi đã thử nghiệm nó với PHP 7 và nó đã bị bắt. Ngoài ra, trong PHP 7, có một hàm bắt (\ throwable $ e) để bắt mọi thứ sai dưới mui xe.
Francisco Luz

@FranciscoLuz bạn đã bắt E_Notice trong PHP 7 như thế nào?
dùng427969

13

Mặc dù câu trả lời tuyệt vời của Pascal MARTIN, tôi tò mò liệu bạn có thể tiếp cận theo cách này không, vì vậy tôi đã làm điều này giống như một bài tập tinh thần

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

Và nó thực sự hoạt động. Nhắc nhở duy nhất là nó có thể sẽ bị hỏng nếu bạn có trình xử lý lỗi đã đăng ký vì cách $ php_errormsg hoạt động .


1
+1: Điều này thật thú vị, tôi phải thừa nhận - sẽ không nghĩ về điều đó! Và tôi cũng không tìm ra cách để làm cho nó thất bại nữa ^^ Làm tốt lắm! Và cảm ơn vì nhận xét về câu trả lời của tôi: nếu không có nó, có lẽ tôi sẽ không thấy câu trả lời này.
Pascal MARTIN

$ a = 'bla'; $ b = 'b: 0;'; Cố gắng hủy xác nhận $ a rồi $ b với điều này, cả hai sẽ thất bại trong khi $ b không nên.
bardiir

Không, nếu có một thất bại ngay trước đó. Bởi vì $ php_errormsg vẫn sẽ chứa lỗi tuần tự hóa từ trước đó và một khi bạn khử lưu trữ sai thì nó sẽ thất bại.
bardiir

Vâng, nhưng chỉ khi bạn không kiểm tra lỗi giữa khử lưu huỳnh $avà khử lưu huỳnh $b, đó không phải là thiết kế ứng dụng thực tế.
Peter Bailey

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

Xử lý chính xác các trường hợp serialize(false). :)


3

xây dựng thành một chức năng

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

1
Regex này rất nguy hiểm, nó sẽ trở lại tích cực khi a:(hoặc b:vv) có mặt ở đâu đó bên trong $ value, không phải lúc đầu. Và ^ở đây không có nghĩa là bắt đầu một chuỗi. Nó hoàn toàn sai lệch.
Denis Chmel

3

Có giải pháp WordPress: (chi tiết tại đây)

    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
tốt, điều này cũng sẽ đúng với nhiều chuỗi JSON, phải không? Vì vậy, không đáng tin cậy để xác định xem chuỗi có thể hủy / nối tiếp hay không.
Gordon

Có thể đúng, nhưng nếu sự thay thế được nối tiếp, hoặc chỉ là văn bản đơn giản, như đối với tôi, nó hoạt động như một sự quyến rũ.
Bjorn3

1
@ Bjorn3 "Vâng, nó hoạt động với tôi trong trường hợp cụ thể này" là một tâm lý thực sự tồi tệ cần có khi viết mã. Có rất nhiều nhà phát triển lười biếng hoặc không suy nghĩ chuyển tiếp như thế này và điều đó tạo ra cơn ác mộng sau này khi các nhà phát triển khác phải làm việc với mã của họ hoặc cố gắng thay đổi một cái gì đó và đột nhiên không còn gì hoạt động chính xác nữa.
BadHorsie

Tạo mã hoàn toàn vững chắc (nếu điều đó thậm chí có thể) không phải lúc nào cũng là mục tiêu hoặc thực tiễn tốt nhất. Không phải khi nó đến vào một thời gian. Điều này chỉ đúng theo quan điểm lập trình viên. Trong cuộc sống thực, có rất nhiều vấn đề trong đó nhanh chóng và bẩn thỉu là cách ưa thích.
Bjorn3

1

Cái này làm việc tốt cho tôi

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

?>

Xin lưu ý kiểm tra này nếu chuỗi đã cho là chuỗi tìm kiếm nối tiếp - nó thực sự sẽ không kiểm tra tính hợp lệ của chuỗi đó.

-2

Tôi thích làm theo cách đó:

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

Tại sao biến nối tiếp nên là một mảng? Nó thực sự có thể là bất cứ loại nào.
Valerio Bozz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.