Programing

stdClass 객체를 다른 클래스로 변환 / 캐스트

lottogame 2020. 9. 18. 19:13
반응형

stdClass 객체를 다른 클래스로 변환 / 캐스트


모호한 이유로 무엇을 공급하든 stdClass 객체 만 반환하는 타사 스토리지 시스템을 사용하고 있습니다. 그래서 stdClass 객체를 주어진 유형의 완전한 객체로 캐스트 / 변환하는 방법이 있는지 알고 싶습니다.

예를 들어 :

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

stdClass를 배열로 캐스팅하고 BusinessClass 생성자에 공급하는 중이지만, 내가 알지 못하는 초기 클래스를 복원하는 방법이있을 수 있습니다.

참고 : '스토리지 시스템 변경'유형의 답변은 관심 지점이 아니므로 관심이 없습니다. 언어 능력에 대한 학문적 질문이라고 생각하십시오.

건배


가능한 캐스트 에 대해서는 Type Juggling대한 설명서를 참조하십시오 .

허용되는 캐스트는 다음과 같습니다.

  • (int), (integer)-정수로 캐스트
  • (bool), (boolean)-부울로 캐스트
  • (float), (double), (real)-부동으로 캐스팅
  • (문자열)-문자열로 캐스트
  • (배열)-배열로 캐스트
  • (객체)-객체로 캐스트
  • (설정되지 않음)-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);

의 모든 기존 속성을 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;
        }
    }
}

BTW : 직렬화 된 경우 변환은 매우 중요합니다. 주로 역 직렬화가 개체 유형을 깨고 DateTime 개체를 포함한 stdclass로 변환되기 때문입니다.

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

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:

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

Yet another approach.

The following is now possible thanks to the recent PHP 7 version.

$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

In this example, $foo is being initialized as an anonymous class that takes one array or stdClass as only parameter for the constructor.

Eventually, we loop through the each items contained in the passed object and dynamically assign then to an object's property.

To make this approch event more generic, you can write an interface or a Trait that you will implement in any class where you want to be able to cast an stdClass.


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

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

참고URL : https://stackoverflow.com/questions/3243900/convert-cast-an-stdclass-object-to-another-class

반응형