Skip to content

[RFC]: Process parent properties in the ReflectionHydrator. #114

@ianef

Description

@ianef

RFC

Q A
Proposed Version(s) next minor
BC Break? No

Provide a means to hydrate private properties of parent classes.

Background

Currently the hydrator gets the class properties using ReflectionClass. This is fine if the class does not inherit from other classes, or if all the properties are public. However if the properties are private then only the instantiated class properties are enumerated. For Doctrine entities that are associations, and other cases using inherited getters and private properties, Doctrine returns a proxied class and not the original entity. When the hydrator enumerates the properties of the class it just gets the properties of the proxy and not the base entity.

Considerations

There will be no impact on current use of the hydrator as fetching parent properties is optional.

Proposal(s)

    /**
     * Extract values from an object
     *
     * {@inheritDoc}
     */
    public function extract(object $object, bool $includeParentProperties = false): array
    {
        $result = [];
        foreach (static::getReflProperties($object, $includeParentProperties) as $property) {
            $propertyName = $this->extractName($property->getName(), $object);
            if (! $this->getCompositeFilter()->filter($propertyName)) {
                continue;
            }

            $value                 = $property->getValue($object);
            $result[$propertyName] = $this->extractValue($propertyName, $value, $object);
        }

        return $result;
    }

    /**
     * Hydrate $object with the provided $data.
     *
     * {@inheritDoc}
     */
    public function hydrate(array $data, object $object, bool $includeParentProperties = false)
    {
        $reflProperties = static::getReflProperties($object, $includeParentProperties);
        foreach ($data as $key => $value) {
            $name = $this->hydrateName($key, $data);
            if (isset($reflProperties[$name])) {
                $reflProperties[$name]->setValue($object, $this->hydrateValue($name, $value, $data));
            }
        }
        return $object;
    }

    /**
     * Get a reflection properties for an object.
     * If $includeParentProperties is true, return return all parent properties as well.
     *
     * @return ReflectionProperty[]
     */
    protected static function getReflProperties(object $input, bool $includeParentProperties): array
    {
        $class = get_class($input);

        if (isset(static::$reflProperties[$class])) {
            return static::$reflProperties[$class];
        }

        static::$reflProperties[$class] = [];
        $reflClass = new ReflectionClass($class);

        do {
            foreach ($reflClass->getProperties() as $property) {
                $property->setAccessible(true);
                static::$reflProperties[$class][$property->getName()] = $property;
            }
        } while ($includeParentProperties === true && ($reflClass = $reflClass->getParentClass()) !== false);

        return static::$reflProperties[$class];
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions