Passed
Push — master ( 32cf20...91f5e8 )
by Corrado
01:54
created

SimpleFactory::setInstanceParameters()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
eloc 15
nc 8
nop 0
dl 0
loc 27
rs 8.4444
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
class SimpleFactory
15
{
16
    /**
17
     * The class to factory
18
     *
19
     * @var string
20
     */
21
    private $class;
22
23
    /**
24
     * The flag to set default paramter to null
25
     *
26
     * @var bool
27
     */
28
    private $configDefaultParameter = false;
29
    
30
    /**
31
     * The class reflaction
32
     *
33
     * @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...
34
     */
35
    private $reflactionClass;
36
37
    /**
38
     * The parameters of class
39
     *
40
     * @var array
41
     */
42
    private $parameters = [];
43
44
    public function __construct(string $object, bool $defaultParamter=null)
45
    {
46
        if (!class_exists($object)) {
47
            throw new \InvalidArgumentException(\sprintf('The class %s not exits', $object));
48
        }
49
        
50
        $this->class = $object;
51
        if (!is_null($defaultParamter)) {
52
            $this->configDefaultParameter = $defaultParamter;
53
        }
54
        $this->makeReflection();
55
    }
56
57
    /**
58
     * This method return an instance of property class
59
     *
60
     * @return object
61
     */
62
    public function make()
63
    {
64
        return $this->reflactionClass->newInstanceArgs($this->setInstanceParameters());
65
    }
66
67
    /**
68
     * This method overwrite the parameter with class instantiated
69
     *
70
     * @param object $object
71
     * @return self
72
     */
73
    public function with($object) : self
74
    {
75
        if (!$object instanceof $this->class) {
76
            throw new \InvalidArgumentException(\sprintf('The object class must be instance of %s', $this->class));
77
        }
78
79
        foreach ($this->reflactionClass->getMethods() as $method) {
80
            $param = strtolower(str_replace('get', '', $method->getName()));
81
            foreach ($this->reflactionArgsClass as $arg) {
82
                if ($arg->getName() === $param) {
83
                    $value = $object->{$method->getName()}();
84
                    $this->parameters[$param] = $value;
85
                }
86
            }
87
        }
88
89
        return $this;
90
    }
91
92
    /**
93
     * The magic method will be use to set parameter of class,
94
     * if the parameter has a type this method validate the parameter
95
     *
96
     * @param string $name
97
     * @param array $arguments
98
     * @return self
99
     */
100
    public function __call($name, $arguments) : self
101
    {
102
        if (strpos($name, 'set') > -1) {
103
            $param = lcfirst(str_replace('set', '', $name));
104
            foreach ($this->reflactionArgsClass as $arg) {
105
                if ($arg->getName() === $param) {
106
                    if ($arg->hasType()) {
107
                        if (gettype(current($arguments)) === 'object') {
108
                            $class = $arg->getType()->getName();
109
                            if (current($arguments) instanceof $class) {
110
                                $reflectionParam = current($arguments);
111
                                continue;
112
                            }
113
                        }
114
                        $argType = $this->normalizeTypeReflection($arg->getType()->getName());
115
                        if (gettype(current($arguments)) !== $argType) {
116
                            throw new \InvalidArgumentException(\sprintf('The parameter %s of %s class must be of the type %s', $param, $this->class, $argType));
117
                        }
118
                    }
119
120
                    $reflectionParam = current($arguments);
121
                }
122
            }
123
124
            if (!isset($reflectionParam)) {
125
                throw new \BadMethodCallException(\sprintf('The parameter %s of %s class doesn\'t exist', $param, $this->class));
126
            }
127
                        
128
            $this->parameters[$param] = $reflectionParam;
129
        }
130
131
        return $this;
132
    }
133
134
    /**
135
     * This method create self class by static function
136
     *
137
     * @param string $object
138
     * @return self
139
     */
140
    public static function create(string $object)
141
    {
142
        return new self($object);
143
    }
144
145
    /**
146
     * Reflection Class
147
     *
148
     * @return void
149
     */
150
    private function makeReflection()
151
    {
152
        $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...
153
        $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...
154
    }
155
156
    /**
157
     * This method prepare parameter of reflection class
158
     * for new instance. If parameter
159
     * has class try to set parameter to empty class instance
160
     * If parameter has changed replace parameter with new value.
161
     *
162
     * @return array
163
     */
164
    private function setInstanceParameters() : array
165
    {
166
        $parameters = [];
167
        if (!$this->configDefaultParameter and count($this->reflactionArgsClass) !== count($this->parameters)) {
168
            throw new \ArgumentCountError(
169
                \sprintf('Arguments to class %s exactly expected %s, passed %s', $this->class, count($this->reflactionArgsClass), count($this->parameters))
170
            );            
171
        }
172
        foreach ($this->reflactionArgsClass as $arg) {
173
            if (\array_key_exists($arg->getName(), $this->parameters)) {
174
                $parameters[$arg->getName()] = $this->parameters[$arg->getName()];
175
                continue;
176
            }
177
178
            if (!is_null($arg->getType())) {
179
                if (class_exists($arg->getType()->getName())) {
180
                    $parameters[$arg->getName()] = (new self($arg->getType()->getName()))->make();
181
                    continue;
182
                }
183
            }
184
185
            if ($this->configDefaultParameter === true) {
186
                $parameters[$arg->getName()] = null;
187
            }
188
        }
189
    
190
        return $parameters;
191
    }
192
193
    /**
194
     * This function normalize reflection argument type
195
     * with gettype php function
196
     *
197
     * @param string $type
198
     * @return string
199
     */
200
    private function normalizeTypeReflection($type) : string
201
    {
202
        switch ($type) {
203
            case 'int':
204
                return 'integer';
205
            case 'float':
206
                return 'double';
207
            default:
208
                return $type;
209
        }
210
    }
211
}
212