Completed
Push — master ( f59311...3e65b5 )
by David
01:42
created

FactoryDefinition::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
4
namespace TheCodingMachine\Funky;
5
6
7
use Psr\Container\ContainerInterface;
8
use ReflectionMethod;
9
use ReflectionParameter;
10
use TheCodingMachine\Funky\Annotations\Factory;
11
use TheCodingMachine\Funky\Injections\ContainerInjection;
12
use TheCodingMachine\Funky\Injections\Injection;
13
use TheCodingMachine\Funky\Injections\ServiceInjection;
14
15
class FactoryDefinition
16
{
17
    /**
18
     * @var ReflectionMethod
19
     */
20
    private $reflectionMethod;
21
    private $name;
22
    private $aliases;
23
24
    public function __construct(ReflectionMethod $reflectionMethod, Factory $annotation)
25
    {
26
        if (!$reflectionMethod->isPublic()) {
27
            throw BadModifierException::mustBePublic($reflectionMethod, '@Factory');
28
        }
29
        if (!$reflectionMethod->isStatic()) {
30
            throw BadModifierException::mustBeStatic($reflectionMethod, '@Factory');
31
        }
32
33
        $this->reflectionMethod = $reflectionMethod;
34
        if ($annotation->isFromMethodName()) {
35
            $this->name = $reflectionMethod->getName();
36
        } elseif ($annotation->isFromType()) {
37
            $returnType = $reflectionMethod->getReturnType();
38
            if ($returnType === null) {
39
                throw UnknownTypeException::create($reflectionMethod);
40
            }
41
            $this->name = (string) $returnType;
42
        } else {
43
            $this->name = $annotation->getName();
44
        }
45
        $this->aliases = $annotation->getAliases();
46
    }
47
48
    /**
49
     * Returns true if the signature of the reflection method is compatible with container-interop/service-provider factories.
50
     */
51
    public function isPsrFactory(): bool
52
    {
53
        $numberOfParameters = $this->reflectionMethod->getNumberOfParameters();
54
        if ($numberOfParameters > 1) {
55
            return false;
56
        }
57
        if ($numberOfParameters === 0) {
58
            return true;
59
        }
60
        $parameter = $this->reflectionMethod->getParameters()[0];
61
        if ($parameter !== null && (string) $parameter->getType() === ContainerInterface::class) {
62
            return true;
63
        }
64
        return false;
65
    }
66
67
    public function buildFactoryCode(string $functionName) : string
68
    {
69
        $returnTypeCode = '';
70
        $returnType = $this->reflectionMethod->getReturnType();
71
        if ($returnType) {
72
            if ($returnType->isBuiltin()) {
73
                $returnTypeCode = ': '.$this->reflectionMethod->getReturnType();
74
            } else {
75
                $returnTypeCode = ': \\'.$this->reflectionMethod->getReturnType();
76
            }
77
        }
78
79
        return sprintf(<<<EOF
80
    public static function %s(ContainerInterface \$container)%s
81
    {
82
        return %s::%s(%s);
83
    }
84
    
85
EOF
86
            , $functionName,
87
            $returnTypeCode,
88
            $this->reflectionMethod->getDeclaringClass()->getName(),
89
            $this->reflectionMethod->getName(),
90
            implode(', ', array_map(function(Injection $injection) {return $injection->getCode();}, $this->getInjections()))
91
        );
92
    }
93
94
    /**
95
     * Returns a list of services to be injected.
96
     *
97
     * @return Injection[]
98
     */
99
    private function getInjections(): array
100
    {
101
        return array_map(function(ReflectionParameter $reflectionParameter) {
102
            $type = $reflectionParameter->getType();
103
            // No type? Let's inject by parameter name.
104
            if ($type === null || $type->isBuiltin()) {
105
                return new ServiceInjection($reflectionParameter->getName(), !$reflectionParameter->allowsNull());
106
            }
107
            if (((string)$type) === ContainerInterface::class) {
108
                return new ContainerInjection();
109
            }
110
            return new ServiceInjection((string)$type, !$reflectionParameter->allowsNull());
111
        }, $this->getReflectionMethod()->getParameters());
112
    }
113
114
    /**
115
     * @return ReflectionMethod
116
     */
117
    public function getReflectionMethod(): ReflectionMethod
118
    {
119
        return $this->reflectionMethod;
120
    }
121
122
    /**
123
     * @return string
124
     */
125
    public function getName(): string
126
    {
127
        return $this->name;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->name could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
128
    }
129
130
    /**
131
     * @return string[]
132
     */
133
    public function getAliases(): array
134
    {
135
        return $this->aliases;
136
    }
137
}