web-dev-qa-db-ja.com

PHP unserializeはエンコードされていない文字で失敗しますか?

$ser = 'a:2:{i:0;s:5:"héllö";i:1;s:5:"wörld";}'; // fails
$ser2 = 'a:2:{i:0;s:5:"hello";i:1;s:5:"world";}'; // works
$out = unserialize($ser);
$out2 = unserialize($ser2);
print_r($out);
print_r($out2);
echo "<hr>";

しかし、なぜ?
シリアル化する前にエンコードするべきですか?どうやって?

PHPの$ _POSTではなく、Javascriptを使用してシリアル化された文字列を非表示フィールドに書き込みます
JSには次のようなものがあります。

function writeImgData() {
    var caption_arr = new Array();
    $('.album img').each(function(index) {
         caption_arr.Push($(this).attr('alt'));
    });
    $("#hidden-field").attr("value", serializeArray(caption_arr));
};
29
FFish

unserialize()が次の理由で失敗する理由:

_$ser = 'a:2:{i:0;s:5:"héllö";i:1;s:5:"wörld";}';
_

PHPはマルチバイト文字列をネイティブで正しく処理しないため、_héllö_および_wörld_の長さが間違っているためです。

_echo strlen('héllö'); // 7
echo strlen('wörld'); // 6
_

ただし、次の正しい文字列をunserialize()しようとすると、

_$ser = 'a:2:{i:0;s:7:"héllö";i:1;s:6:"wörld";}';

echo '<pre>';
print_r(unserialize($ser));
echo '</pre>';
_

できます:

_Array
(
    [0] => héllö
    [1] => wörld
)
_

PHP serialize()を使用すると、マルチバイト文字列インデックスの長さが正しく計算されます。

一方、複数の(プログラミング)言語でシリアル化されたデータを操作する場合は、それを忘れて、JSONなどの標準化された方法に移行する必要があります。

50
Alix Axel

私はこれが1年前のように投稿されたことを知っていますが、私はこの問題を抱えていてこれに遭遇し、実際に解決策を見つけました。このコードは魅力のように機能します!

背後にある考えは簡単です。これは、上記の@Alixによって投稿されたマルチバイト文字列の長さを再計算するだけで役立ちます。

いくつかの変更はあなたのコードに合うはずです:

/**
 * Mulit-byte Unserialize
 *
 * UTF-8 will screw up a serialized string
 *
 * @access private
 * @param string
 * @return string
 */
function mb_unserialize($string) {
    $string = preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $string);
    return unserialize($string);
}

ソース: http://snippets.dzone.com/posts/show/6592

私のマシンでテストしました、そしてそれは魅力のように動作します!!

51
Lionel Chan

Lionel Chan PHP> = 5.5で動作するように変更された回答:

function mb_unserialize($string) {
    $string2 = preg_replace_callback(
        '!s:(\d+):"(.*?)";!s',
        function($m){
            $len = strlen($m[2]);
            $result = "s:$len:\"{$m[2]}\";";
            return $result;

        },
        $string);
    return unserialize($string2);
}    

このコードでは、preg_replace_callbackpreg_replaceとして使用し、/ e修飾子は廃止されました =PHP 5.5。

25
David

問題は-であり、Alixによって指摘されている-エンコーディングに関連しています。

PHP 5.4の内部エンコーディングPHPはISO-8859-1でした。 UTF-8システムでシリアル化されたマルチバイト値は、ISO-8859-1システムでは読み取ることができないということです。

このような問題を回避するには、すべてのシステムが同じエンコーディングを使用するようにしてください:

_mb_internal_encoding('utf-8');
$arr = array('foo' => 'bár');
$buf = serialize($arr);
_

utf8_(encode|decode)を使用してクリーンアップできます:

_// Set system encoding to iso-8859-1
mb_internal_encoding('iso-8859-1');
$arr = unserialize(utf8_encode($serialized));
print_r($arr);
_
8
lafka

上記の@Lionelへの返信として、実際には、シリアル化された文字列自体に文字シーケンス";(セミコロンが続く引用符)が含まれている場合、提案した関数mb_unserialize()は機能しません。注意して使用してください。例えば:

$test = 'test";string'; 
// $test is now 's:12:"test";string";'
$string = preg_replace('!s:(\d+):"(.*?)";!se', "'s:'.strlen('$2').':\"$2\";'", $test);
print $string; 
// output: s:4:"test";string";  (Wrong!!)

他の人が言っているように、JSONは進むべき道です

注:直接返信する方法がわからないため、これを新しい回答として投稿します(ここで新規作成)。

2
Joe Hong

Do not use PHP serialization/unserialization when the other end is PHPではない。これは移植可能な形式ではなく、たとえばascii-1も含まれる保護されたキーの文字は、JavaScriptで処理したくないものです(完全に問題なく機能しますが、非常に醜いです)。

代わりに、[〜#〜] json [〜#〜]のような移植可能な形式を使用してください。 XMLも機能しますが、JSONはオーバーヘッドが少なく、XPathやDOMツリーなどを処理する必要がなく、単純なデータ構造に簡単に解析できるため、よりプログラマーフレンドリーです。

2
ThiefMaster

ここにもう1つ、わずかなバリエーションがあります。これは誰かに役立つと思います...配列をシリアル化してからデータベースに書き込みました。データの取得時に、シリアル化解除操作が失敗しました。

私が書いていたデータベースのロングテキストフィールドはUTF8ではなくlatin1を使用していることがわかりました。私がそれを切り替えたとき、すべてが計画通りに機能しました。

文字エンコーディングに言及し、私を正しい軌道に乗せてくれた上記のすべてに感謝します!

1
Mike

シリアライズ:

foreach ($income_data as $key => &$value)
{
    $value = urlencode($value);
}
$data_str = serialize($income_data);

シリアル化解除:

$data = unserialize($data_str);
foreach ($data as $key => &$value)
{
    $value = urldecode($value);
}
0
sNICkerssss

文字列を配列に分解できます:

$finalArray = array();
$nodeArr = explode('&', $_POST['formData']);

foreach($nodeArr as $value){
    $childArr = explode('=', $value);
    $finalArray[$childArr[0]] = $childArr[1];
}
0
Rondip

私の場合、問題は行末でした(おそらく、一部のエディターが私のファイルをDOSからUnixに変更しました)。

私はこれらの不快なラッパーをまとめました:

function unserialize_fetchError($original, &$unserialized, &$errorMsg) {
    $unserialized = @unserialize($original);
    $errorMsg = error_get_last()['message'];
    return ( $unserialized !== false || $original == 'b:0;' );  // "$original == serialize(false)" is a good serialization even if deserialization actually returns false
}

function unserialize_checkAllLineEndings($original, &$unserialized, &$errorMsg, &$lineEndings) {
    if ( unserialize_fetchError($original, $unserialized, $errorMsg) ) {
        $lineEndings = 'unchanged';
        return true;
    } elseif ( unserialize_fetchError(str_replace("\n", "\n\r", $original), $unserialized, $errorMsg) ) {
        $lineEndings = '\n to \n\r';
        return true;
    } elseif ( unserialize_fetchError(str_replace("\n\r", "\n", $original), $unserialized, $errorMsg) ) {
        $lineEndings = '\n\r to \n';
        return true;
    } elseif ( unserialize_fetchError(str_replace("\r\n", "\n", $original), $unserialized, $errorMsg) ) {
        $lineEndings = '\r\n to \n';
        return true;
    } //else
    return false;
}
0

JavaScriptを使用してjsonとしてエンコードし、次に json_decode を使用してシリアル化を解除することをお勧めします。

0
Artefacto