Passed
Push — master ( 3a280d...539d3e )
by Alexander
02:49 queued 01:18
created

DefinitionExtractor::fromFunction()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Factory\Extractors;
6
7
use Yiisoft\Factory\Definitions\DefinitionInterface;
8
use Yiisoft\Factory\Definitions\ClassDefinition;
9
use Yiisoft\Factory\Definitions\InvalidDefinition;
10
use Yiisoft\Factory\Definitions\ValueDefinition;
11
use Yiisoft\Factory\Exceptions\NotInstantiableException;
12
13
/**
14
 * Class DefinitionExtractor
15
 * This implementation resolves dependencies by using class type hints.
16
 * Note that service names need not match the parameter names, parameter names are ignored
17
 */
18
class DefinitionExtractor implements ExtractorInterface
19
{
20 13
    public function fromClassName(string $class): array
21
    {
22 13
        $reflectionClass = new \ReflectionClass($class);
23 13
        if (!$reflectionClass->isInstantiable()) {
24 5
            throw new NotInstantiableException($class);
25
        }
26 13
        $constructor = $reflectionClass->getConstructor();
27 13
        return $constructor === null ? [] : $this->fromFunction($constructor);
28
    }
29
30
    /**
31
     * @suppress PhanUndeclaredMethod
32
     */
33 11
    private function fromFunction(\ReflectionFunctionAbstract $reflectionFunction): array
34
    {
35 11
        $result = [];
36 11
        foreach ($reflectionFunction->getParameters() as $parameter) {
37 11
            $result[$parameter->getName()] = $this->fromParameter($parameter);
38
        }
39 11
        return $result;
40
    }
41
42
    /**
43
     * @suppress PhanUndeclaredMethod
44
     */
45 11
    private function fromParameter(\ReflectionParameter $parameter): DefinitionInterface
46
    {
47 11
        $type = $parameter->getType();
48 11
        $hasDefault = $parameter->isOptional() || $parameter->isDefaultValueAvailable() || (isset($type) && $type->allowsNull());
49
50 11
        if (!$hasDefault && $type === null) {
51
            return new InvalidDefinition();
52
        }
53
54
        // Our parameter has a class type hint
55 11
        if ($type !== null && !$type->isBuiltin()) {
56 8
            return new ClassDefinition($type->getName(), $type->allowsNull());
57
        }
58
59
        // Our parameter does not have a class type hint and either has a default value or is nullable.
60 4
        return new ValueDefinition(
61 4
            $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null,
62 4
            $type !== null ? $type->getName() : null
63
        );
64
    }
65
66
    public function fromCallable(callable $callable): array
67
    {
68
        return $this->fromFunction(new \ReflectionFunction(\Closure::fromCallable($callable)));
69
    }
70
}
71