2010-12-12 2 views
3

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

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

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

Короче: Я хотел бы следующее:

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

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

+0

Есть ли причина, по которой вы не можете просто использовать 'new'? '$ x = new $ class ($ argument)' – mattbasta

+0

Это не будет иметь свойств, заданных до вызова конструктора, и частные свойства не могут быть установлены. – ontrack

ответ

1

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

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

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

, который упорядочивает свойство-массив и переименовывает сериализованные к желаемому class_name.

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

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

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

или просто:

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

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

+0

Это приведет к объекту stdClass, который не имеет каких-либо методов, таких как объект, получаемый с передачей $ class_name. Мне нужен объект определенного класса. – ontrack

+0

Ahhh, хороший момент. Теперь я заинтригован. –

+0

Хорошо, мой другой ответ использовал всевозможные вещи, которые вы даже не можете сделать в PHP. Извини за это. Мне будет очень интересно посмотреть, что вы придумали. –

0

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

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 
) 

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

Смежные вопросы