Completed
Pull Request — master (#3)
by Alexpts
01:14
created

Cascader::resolveArgument()   B

Complexity

Conditions 6
Paths 13

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 11
cts 11
cp 1
rs 8.9617
c 0
b 0
f 0
cc 6
nc 13
nop 2
crap 6
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cascader;
6
7
use Cascader\Exception\InvalidClassException;
8
use Cascader\Exception\InvalidOptionsException;
9
use Cascader\Exception\OptionNotSetException;
10
use ReflectionClass;
11
use ReflectionParameter;
12
13
class Cascader
14
{
15
    /**
16
     * @param string $className
17
     * @param array $options
18
     *
19
     * @throws InvalidClassException
20
     * @throws InvalidOptionsException
21
     *
22
     * @return object
23
     */
24 10
    public function create(string $className, array $options)
25
    {
26 10
        $reflectionClass = $this->getReflectionClass($className);
27 8
        $options = Options::fromArray($options);
28
29 7
        $arguments = $this->marshalArguments($reflectionClass, $options);
30
31 6
        return new $className(...$arguments);
32
    }
33
34 10
    protected function getReflectionClass(string $className) : ReflectionClass
35
    {
36
        try {
37 10
            $reflectionClass = new ReflectionClass($className);
38 1
        } catch (\ReflectionException $ex) {
39 1
            throw InvalidClassException::forNonExistingClass($className);
40
        }
41
42 9
        if (! $reflectionClass->isInstantiable()) {
43 1
            throw InvalidClassException::forNonInstantiableClass($className);
44
        }
45
46 8
        return $reflectionClass;
47
    }
48
49 7
    protected function marshalArguments(ReflectionClass $reflectionClass, Options $options) : array
50
    {
51 7
        $constructor = $reflectionClass->getConstructor();
52
53 7
        if (null === $constructor) {
54 1
            return [];
55
        }
56
57 6
        $arguments = [];
58
59 6
        $constructorParameters = $constructor->getParameters();
60
61 6
        foreach ($constructorParameters as $parameter) {
62 6
            $arguments[] = $this->resolveArgument($parameter, $options);
63
        }
64
65 5
        return $arguments;
66
    }
67
68 6
    protected function resolveArgument(ReflectionParameter $parameter, Options $options)
69
    {
70
        try {
71 6
            $argument = $options->get($parameter->name);
72
73 5
            if (null !== ($parameterType = $parameter->getType())) {
74 5
                if (\is_array($argument) && ! $parameterType->isBuiltin()) {
75 1
                    list($class, $arguments) = $this->resolveClass((string)$parameterType, $argument);
76 1
                    $argument = $this->create($class, $arguments);
77
                }
78
            }
79
80 5
            return $argument;
81 4
        } catch (OptionNotSetException $ex) {
82 4
            if (! $parameter->isOptional()) {
83 1
                throw InvalidOptionsException::forMissingMandatoryParameter($parameter);
84
            }
85
86 3
            return $parameter->getDefaultValue();
87
        }
88
    }
89
90 4
    protected function resolveClass(string $class, array $arguments = []): array
91
    {
92 4
        $class = $arguments['__class__'] ?? $class;
93 4
        unset($arguments['__class__']);
94
95 4
        return [$class, $arguments];
96
    }
97
}
98