题
我正在使用第三方存储系统,无论我出于某种不明原因输入什么,它都只返回 stdClass 对象。所以我很好奇是否有一种方法可以将 stdClass 对象转换/转换为给定类型的完整对象。
例如:
//$stdClass is an stdClass instance
$converted = (BusinessClass) $stdClass;
我只是将 stdClass 转换为数组并将其提供给 BusinessClass 构造函数,但也许有一种方法可以恢复我不知道的初始类。
笔记:我对“更改存储系统”类型的答案不感兴趣,因为这不是兴趣点。请更多地将其视为关于语言能力的学术问题。
干杯
解决方案
请参阅手册在可能的演员中杂耍juggling 。
允许的铸件是:
- (int),(整数) - 转换为整数
- (bool),(boolean) - 铸造到布尔什
- (浮动),(双),(真实) - 浇注到浮子
- (string) - 演义为字符串
- (阵列) - 施加到阵列
- (对象) - 施放到对象
- (未命令) - 铸造为null(php 5)
您必须写一个映射器,这对stdclass铸造到另一个具体的类。不应该太难做到。
或者,如果您处于乱砍的心情,则可以调整以下代码:
function arrayToObject(array $array, $className) {
return unserialize(sprintf(
'O:%d:"%s"%s',
strlen($className),
$className,
strstr(serialize($array), ':')
));
}
.
哪个伪数组到某个类的对象。这首先通过首次序列化数组然后更改序列化数据来实现,以便它代表某个类。结果是未识别到此类的实例。但就像我说的那样,它是喧嚣的,所以期待副作用。
对于对象,代码将是
function objectToObject($instance, $className) {
return unserialize(sprintf(
'O:%d:"%s"%s',
strlen($className),
$className,
strstr(strstr(serialize($instance), '"'), ':')
));
}
. 其他提示
您可以使用上面的函数来铸造不是类似的类对象(PHP>= 5.3)
/**
* Class casting
*
* @param string|object $destination
* @param object $sourceObject
* @return object
*/
function cast($destination, $sourceObject)
{
if (is_string($destination)) {
$destination = new $destination();
}
$sourceReflection = new ReflectionObject($sourceObject);
$destinationReflection = new ReflectionObject($destination);
$sourceProperties = $sourceReflection->getProperties();
foreach ($sourceProperties as $sourceProperty) {
$sourceProperty->setAccessible(true);
$name = $sourceProperty->getName();
$value = $sourceProperty->getValue($sourceObject);
if ($destinationReflection->hasProperty($name)) {
$propDest = $destinationReflection->getProperty($name);
$propDest->setAccessible(true);
$propDest->setValue($destination,$value);
} else {
$destination->$name = $value;
}
}
return $destination;
}
.
示例:
class A
{
private $_x;
}
class B
{
public $_x;
}
$a = new A();
$b = new B();
$x = cast('A',$b);
$x = cast('B',$a);
. 移动 a 的所有现有属性 stdClass
指定类名的新对象:
/**
* recast stdClass object to an object with type
*
* @param string $className
* @param stdClass $object
* @throws InvalidArgumentException
* @return mixed new, typed object
*/
function recast($className, stdClass &$object)
{
if (!class_exists($className))
throw new InvalidArgumentException(sprintf('Inexistant class %s.', $className));
$new = new $className();
foreach($object as $property => &$value)
{
$new->$property = &$value;
unset($object->$property);
}
unset($value);
$object = (unset) $object;
return $new;
}
用法:
$array = array('h','n');
$obj=new stdClass;
$obj->action='auth';
$obj->params= &$array;
$obj->authKey=md5('i');
class RestQuery{
public $action;
public $params=array();
public $authKey='';
}
$restQuery = recast('RestQuery', $obj);
var_dump($restQuery, $obj);
输出:
object(RestQuery)#2 (3) {
["action"]=>
string(4) "auth"
["params"]=>
&array(2) {
[0]=>
string(1) "h"
[1]=>
string(1) "n"
}
["authKey"]=>
string(32) "865c0c0b4ab0e063e5caa3387c1a8741"
}
NULL
这是有限的,因为 new
运算符,因为未知它需要哪些参数。对于你的情况可能很合适。
我有一个非常相似的问题。简化的反射解决方案对我来说很好:
public static function cast($destination, \stdClass $source)
{
$sourceReflection = new \ReflectionObject($source);
$sourceProperties = $sourceReflection->getProperties();
foreach ($sourceProperties as $sourceProperty) {
$name = $sourceProperty->getName();
$destination->{$name} = $source->$name;
}
return $destination;
}
. 希望有人发现这种有用的
// new instance of stdClass Object
$item = (object) array(
'id' => 1,
'value' => 'test object',
);
// cast the stdClass Object to another type by passing
// the value through constructor
$casted = new ModelFoo($item);
// OR..
// cast the stdObject using the method
$casted = new ModelFoo;
$casted->cast($item);
.
class Castable
{
public function __construct($object = null)
{
$this->cast($object);
}
public function cast($object)
{
if (is_array($object) || is_object($object)) {
foreach ($object as $key => $value) {
$this->$key = $value;
}
}
}
}
.
class ModelFoo extends Castable
{
public $id;
public $value;
}
. 改变了深度铸造的功能(使用递归)
/**
* Translates type
* @param $destination Object destination
* @param stdClass $source Source
*/
private static function Cast(&$destination, stdClass $source)
{
$sourceReflection = new \ReflectionObject($source);
$sourceProperties = $sourceReflection->getProperties();
foreach ($sourceProperties as $sourceProperty) {
$name = $sourceProperty->getName();
if (gettype($destination->{$name}) == "object") {
self::Cast($destination->{$name}, $source->$name);
} else {
$destination->{$name} = $source->$name;
}
}
}
. 还有另一种方法,使用装饰器模式和PHPS魔法吸气剂和设置:
// A simple StdClass object
$stdclass = new StdClass();
$stdclass->foo = 'bar';
// Decorator base class to inherit from
class Decorator {
protected $object = NULL;
public function __construct($object)
{
$this->object = $object;
}
public function __get($property_name)
{
return $this->object->$property_name;
}
public function __set($property_name, $value)
{
$this->object->$property_name = $value;
}
}
class MyClass extends Decorator {}
$myclass = new MyClass($stdclass)
// Use the decorated object in any type-hinted function/method
function test(MyClass $object) {
echo $object->foo . '<br>';
$object->foo = 'baz';
echo $object->foo;
}
test($myclass);
. btw:如果序列化,转换非常重要,主要是因为去序列化破坏了对象的类型并变成了STDClass,包括Datetime对象。
我更新了@jadrovski的示例,现在它允许对象和数组。
示例
$stdobj=new StdClass();
$stdobj->field=20;
$obj=new SomeClass();
fixCast($obj,$stdobj);
.
示例阵列
$stdobjArr=array(new StdClass(),new StdClass());
$obj=array();
$obj[0]=new SomeClass(); // at least the first object should indicates the right class.
fixCast($obj,$stdobj);
.
代码:(其递归)。但是,我不知道它是否与阵列递归。可能是它缺少额外的is_array
public static function fixCast(&$destination,$source)
{
if (is_array($source)) {
$getClass=get_class($destination[0]);
$array=array();
foreach($source as $sourceItem) {
$obj = new $getClass();
fixCast($obj,$sourceItem);
$array[]=$obj;
}
$destination=$array;
} else {
$sourceReflection = new \ReflectionObject($source);
$sourceProperties = $sourceReflection->getProperties();
foreach ($sourceProperties as $sourceProperty) {
$name = $sourceProperty->getName();
if (is_object(@$destination->{$name})) {
fixCast($destination->{$name}, $source->$name);
} else {
$destination->{$name} = $source->$name;
}
}
}
}
. 考虑向商业类添加新方法:
public static function fromStdClass(\stdClass $in): BusinessClass
{
$out = new self();
$reflection_object = new \ReflectionObject($in);
$reflection_properties = $reflection_object->getProperties();
foreach ($reflection_properties as $reflection_property)
{
$name = $reflection_property->getName();
if (property_exists('BusinessClass', $name))
{
$out->{$name} = $in->$name;
}
}
return $out;
}
.
然后,您可以从$ stdclass创建一个新的商业类:
$converted = BusinessClass::fromStdClass($stdClass);
. 另一种方法。
据最近的PHP 7版本,现在可以进行以下情况。
$theStdClass = (object) [
'a' => 'Alpha',
'b' => 'Bravo',
'c' => 'Charlie',
'd' => 'Delta',
];
$foo = new class($theStdClass) {
public function __construct($data) {
if (!is_array($data)) {
$data = (array) $data;
}
foreach ($data as $prop => $value) {
$this->{$prop} = $value;
}
}
public function word4Letter($letter) {
return $this->{$letter};
}
};
print $foo->word4Letter('a') . PHP_EOL; // Alpha
print $foo->word4Letter('b') . PHP_EOL; // Bravo
print $foo->word4Letter('c') . PHP_EOL; // Charlie
print $foo->word4Letter('d') . PHP_EOL; // Delta
print $foo->word4Letter('e') . PHP_EOL; // PHP Notice: Undefined property
.
在此示例中,$ foo被初始化为匿名类,该类只需要一个数组或stdclass作为构造函数的参数。
最终,我们循环通过传递对象中包含的每个项目,并动态分配给对象属性。
要使此批准事件更通用,您可以编写一个接口或其特征,以便在您希望施放STDClass的任何类中实现。将其转换为数组,返回该数组的第一个元素,并将Return Param设置为该类。现在,您应该为该类获取自动填充,因为它将将其缩短为该类而不是stdclass。
/**
* @return Order
*/
public function test(){
$db = new Database();
$order = array();
$result = $db->getConnection()->query("select * from `order` where productId in (select id from product where name = 'RTX 2070')");
$data = $result->fetch_object("Order"); //returns stdClass
array_push($order, $data);
$db->close();
return $order[0];
}
.