Я использую стороннюю систему хранения, которая по какой-то непонятной причине возвращает мне только объекты stdClass, независимо от того, что я использую. Поэтому мне любопытно узнать, есть ли способ преобразовать объект stdClass в полноценный объект данного типа.
Например, что-то вроде:
//$stdClass is an stdClass instance
$converted = (BusinessClass) $stdClass;
Я просто преобразую stdClass в массив и передаю его конструктору BusinessClass, но, возможно, есть способ восстановить исходный класс, о котором я не знаю.
Примечание. Меня не интересуют ответы типа «Измените систему хранения», поскольку это не главное. Пожалуйста, считайте это скорее академическим вопросом о языковых способностях.
Ура
PDOStatement::fetchObject
выполнить эту задачу?Ответы:
О возможных забросах см. Руководство по жонглированию шрифтами.
Разрешенные броски:
Вам нужно будет написать Mapper, который выполняет приведение из 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_Incomplete_Class
Используя этот метод, вы получаете объект (по крайней мере, начиная с PHP 5.6).fooBar
s (частный отexample\Foo
и публичный от stdClass). Вместо этого он заменяет значение.Вы можете использовать указанную выше функцию для приведения объектов не похожих классов (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);
источник
@Given the user :username has the id :id
свою функцию и обработать ее с помощью отражений в классе контекста$destination = new $destination();
можно заменить,$destination = ( new ReflectionClass( $destination ) )->newInstanceWithoutConstructor();
если вам нужно избежать вызова конструктора.Чтобы переместить все существующие свойства 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; } } }
источник
И еще один подход с использованием шаблона декоратора и магических методов получения и установки PHP:
// 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);
источник
рассмотрите возможность добавления нового метода в 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; }
тогда вы можете создать новый BusinessClass из $ stdClass:
источник
Кстати: преобразование очень важно, если вы сериализованы, главным образом потому, что десериализация разбивает тип объектов и превращается в 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; } } } }
источник
Еще один подход.
Следующее теперь возможно благодаря последней версии 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.
источник
Преобразуйте его в массив, верните первый элемент этого массива и установите параметр возврата для этого класса. Теперь вы должны получить автозаполнение для этого класса, поскольку оно будет преобразовывать его как этот класс вместо 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]; }
источник