Passed
Pull Request — master (#13)
by Julien
03:19
created

Builder::extractConstructorArguments()   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 1
Bugs 0 Features 1
Metric Value
eloc 4
c 1
b 0
f 1
dl 0
loc 7
ccs 5
cts 5
cp 1
rs 10
cc 2
nc 2
nop 1
crap 2
1
<?php declare(strict_types=1);
2
3
4
namespace Pitchart\Phlunit;
5
6
use function Pitchart\Transformer\transform;
7
8
abstract class Builder
9
{
10
    /** @var array */
11
    private $arguments = [];
12
13
    private $constructorArguments = [];
14
15
    /** @var string|null */
16
    private $staticFactoryMethod;
17
18
    /**
19
     * @var \ReflectionClass
20
     */
21
    private $reflection;
22
23
    /**
24
     * @var bool|\ReflectionClass|string
25
     */
26
    protected $class;
27
28
    abstract public function build();
29
30 10
    protected function __construct(string $class, array $arguments, string $staticFactoryMethod = null)
31
    {
32 10
        $this->staticFactoryMethod = $staticFactoryMethod;
33 10
        $this->arguments = $arguments;
34 10
        $this->class = $class;
35 10
        $this->reflection = new \ReflectionClass($class);
36
37 10
        $this->extractConstructorArguments($staticFactoryMethod);
38
39 10
        $missingConstructorArguments = transform($this->constructorArguments)
40 10
            ->diff(\array_keys($arguments))
41 10
            ->toArray()
42
        ;
43
44 10
        if (\count($missingConstructorArguments) > 0) {
45 1
            throw new \InvalidArgumentException(\sprintf(
46 1
                'The following arguments key(s) must be provided with default value : [%s]',
47 1
                \implode(', ', $missingConstructorArguments)
48
            ));
49
        }
50 9
    }
51
52
    /**
53
     * Override arguments
54
     *
55
     * @param $name
56
     * @param $value
57
     */
58 2
    final protected function setArgument($name, $value)
59
    {
60 2
        if (! \array_key_exists($name, $this->arguments)) {
61 1
            throw new \InvalidArgumentException("There is no argument $name");
62
        }
63 1
        $this->arguments[$name] = $value;
64 1
    }
65
66
    /**
67
     * Allows to call with*() and and*() for defined arguments
68
     *
69
     * @param $name
70
     * @param $arguments
71
     *
72
     * @return Builder
73
     */
74 4
    final public function __call($name, $arguments): self
75
    {
76 4
        if (\strpos($name, 'with') === 0) {
77 3
            $property = \substr($name, 4);
78
        }
79 4
        if (\strpos($name, 'and') === 0) {
80 1
            $property = \substr($name, 3);
81
        }
82 4
        if (!isset($property)) {
83 1
            throw new \BadMethodCallException("Method $name is not supported. Supported methods are with*() and and*()");
84
        }
85 3
        if (\count($arguments) !== 1) {
86 1
            throw new \InvalidArgumentException(\sprintf("There must be exactly one argument for method '%s', %d given", $name, \count($arguments)));
87
        }
88 2
        $property = \lcfirst($property);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $property does not seem to be defined for all execution paths leading up to this point.
Loading history...
89 2
        $this->setArgument($property, \current($arguments));
90 1
        return $this;
91
    }
92
93
    /**
94
     * Builds the instance using the constructor or a static factory method
95
     *
96
     * @return mixed|object
97
     */
98 6
    final protected function buildInstance()
99
    {
100 6
        if (!$this->staticFactoryMethod) {
101 5
            $args = transform($this->constructorArguments)->map(function(string $argumentName) {
102 5
                return $this->arguments[$argumentName];
103 5
            })->toArray();
104
105 5
            return $this->reflection->newInstanceArgs($args);
106
        }
107 1
        return \call_user_func_array([$this->class, $this->staticFactoryMethod], $this->arguments);
108
    }
109
110
    /**
111
     * Extracts the names of the instanciation method arguments
112
     *
113
     * @param null|string $staticFactoryMethod
114
     */
115 10
    private function extractConstructorArguments(?string $staticFactoryMethod)
116
    {
117 10
        $instanciationMethod = $staticFactoryMethod === null ? $this->reflection->getConstructor() : $this->reflection->getMethod($staticFactoryMethod);
118
119 10
        $this->constructorArguments = transform($instanciationMethod->getParameters())
0 ignored issues
show
Bug introduced by
The method getParameters() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
        $this->constructorArguments = transform($instanciationMethod->/** @scrutinizer ignore-call */ getParameters())

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
120 10
            ->map(static function(\ReflectionParameter $parameter) { return $parameter->getName(); })
121 10
            ->toArray()
122
        ;
123 10
    }
124
}
125