peakphp/framework

View on GitHub
src/Di/ClassResolver.php

Summary

Maintainability
B
4 hrs
Test Coverage
<?php

declare(strict_types=1);

namespace Peak\Di;

use Peak\Di\Exception\AmbiguousResolutionException;
use Peak\Di\Exception\InterfaceNotFoundException;
use Peak\Di\Exception\ClassDefinitionNotFoundException;
use \Exception;
use \InvalidArgumentException;

use function array_key_exists;
use function count;
use function interface_exists;
use function is_array;

class ClassResolver
{
    /**
     * ClassInspector
     * @var \Peak\Di\ClassInspector
     */
    protected $inspector;

    /**
     * Interface resolver
     * @var \Peak\Di\InterfaceResolver
     */
    protected $iresolver;

    /**
     * Explicit resolver
     * @var \Peak\Di\ExplicitResolver
     */
    protected $explicit;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->inspector = new ClassInspector();
        $this->explicit  = new ExplicitResolver();
        $this->iresolver = new InterfaceResolver();
    }

    /**
     * Resolve class arguments dependencies
     *
     * @param mixed $class
     * @param Container $container
     * @param array $args
     * @param null $explicit
     * @return array
     * @throws AmbiguousResolutionException
     * @throws ClassDefinitionNotFoundException
     * @throws InterfaceNotFoundException
     * @throws \ReflectionException
     */
    public function resolve($class, Container $container, array $args = [], $explicit = null)
    {
        $method = '__construct';

        if (is_array($class)) {
            if (count($class) != 2) {
                throw new InvalidArgumentException('Expecting a valid callback definition');
            }
            // treat $class as a callback
            $method = $class[1];
            $class  = $class[0];
        }

        $dependencies = $this->inspector->inspect($class, $method);
        $class_args   = [];
        $class_count  = 0;
        $i            = 0;

        foreach ($dependencies as $d) {
            if (isset($d['error'])) {
                throw new Exception($d['error']);
            }

            // its a class or an interface
            if (isset($d['class'])) {
                $name = $d['class'];
                ++$class_count;

                // look for object in explicit dependency declaration
                $result = $this->explicit->resolve($name, $container, $explicit);
                if ($result !== null) {
                    $class_args[] = $result;
                } elseif ($container->has($name)) {
                    // check if container has a stored instance
                    $class_args[] = $container->get($name);
                } elseif (!$d['optional'] && interface_exists($name)) {
                    // otherwise check if we are dealing with an interface dependency
                    $class_args[] = $this->iresolver->resolve($name, $container, $explicit);
                } elseif (!$d['optional']) {
                    // or resolve dependency by trying to instantiate object class name string
                    $child_args = [];
                    if (array_key_exists($name, $args)) {
                        $child_args = $args[$name];
                    }
                    $class_args[] = $container->create($name, $child_args, $explicit);
                }
            } elseif (array_key_exists($i - ($class_count), $args)) {
                // everything else that is not a type of class or interface
                $class_args[] = $args[$i - $class_count];
            }

            ++$i;
        }

        return $class_args;
    }
}