我的项目与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
回调的例外情况。所以它甚至没有到达那里。
答案 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;
您正在接收以这种方式序列化的数据,因为在类上调用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
,则*
将在每个属性的前面加上一个,以表明该属性属于类继承结构中的所有类。
所有这一切的原因,是一种简单的方式来展示属性如何存在于类中,以及一个字符串表示来显示这样的东西。