Преобразование / приведение объекта stdClass в другой класс
Я использую стороннюю систему хранения, которая возвращает мне только объекты stdClass независимо от того, что я ввожу по какой-то неясной причине. Поэтому мне интересно узнать, есть ли способ привести/преобразовать объект stdClass в полноценный объект данного типа.
например, что-то вроде:
//$stdClass is an stdClass instance
$converted = (BusinessClass) $stdClass;
Я просто бросаю stdClass в массив и передаю его конструктору BusinessClass, но, возможно, есть способ восстановить начальный класс, который я не осознаю.
Примечание: меня не интересует тип ответов "изменить систему хранения", так как это не представляет интереса. Пожалуйста, рассматривайте это скорее как академический вопрос о языковых возможностях.
Ура
7 ответов
посмотреть руководство по типу жонглирования о возможном приведении.
допускаются слепки:
- (int), (integer) - приведение к integer
- (типа bool), (boolean) - приведение к boolean
- (float), (double), (real) - cast to float
- (строка) - cast to string
- (array) - приведение к массиву
- (object) - приведение к объекту
- (unset) - приведение к нулю (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;
}
}
}
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;
}
}
}
}