SimpleFactory::makeReflection()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 4
rs 10
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
                    if (is_null(current($arguments)) && $param->allowsNull()) {
115
                        $this->parameters[$param->getName()] = null;
116
                        return $this;
117
                    }
118
                    
119
                    throw new \InvalidArgumentException(\sprintf('The parameter %s of %s class must be of the type %s', $param, $this->class, $argType));
120
                }
121
            }
122
            
123
            $this->parameters[$param->getName()] = current($arguments);            
124
        }
125
126
        return $this;
127
    }
128
129
    /**
130
     * This method create self class by static function
131
     *
132
     * @param string $object
133
     * @param bool $defaultParamter
134
     * @return self
135
     */
136
    public static function create(string $object, bool $defaultParamter=null)
137
    {
138
        return new self($object, $defaultParamter);
139
    }
140
141
    /**
142
     * Reflection Class
143
     *
144
     * @return void
145
     */
146
    private function makeReflection()
147
    {
148
        $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...
149
        $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...
150
    }
151
152
    /**
153
     * This function check param to
154
     * arguments of reflection class
155
     *
156
     * @param string $param
157
     * @return ReflectionParameter
158
     * @throws BadMethodCallException
159
     */
160
    private function findParam(string $param) : ReflectionParameter
161
    {
162
        foreach ($this->reflactionArgsClass as $arg) {
163
            if ($arg->getName() === $param) {
164
                return $arg;
165
            }
166
        }
167
168
        throw new \BadMethodCallException(\sprintf('The parameter %s of %s class doesn\'t exist', $param, $this->class));        
169
    }
170
171
    /**
172
     * This method prepare parameter of reflection class
173
     * for new instance. If parameter
174
     * has class try to set parameter to empty class instance
175
     * If parameter has changed replace parameter with new value.
176
     *
177
     * @return array
178
     */
179
    private function setInstanceParameters() : array
180
    {
181
        $parameters = [];
182
        if (!$this->configDefaultParameter and count($this->reflactionArgsClass) !== count($this->parameters)) {
183
            throw new \ArgumentCountError(
184
                \sprintf('Arguments to class %s exactly expected %s, passed %s', $this->class, count($this->reflactionArgsClass), count($this->parameters))
185
            );            
186
        }
187
        foreach ($this->reflactionArgsClass as $arg) {
188
            if (\array_key_exists($arg->getName(), $this->parameters)) {
189
                $parameters[$arg->getName()] = $this->parameters[$arg->getName()];
190
                continue;
191
            }
192
193
            if (!is_null($arg->getType())) {                
194
                if (class_exists($arg->getType()->getName())) {
195
                    if ($arg->allowsNull() !== true) {
196
                        $parameters[$arg->getName()] = (new self($arg->getType()->getName()))->make();
197
                        continue;
198
                    }                                        
199
                }
200
            }
201
202
            if ($this->configDefaultParameter === true) {
203
                $parameters[$arg->getName()] = null;
204
            }
205
        }
206
    
207
        return $parameters;
208
    }
209
210
    /**
211
     * This function normalize reflection argument type
212
     * with gettype php function
213
     *
214
     * @param string $type
215
     * @return string
216
     */
217
    private function normalizeTypeReflection($type) : string
218
    {
219
        switch ($type) {
220
            case 'int':
221
                return 'integer';
222
            case 'float':
223
                return 'double';
224
            case 'bool':
225
                return 'boolean';    
226
            default:
227
                return $type;
228
        }
229
    }
230
}
231