Issues (26)

src/Builder.php (2 issues)

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
    /**
14
     * @var array
15
     */
16
    private $constructorArguments = [];
17
18
    /** @var string|null */
19
    private $staticFactoryMethod;
20
21
    /**
22
     * @var \ReflectionClass
23
     */
24
    private $reflection;
25
26
    /**
27
     * @var bool|\ReflectionClass|string
28
     */
29
    protected $class;
30
31
    abstract public function build();
32
33 11
    protected function __construct(string $class, array $arguments, string $staticFactoryMethod = null)
34
    {
35 11
        $this->staticFactoryMethod = $staticFactoryMethod;
36 11
        $this->arguments = $arguments;
37 11
        $this->class = $class;
38 11
        $this->reflection = new \ReflectionClass($class);
39
40 11
        $this->extractConstructorArguments($staticFactoryMethod);
41
42 11
        $missingConstructorArguments = transform($this->constructorArguments)
43 11
            ->remove(static function(string $key) use ($arguments) {
44 11
                return \array_key_exists($key, $arguments);
45 11
            })
46 11
            ->toArray()
47
        ;
48
49 11
        if (\count($missingConstructorArguments) > 0) {
50 2
            throw new \InvalidArgumentException(\sprintf(
51 2
                'The following arguments key(s) must be provided with default value : [%s]',
52 2
                \implode(', ', $missingConstructorArguments)
53
            ));
54
        }
55 9
    }
56
57
    /**
58
     * Override arguments
59
     *
60
     * @param $name
61
     * @param $value
62
     *
63
     * @return void
64
     */
65 2
    final protected function setArgument(string $name, $value): void
66
    {
67 2
        if (! \array_key_exists($name, $this->arguments)) {
68 1
            throw new \InvalidArgumentException("There is no argument $name");
69
        }
70 1
        $this->arguments[$name] = $value;
71 1
    }
72
73
    /**
74
     * Allows to call with*() and and*() for defined arguments
75
     *
76
     * @param $name
77
     * @param $arguments
78
     *
79
     * @return Builder
80
     */
81 4
    final public function __call($name, $arguments): self
82
    {
83 4
        if (\strpos($name, 'with') === 0) {
84 3
            $property = \substr($name, 4);
85
        }
86 4
        if (\strpos($name, 'and') === 0) {
87 1
            $property = \substr($name, 3);
88
        }
89 4
        if (!isset($property)) {
90 1
            throw new \BadMethodCallException("Method $name is not supported. Supported methods are with*() and and*()");
91
        }
92 3
        if (\count($arguments) !== 1) {
93 1
            throw new \InvalidArgumentException(\sprintf("There must be exactly one argument for method '%s', %d given", $name, \count($arguments)));
94
        }
95 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...
96 2
        $this->setArgument($property, \current($arguments));
97 1
        return $this;
98
    }
99
100
    /**
101
     * Builds the instance using the constructor or a static factory method
102
     *
103
     * @return mixed|object
104
     */
105 6
    final protected function buildInstance()
106
    {
107 6
        if (!$this->staticFactoryMethod) {
108 5
            $args = transform($this->constructorArguments)->map(function(string $argumentName) {
109 5
                return $this->arguments[$argumentName];
110 5
            })->toArray();
111
112 5
            return $this->reflection->newInstanceArgs($args);
113
        }
114 1
        return \call_user_func_array([$this->class, $this->staticFactoryMethod], $this->arguments);
115
    }
116
117
    /**
118
     * Extracts the names of the instanciation method arguments
119
     *
120
     * @param null|string $staticFactoryMethod
121
     *
122
     * @return void
123
     */
124 11
    private function extractConstructorArguments(?string $staticFactoryMethod): void
125
    {
126 11
        $instanciationMethod = $staticFactoryMethod === null ? $this->reflection->getConstructor() : $this->reflection->getMethod($staticFactoryMethod);
127
128 11
        $this->constructorArguments = transform($instanciationMethod->getParameters())
0 ignored issues
show
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

128
        $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...
129 11
            ->map(static function(\ReflectionParameter $parameter) { return $parameter->getName(); })
130 11
            ->toArray()
131
        ;
132 11
    }
133
}
134