PHP序列化对象解释

时间:2015-11-11 19:52:48

标签: php serialization

我的项目与API通信,该API返回带有PHP序列化对象的字符串。通常,响应是序列化阵列&没有问题将其反序列化。但是,我们刚刚遇到一个端点,它返回一个我们之前没有遇到的对象,我不知道如何创建一个可用于反序列化对象的类。

这是从API返回的内容(仅显示令人困惑的部分):

s:8:" * _data";a:1:{
    i:4057353;C:25:"Transfer_Sales_Order_Item":201:{
        a:5:{
            s:8:"quantity";i:1;
            s:19:"id_sales_order_item";s:7:"4057353";
            s:4:"name";s:44:"Empacadora Selladora FoodSaver al Alto Vacio";
            s:10:"paid_price";d:796.13999999999999;
            s:3:"sku";s:15:"LI200HL37WWCLMX";
        }
    }
}

完整序列化字符串:

O:16:"Service_Response":8:{s:11:" * _success";b:1;s:17:" * _errorMessages";a:0:{}s:19:" * _successMessages";a:0:{}s:22:" * _validationMessages";a:0:{}s:18:" * _noticeMessages";a:0:{}s:14:" * redirectUrl";N;s:23:" Service_Abstract _type";N;s:14:" * _resultData";a:1:{s:24:"NotAvailablesForTracking";O:40:"Transfer_Sales_Order_ItemCollection_test":6:{s:19:" * _objectClassName";s:25:"Transfer_Sales_Order_Item";s:15:" * _idAttribute";s:19:"id_sales_order_item";s:47:" Vendor_Transfer_AbstractCollection _indexDirty";b:0;s:8:" * _data";a:1:{i:4057353;C:25:"Transfer_Sales_Order_Item":201:{a:5:{s:8:"quantity";i:1;s:19:"id_sales_order_item";s:7:"4057353";s:4:"name";s:44:"Empacadora Selladora FoodSaver al Alto Vacio";s:10:"paid_price";d:796.13999999999999;s:3:"sku";s:15:"LI200HL37WWCLMX";}}}s:17:" * _setDataStrict";b:1;s:9:" * locale";N;}}}

好像它是一个只有数组的对象/类(?)数组?我试图寻找C符号的含义,但没有成功。知道Transfer_Sales_Order_Item的结构应该是什么样的?

我的猜测是因为原始类实现了ArrayAccess,但我尝试了这个&非序列化仍然抱怨它没有一个目标来反序列化。

目前我们通过在课堂上实施Serializable解决了这个问题。 unserialize()方法,它将类的内容作为序列化字符串获取。然后,这通常会反序列化为数组&我们从那里开始。

这是我们用于反序列化字符串的代码:

