Passed
Push — master ( 332aa8...5dca75 )
by Corrado
01:48
created

SimpleFactory::findParam()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of SimpleFactory.
5
 *
6
 * (c) Corrado Ronci <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace SimpleFactory;
13
14
use ReflectionParameter;
15
16
class SimpleFactory
17
{
18
    /**
19
     * The class to factory
20
     *
21
     * @var string
22
     */
23
    private $class;
24
25
    /**
26
     * The flag to set default paramter to null
27
     *
28
     * @var bool
29
     */
30
    private $configDefaultParameter = false;
31
    
32
    /**
33
     * The class reflaction
34
     *
35
     * @var ReflectionClass
0 ignored issues
show
Bug introduced by
The type SimpleFactory\ReflectionClass was not found. Did you mean ReflectionClass? If so, make sure to prefix the type with \.
Loading history...
36
     */
37
    private $reflactionClass;
38
39
    /**
40
     * The parameters of class
41
     *
42
     * @var array
43
     */
44
    private $parameters = [];
45
46
    public function __construct(string $object, bool $defaultParamter=null)
47
    {
48
        if (!class_exists($object)) {
49
            throw new \InvalidArgumentException(\sprintf('The class %s not exits', $object));
50
        }
51
        
52
        $this->class = $object;
53
        if (!is_null($defaultParamter)) {
54
            $this->configDefaultParameter = $defaultParamter;
55
        }
56
        $this->makeReflection();
57
    }
58
59
    /**
60
     * This method return an instance of property class
61
     *
62
     * @return object
63
     */
64
    public function make()
65
    {
66
        return $this->reflactionClass->newInstanceArgs($this->setInstanceParameters());
67
    }
68
69
    /**
70
     * This method overwrite the parameter with class instantiated
71
     *
72
     * @param object $object
73
     * @return self
74
     */
75
    public function with($object) : self
76
    {
77
        if (!$object instanceof $this->class) {
78
            throw new \InvalidArgumentException(\sprintf('The object class must be instance of %s', $this->class));
79
        }
80
81
        $reflactionWithClass = new \ReflectionClass($object);
82
        
83
        foreach ($reflactionWithClass->getProperties() as $property) {
84
            $property->setAccessible(true);
85
            $this->parameters[$property->getName()] = $property->getValue($object);
86
        }
87
88
        return $this;
89
    }
90
91
    /**
92
     * The magic method will be use to set parameter of class,
93
     * if the parameter has a type this method validate the parameter
94
     *
95
     * @param string $name
96
     * @param array $arguments
97
     * @return self
98
     */
99
    public function __call($name, $arguments) : self
100
    {
101
        if (strpos($name, 'set') > -1) {
102
            $arg   = lcfirst(str_replace('set', '', $name));
103
            $param = $this->findParam($arg);            
104
            if ($param->hasType()) {
105
                if (gettype(current($arguments)) === 'object') {
106
                    $class = $param->getType()->getName();
107
                    if (current($arguments) instanceof $class) {
108
                        $this->parameters[$param->getName()] = current($arguments);
109
                        return $this;
110
                    }
111
                }
112
                $argType = $this->normalizeTypeReflection($param->getType()->getName());
113
                if (gettype(current($arguments)) !== $argType) {
114
                    throw new \InvalidArgumentException(\sprintf('The parameter %s of %s class must be of the type %s', $param, $this->class, $argType));
115
                }
116
            }
117
            
118
            $this->parameters[$param->getName()] = current($arguments);            
119
        }
120
121
        return $this;
122
    }
123
124
    /**
125
     * This method create self class by static function
126
     *
127
     * @param string $object
128
     * @param bool $defaultParamter
129
     * @return self
130
     */
131
    public static function create(string $object, bool $defaultParamter=null)
132
    {
133
        return new self($object, $defaultParamter);
134
    }
135
136
    /**
137
     * Reflection Class
138
     *
139
     * @return void
140
     */
141
    private function makeReflection()
142
    {
143
        $this->reflactionClass = new \ReflectionClass($this->class);
0 ignored issues
show
Documentation Bug introduced by
It seems like new ReflectionClass($this->class) of type ReflectionClass is incompatible with the declared type SimpleFactory\ReflectionClass of property $reflactionClass.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
144
        $this->reflactionArgsClass = $this->reflactionClass->getConstructor()->getParameters();
0 ignored issues
show
Bug Best Practice introduced by
The property reflactionArgsClass does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
145
    }
146
147
    /**
148
     * This function check param to
149
     * arguments of reflection class
150
     *
151
     * @param string $param
152
     * @return ReflectionParameter
153
     * @throws BadMethodCallException
154
     */
155
    private function findParam(string $param) : ReflectionParameter
156
    {
157
        foreach ($this->reflactionArgsClass as $arg) {
158
            if ($arg->getName() === $param) {
159
                return $arg;
160
            }
161
        }
162
163
        throw new \BadMethodCallException(\sprintf('The parameter %s of %s class doesn\'t exist', $param, $this->class));        
164
    }
165
166
    /**
167
     * This method prepare parameter of reflection class
168
     * for new instance. If parameter
169
     * has class try to set parameter to empty class instance
170
     * If parameter has changed replace parameter with new value.
171
     *
172
     * @return array
173
     */
174
    private function setInstanceParameters() : array
175
    {
176
        $parameters = [];
177
        if (!$this->configDefaultParameter and count($this->reflactionArgsClass) !== count($this->parameters)) {
178
            throw new \ArgumentCountError(
179
                \sprintf('Arguments to class %s exactly expected %s, passed %s', $this->class, count($this->reflactionArgsClass), count($this->parameters))
180
            );            
181
        }
182
        foreach ($this->reflactionArgsClass as $arg) {
183
            if (\array_key_exists($arg->getName(), $this->parameters)) {
184
                $parameters[$arg->getName()] = $this->parameters[$arg->getName()];
185
                continue;
186
            }
187
188
            if (!is_null($arg->getType())) {
189
                if (class_exists($arg->getType()->getName())) {
190
                    $parameters[$arg->getName()] = (new self($arg->getType()->getName()))->make();
191
                    continue;
192
                }
193
            }
194
195
            if ($this->configDefaultParameter === true) {
196
                $parameters[$arg->getName()] = null;
197
            }
198
        }
199
    
200
        return $parameters;
201
    }
202
203
    /**
204
     * This function normalize reflection argument type
205
     * with gettype php function
206
     *
207
     * @param string $type
208
     * @return string
209
     */
210
    private function normalizeTypeReflection($type) : string
211
    {
212
        switch ($type) {
213
            case 'int':
214
                return 'integer';
215
            case 'float':
216
                return 'double';
217
            default:
218
                return $type;
219
        }
220
    }
221
}
222