php 将 stdClass 对象转换/强制转换为另一个类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3243900/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 09:07:01  来源:igfitidea点击:

Convert/cast an stdClass object to another class

phpstdclass

提问by The Mighty Rubber Duck

I'm using a third party storage system that only returns me stdClass objects no matter what I feed in for some obscure reason. So I'm curious to know if there is a way to cast/convert an stdClass object into a full fledged object of a given type.

我正在使用第三方存储系统,无论我出于某种晦涩的原因输入什么,它都只会返回我的 stdClass 对象。所以我很想知道是否有办法将 stdClass 对象转换/转换为给定类型的完整对象。

For instance something along the lines of:

例如:

//$stdClass is an stdClass instance
$converted = (BusinessClass) $stdClass;

I am just casting the stdClass into an array and feed it to the BusinessClass constructor, but maybe there is a way to restore the initial class that I am not aware of.

我只是将 stdClass 转换为一个数组并将其提供给 BusinessClass 构造函数,但也许有一种方法可以恢复我不知道的初始类。

Note: I am not interested in 'Change your storage system' type of answers since it is not the point of interest. Please consider it more an academic question on the language capacities.

注意:我对“更改您的存储系统”类型的答案不感兴趣,因为这不是兴趣点。请更多地将其视为关于语言能力的学术问题。

Cheers

干杯

采纳答案by Gordon

See the manual on Type Jugglingon possible casts.

请参阅有关可能的类型转换的Type Juggling 手册

The casts allowed are:

允许的演员表是:

  • (int), (integer) - cast to integer
  • (bool), (boolean) - cast to boolean
  • (float), (double), (real) - cast to float
  • (string) - cast to string
  • (array) - cast to array
  • (object) - cast to object
  • (unset) - cast to NULL (PHP 5)
  • (int), (integer) - 转换为整数
  • (bool), (boolean) - 转换为布尔值
  • (float), (double), (real) - 转换为浮动
  • (string) - 转换为字符串
  • (array) - 转换为数组
  • (object) - 转换为对象
  • (未设置) - 强制转换为 NULL (PHP 5)

You would have to write a Mapperthat does the casting from stdClass to another concrete class. Shouldn't be too hard to do.

您必须编写一个 Mapper来执行从 stdClass 到另一个具体类的转换。应该不会太难做。

Or, if you are in a hackish mood, you could adapt the following code:

或者,如果您心情不好,可以修改以下代码:

function arrayToObject(array $array, $className) {
    return unserialize(sprintf(
        'O:%d:"%s"%s',
        strlen($className),
        $className,
        strstr(serialize($array), ':')
    ));
}

which pseudocasts an array to an object of a certain class. This works by first serializing the array and then changing the serialized data so that it represents a certain class. The result is unserialized to an instance of this class then. But like I said, it's hackish, so expect side-effects.

它将数组伪转换为某个类的对象。这是通过首先序列化数组然后更改序列化数据使其代表某个类来工作的。然后将结果反序列化为此类的实例。但就像我说的那样,它是骇人听闻的,所以期待副作用。

For object to object, the code would be

对于对象到对象,代码将是

function objectToObject($instance, $className) {
    return unserialize(sprintf(
        'O:%d:"%s"%s',
        strlen($className),
        $className,
        strstr(strstr(serialize($instance), '"'), ':')
    ));
}

回答by Adam Puza

You can use above function for casting not similar class objects (PHP >= 5.3)

您可以使用上面的函数来转换不相似的类对象(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;
}

EXAMPLE:

例子:

class A 
{
  private $_x;   
}

class B 
{
  public $_x;   
}

$a = new A();
$b = new B();

$x = cast('A',$b);
$x = cast('B',$a);

回答by hakre

To move all existing properties of a stdClassto a new object of a specified class name:

将 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;
}

Usage:

用法:

$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);

Output:

输出:

object(RestQuery)#2 (3) {
  ["action"]=>
  string(4) "auth"
  ["params"]=>
  &array(2) {
    [0]=>
    string(1) "h"
    [1]=>
    string(1) "n"
  }
  ["authKey"]=>
  string(32) "865c0c0b4ab0e063e5caa3387c1a8741"
}
NULL

This is limited because of the newoperator as it is unknown which parameters it would need. For your case probably fitting.

由于new运营商的原因,这是有限的,因为它需要哪些参数是未知的。对于你的情况可能很合适。

回答by Sergei G

I have a very similar problem. Simplified reflection solution worked just fine for me:

我有一个非常相似的问题。简化的反射解决方案对我来说很好用:

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

回答by Wizzard

Hope that somebody find this useful

希望有人觉得这有用

// 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;
}

回答by Jadrovski

Changed function for deep casting (using recursion)

更改了深度铸造的功能(使用递归)

/**
 * 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;
        }
    }
}

回答by Benni

And yet another approach using the decorator pattern and PHPs magic getter & setters:

还有另一种使用装饰器模式和 PHP 魔法 getter 和 setter 的方法:

// 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);

回答by CLUTCHER

Convert it to an array, return the first element of that array, and set the return param to that class. Now you should get the autocomplete for that class as it will regconize it as that class instead of stdclass.

将其转换为数组,返回该数组的第一个元素,并将返回参数设置为该类。现在您应该获得该类的自动完成功能,因为它会将其重新定义为该类而不是 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];
}

回答by magallanes

BTW: Converting is highly important if you are serialized, mainly because the de-serialization breaks the type of objects and turns into stdclass, including DateTime objects.

BTW:如果你被序列化,转换是非常重要的,主要是因为反序列化破坏了对象的类型并变成了 stdclass,包括 DateTime 对象。

I updated the example of @Jadrovski, now it allows objects and arrays.

我更新了@Jadrovski 的例子,现在它允许对象和数组。

example

例子

$stdobj=new StdClass();
$stdobj->field=20;
$obj=new SomeClass();
fixCast($obj,$stdobj);

example array

示例数组

$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);

code: (its recursive). However, i don't know if its recursive with arrays. May be its missing an extra is_array

代码:(它的递归)。但是,我不知道它是否与数组递归。可能是缺少一个额外的 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;
            }
        }
    }
}

回答by pgee70

consider adding a new method to BusinessClass:

考虑向 BusinessClass 添加一个新方法:

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

then you can make a new BusinessClass from $stdClass:

那么你可以从 $stdClass 创建一个新的 BusinessClass:

$converted = BusinessClass::fromStdClass($stdClass);