创建我自己的(非数据库)fetch_object函数

时间:2010-12-12 22:41:39

标签: php oop

在php mysql / mysqli / postgre / etc ...中有fetch_object函数,您可以在其中获取数据行的对象。默认情况下,它将返回stdClass的对象,但您也可以为构造函数定义class_name和params数组。

我想用一组简单的值来做同样的事情。最好在调用构造函数之前设置对象的属性,这与数据库函数显示的行为相同。但是,这似乎不可能。

甚至创建具有属性集的对象的唯一方法似乎是unserialize a preconstructed string。但该示例仍然会创建一个新对象,然后从未序列化的对象设置该对象的属性,以确保调用构造函数。但这意味着在设置属性之前调用构造函数。

简而言之:我想要以下内容:

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

在设置属性后调用构造函数。

3 个答案:

答案 0 :(得分:1)

最后,我写了下面的类,它使用反序列化一个伪造的字符串来执行任务。它使用反射来确定属性的访问类型是什么以及构造函数的名称是什么(如果有的话)。

班级的核心是以下几行:

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

答案 1 :(得分:0)

好吧,您可以将数组转换为对象,如下所示:

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

或只是:

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

这将为您提供一个带有数组属性的stdClass对象。

答案 2 :(得分: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
)

我必须在创建序列化字符串之前检查属性的访问类型,因为classname是为私有属性预先添加的。但是,调用已构造对象的构造函数是否可以继续工作?