Преобразование / приведение объекта 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
- (string) - приведение к строке
- (array) - приведение к массиву
- (объект) - приведение к объекту
- (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; } } } }
Comments