Test Failed
Push — master ( 4b1a9b...99d6a7 )
by Corrado
03:37
created

SimpleFactory::create()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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