1

I wrote a dependency provider (basically IOC container) for my application. I added the feature of autowiring, although it hits a snag when the class it is trying to autowire doesn't have a constructor.

Uncaught Error: Call to a member function getParameters() on null in C:\xampp\htdocs\src\app\Providers\DependencyProvider.php:68

I am fairly certain it is trying to resolve constructor arguments in the resolveArguments method, on a class that doesn't have a constructor, and this is why the issue is happening.

So, resolveArguments should only be called if the class needs their arguments resolved (autowired), and that is usually only when it has a constructor.

The error message above, is happening because getConstructor is returning null. I am asking what is the best practice to check if a reflection class has a constructor that needs autowiring?

Full class:

<?php

namespace App\Providers;

class DependencyProvider {
    private static $objects = [];

    /**
     * Register an instantiated object to the container.
     *
     * @param object $object
     */
    public static function register(object $object) : void {
        self::$objects[get_class($object)] = $object;
    }

    /**
     * Fetch a cached object from the container.
     *
     * @param string $objectName
     * @return object
     */
    public static function fetch(string $objectName) : object {
        if (array_key_exists($objectName, self::$objects)) {
            return self::$objects[$objectName];
        }

        $object = self::make($objectName);

        self::$objects[$objectName] = $object;

        return $object;
    }

    /**
     * Creates an object from its name and auto-wires constructor arguments.
     *
     * @param string $objectName
     * @return object
     * @throws \ReflectionException
     */
    private static function make(string $objectName) : object {
        $reflection = new \ReflectionClass($objectName);

        if (!$reflection->isInstantiable()) {
            throw new RuntimeException($reflection->getName() . ' can\'t be instantiated.');
        }

        $arguments = self::resolveArguments($reflection);

        if (count($arguments) < 1) {
            return $reflection->newInstance();
        }
        else {
            return $reflection->newInstanceArgs($arguments);
        }
    }

    /**
     * Creates an array of arguments from a reflection class.
     * Uses default value if there is one, auto-wires the object if not.
     *
     * @param $reflection
     * @return array
     */
    private static function resolveArguments($reflection) : array {
        $constructor = $reflection->getConstructor();
        $parameters = $constructor->getParameters();

        if (!$parameters) {
            return $reflection->newInstance();
        }

        $arguments = [];

        foreach ($parameters as $parameter) {
            if ($parameter->isDefaultValueAvailable()) {
                $arguments[] = $parameter->getDefaultValue();
                continue;
            }

            if ($parameter->getClass() == null) {
                exit($parameter->name . ' on ' . $reflection->getName() . ' needs a default value');
            }

            $arguments[] = self::fetch($parameter->getClass()->getName());
        }

        return $arguments;
    }
}
1
  • I don't think there is a better way than $reflection->getConstructor() !== null Commented Sep 7, 2020 at 14:43

1 Answer 1

1

Edit (misread the question):

Just check that the constructor actually exists before calling that method:

if (! is_null($reflection->getConstructor())) { ... }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.