class Transformer
{
    public static function debugUnserialize($className)
    {
        throw new \RuntimeException(sprintf('Cannot unserialize object %s', $className, 500);
    }

    public function transform($value)
    {
        $previousCallback = ini_get('unserialize_callback_func');
        ini_set('unserialize_callback_func', self::class . '::debugUnserialzie');
        $data = unserialize($value);
        ini_set('unserialize_callback_func', $previousCallback);
        return $data;
    }
}

添加空类Transfer_Sales_Order_Item后,PHP抛出

Warning: Class Transfer_Sales_Order_Item has no unserializer

请注意,这不是unserialize_callback_func回调的例外情况。所以它甚至没有到达那里。

1 个答案:

答案 0 :(得分:0)

在脚本中的某处添加它,它应该可以解决您的问题

<?php
function unserialize_callback_func($fqcn){
    $explode = explode("\\", $fqcn);
    $namespace = implode("\\", array_slice($explode, 0, count($explode)-1));
    $className = end($explode);
    $dev = "
        namespace $namespace;
        class $className{

        }
    ";
    eval($dev);
};
ini_set('unserialize_callback_func', 'unserialize_callback_func');

一旦注册了ini_set,每次unserialize_callback_func()遇到不存在的对象时,它都会调用unserialize。但是,需要在调用unserialize()

之前注册

这个回调函数将创建一个名为你想要的类,允许反序列化将每个属性设置为可公开访问的属性。

如果您的服务器上不允许eval(),您可以在函数

中定义所需的每个类

编辑:

既然你给了我你的课程,以及完整的序列化字符串,我已经修好了你的课程。这是我用来测试它的脚本:

<?php

$data = 'O:16:"Service_Response":8:{s:11:" * _success";b:1;s:17:" * _errorMessages";a:0:{}s:19:" * _successMessages";a:0:{}s:22:" * _validationMessages";a:0:{}s:18:" * _noticeMessages";a:0:{}s:14:" * redirectUrl";N;s:23:" Service_Abstract _type";N;s:14:" * _resultData";a:1:{s:24:"NotAvailablesForTracking";O:40:"Transfer_Sales_Order_ItemCollection_test":6:{s:19:" * _objectClassName";s:25:"Transfer_Sales_Order_Item";s:15:" * _idAttribute";s:19:"id_sales_order_item";s:47:" Vendor_Transfer_AbstractCollection _indexDirty";b:0;s:8:" * _data";a:1:{i:4057353;C:25:"Transfer_Sales_Order_Item":201:{a:5:{s:8:"quantity";i:1;s:19:"id_sales_order_item";s:7:"4057353";s:4:"name";s:44:"Empacadora Selladora FoodSaver al Alto Vacio";s:10:"paid_price";d:796.13999999999999;s:3:"sku";s:15:"LI200HL37WWCLMX";}}}s:17:" * _setDataStrict";b:1;s:9:" * locale";N;}}}';

class Transformer
{
    public static function debugUnserialize($className)
    {
        $explode = explode("\\", $className);
        $namespace = implode("\\", array_slice($explode, 0, count($explode)-1));
        $className = end($explode);
        $dev = "
            ".(!empty($namespace)?"namespace $namespace;":"")."
            class $className implements Serializable {
                public function unserialize(".'$data'.") {
                    ".'$this->data'." = unserialize(".'$data'.");
                }
                public function serialize() {
                    return serialize(".'$this->data'.");
                }
                public function getData(){
                    return ".'$this->data'.";
                }
            }
        ";

        eval($dev);
    }

    public function transform($value)
    {
        $previousCallback = ini_get('unserialize_callback_func');
        ini_set('unserialize_callback_func', self::class . '::debugUnserialize');
        $data = unserialize($value);
        ini_set('unserialize_callback_func', $previousCallback);
        return $data;
    }
}

$trans = new Transformer();
var_dump($trans->transform($data));exit;

编辑2:

您正在接收以这种方式序列化的数据,因为在类上调用serialize()时,如果没有实现序列化差异,它将以这样的方式提取所有属性,使其看起来像一个数组。我使用了您提供的部分信息来创建测试脚本:

<?php
class Transfer_Sales_Order_Item {
    public
        $quantity = 1
    ,   $id_sales_order_item = '4057353'
    ,   $name = 'Empacadora Selladora FoodSaver al Alto Vacio'
    ,   $paid_price = 796.14
    ,   $sku = 'LI200HL37WWCLMX'
    ;
}

echo serialize(new Transfer_Sales_Order_Item);

将输出:

O:25:"Transfer_Sales_Order_Item":5:{s:8:"quantity";i:1;s:19:"id_sales_order_item";s:7:"4057353";s:4:"name";s:44:"Empacadora Selladora FoodSaver al Alto Vacio";s:10:"paid_price";d:796.13999999999999;s:3:"sku";s:15:"LI200HL37WWCLMX";}

然而改为使用这个类:

class Transfer_Sales_Order_Item {
    private
        $quantity = 1
    ,   $id_sales_order_item = '4057353'
    ,   $name = 'Empacadora Selladora FoodSaver al Alto Vacio'
    ,   $paid_price = 796.14
    ,   $sku = 'LI200HL37WWCLMX'
    ;
}

将输出前缀为每个属性的类名,以显示该属性仅属于该类。

O:25:"Transfer_Sales_Order_Item":5:{s:35:"Transfer_Sales_Order_Itemquantity";i:1;s:46:"Transfer_Sales_Order_Itemid_sales_order_item";s:7:"4057353";s:31:"Transfer_Sales_Order_Itemname";s:44:"Empacadora Selladora FoodSaver al Alto Vacio";s:37:"Transfer_Sales_Order_Itempaid_price";d:796.13999999999999;s:30:"Transfer_Sales_Order_Itemsku";s:15:"LI200HL37WWCLMX";}

并且为了保持一致性,如果它被更改为protected,则*将在每个属性的前面加上一个,以表明该属性属于类继承结构中的所有类。

所有这一切的原因,是一种简单的方式来展示属性如何存在于类中,以及一个字符串表示来显示这样的东西。