Create my own (non-database) fetch_object function

In php mysql / mysqli / postgre / etc ... there are fetch_object functions where you can get an object for your data row. By default, it returns an object stdClass, but you can also define a class name and an array of parameters for the constructor.

I would like to do the same with a simple set of values. Preferably, setting the properties of an object before calling the constructor is the same behavior as the database functions. However, this is not possible.

The only way to create an object with a set of properties is unserialize preconstructed string . But this example still creates a new object, and then sets the properties of this object from an unserialized object to provide a constructor call. But this means that the constructor is called before the properties are set.

In short: I would like to:

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

with a constructor called after properties are set.

+3
source share
3 answers

At the end, I wrote the following class that performs the task using an unserializing fabricated string. It uses reflection to determine which properties of the access type and which constructor name (if any).

The heart of the class is the following line:

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

, , :

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

:

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

stdClass .

0

Well, I did not expect this to work, but it does:

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);

results:

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

I just need to check the access type of the property before creating the serialized string, because the class name is added to the private properties. However, does it call a constructor of an already constructed object, which is expected to remain working?

0
source

Source: https://habr.com/ru/post/1779891/


All Articles