Создайте мою собственную (не-базу данных) функцию fetch_object

В php mysql / mysqli / postgre / etc … есть функции fetch_object, где вы можете получить объект для своей строки данных. По умолчанию он возвращает объект stdClass , но вы также можете определить имя класса и массив параметров для конструктора.

Я хотел бы сделать то же самое с простым набором значений. Предпочтительно, задавая свойства объекта перед вызовом конструктора, это то же поведение, что и функции базы данных. Однако это не представляется возможным.

Единственный способ создать объект с набором свойств, по-видимому, состоит в том, чтобы неэтериализовать предварительно сконфигурированную строку . Но этот пример по-прежнему создает и новый объект, а затем устанавливает свойства этого объекта из несериализованного объекта, чтобы обеспечить вызов конструктора. Но это означает, что конструктор вызывается до того, как будут установлены свойства.

Короче говоря, я бы хотел:

array_fetch_object(array $properties, string $class_name [, array $params ])

с конструктором, вызванным после того, как свойства установлены.

В конце я написал следующий класс, который выполняет задачу, используя unserializing сфабрикованную строку. Он использует отражение, чтобы определить, что такое тип свойства доступа и какое имя конструктора (если оно есть).

Сердцем класса является следующая строка:

 $object = unserialize('O:'.strlen($class_name).':"'.$class_name.'"'.substr(serialize($properties), 1)); 

который сериализует массив свойств и переименовывает сериализованно в требуемое имя класса.

 class ObjectFactory { private $properties; private $constructors; public function __construct() { $this->properties = array(); $this->constructors = array(); } private function setClass($class_name) { $class = new ReflectionClass($class_name); $this->properties[$class_name] = array(); foreach($class->getProperties() as $property) { $name = $property->getName(); $modifier = $property->getModifiers(); if($modifier & ReflectionProperty::IS_STATIC) { continue; } else if($modifier & ReflectionProperty::IS_PUBLIC) { $this->properties[$class_name][$name] = $name; } else if($modifier & ReflectionProperty::IS_PROTECTED) { $this->properties[$class_name][$name] = "\0*\0".$name; // prefix * with \0's unserializes to protected property } else if($modifier & ReflectionProperty::IS_PRIVATE) { $this->properties[$class_name][$name] = "\0".$class_name."\0".$name; // prefix class_name with \0's unserializes to private property } } if($constructor = $class->getConstructor()) { $this->constructors[$class_name] = $constructor->getName(); } } private function hasClassSet($class_name) { return array_key_exists($class_name, $this->properties); } private function hasClassProperty($class_name, $property_name) { if(!$this->hasClassSet($class_name)) $this->setClass($class_name); return array_key_exists($property_name, $this->properties[$class_name]); } private function getClassProperty($class_name, $property_name) { if(!$this->hasClassProperty($class_name, $property_name)) return false; return $this->properties[$class_name][$property_name]; } private function hasClassConstructor($class_name) { if(!$this->hasClassSet($class_name)) $this->setClass($class_name); return $this->constructors[$class_name] !== false; } private function getClassConstructor($class_name) { if(!$this->hasClassConstructor($class_name)) return false; return $this->constructors[$class_name]; } public function fetch_object(array $assoc, $class_name = 'stdClass', array $params = array()) { $properties = array(); foreach($assoc as $key => $value) { if($property = $this->getClassProperty($class_name, $key)) { $properties[$property] = $value; } } $object = unserialize('O:'.strlen($class_name).':"'.$class_name.'"'.substr(serialize($properties), 1)); if($constructor = $this->getClassConstructor($class_name)) { call_user_func_array(array($object, $constructor), $params); } return $object; } } 

Ну, вы можете выделить массив для объекта, например:

 $array = array('a' => 'a', 'b' => 'c'); $object = (object) $array; 

или просто:

 $object = (object) array('a' => 'a', 'b' => 'c'); 

Это даст вам объект stdClass со свойствами массива.

Хорошо, я не ожидал, что это сработает, но это делает:

 class TestObject { public $property; public $preset; public function __construct($param) { $this->property = $param; } } $object = unserialize('O:10:"TestObject":1:{s:6:"preset";i:1;}'); $object->__construct(1); print_r($object); 

Результаты:

 TestObject Object ( [property] => 1 [preset] => 1 ) 

Мне просто нужно проверить тип доступа свойства, прежде чем создавать сериализованную строку, потому что имя класса добавляется к закрытым свойствам. Однако вызывает конструктор уже построенного объекта, который, как ожидается, останется работать?