php-vivace/di

View on GitHub
src/Resolver.php

Summary

Maintainability
A
45 mins
Test Coverage
<?php
namespace vivace\di;

/**
 * Allows dependencies by finding values from the scope.
 * @package vivace\di
 */
class Resolver
{
    /** @var array */
    private $metaData = [];
    /** @var Scope The scope in which the values will be searched */
    private $scope;

    /**
     * Resolver constructor.
     * @param Scope $scope The scope in which the values will be searched
     */
    public function __construct(Scope $scope)
    {
        $this->scope = $scope;
    }

    /**
     * @param $target
     * @return array [position, name, className, defaultValue]
     */
    protected function analyze($target): array
    {
        $mtd = (new \ReflectionClass($target))->getConstructor();

        if (empty($mtd)) {
            return [];
        }
        $result = [];
        foreach ($mtd->getParameters() as $parameter) {
            $property = [
                $parameter->getPosition(),
                $parameter->getName(),
                $parameter->getClass() ? $parameter->getClass()->getName() : null,
            ];
            if ($parameter->isDefaultValueAvailable()) {
                $property[] = $parameter->getDefaultValue();
            }
            $result[] = $property;
        }
        return $result;
    }

    /**
     * @param string|callable $target
     * @return array
     */
    private function meta($target): array
    {
        if (!isset($this->metaData[$target])) {
            $this->metaData[$target] = $this->analyze($target);
        }

        return $this->metaData[$target];
    }

    protected function getResolvers(): array
    {
        return [
            '\vivace\di\resolve\byPosition',
            '\vivace\di\resolve\byName',
            '\vivace\di\resolve\byTypeClass',
            '\vivace\di\resolve\byDefaultValue',
        ];
    }


    /**
     * @param string $className
     * @param array $parameters
     * @return array
     * @throws NotResolvedError
     */
    public function resolve(string $className, array $parameters = []): array
    {
        $meta = $this->meta($className);
        $argumentsValues = [];
        foreach ($meta as $item) {
            foreach ($this->getResolvers() as $resolver) {
                try {
                    $argumentsValues[] = call_user_func($resolver, $item, $parameters, $this->scope);
                    continue(2);
                } catch (NotResolvedError $e) {
                    continue;
                }
            }
            [$pos, $name] = $item;
            throw new NotResolvedError("Argument $className::$name#$pos required.");
        }
        return $argumentsValues;
    }

    public function __invoke(string $className, array $arguments = [])
    {
        return $this->resolve($className, $arguments);
    }
}