Factory::as()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
namespace CheckedInstance;
3
4
use Psr\Container\ContainerInterface;
5
6
/**
7
 * Class Factory
8
 * @package CheckedInstance
9
 */
10
class Factory
11
{
12
    /**
13
     * @var ContainerInterface
14
     */
15
    protected static $container;
16
    /**
17
     * @var string
18
     */
19
    protected $for;
20
    /**
21
     * @var string
22
     */
23
    protected $prefix = '';
24
    /**
25
     * @var array
26
     */
27
    private $vars = [];
28
29
    /**
30
     * @param string $class
31
     * @return Factory
32
     */
33
    public static function for (string $class) : Factory
34
    {
35
        $instance = new self();
36
        $instance->for = $class;
37
        return $instance;
38
    }
39
40
    /**
41
     * @param ContainerInterface $c
42
     */
43
    public static function container(ContainerInterface $c)
44
    {
45
        self::$container = $c;
46
    }
47
48
    /**
49
     * @param string $prefix
50
     * @return Factory
51
     */
52
    public static function prefix(string $prefix) : Factory
53
    {
54
        $instance = new self();
55
        $instance->prefix = $prefix;
56
        return $instance;
57
    }
58
59
    /**
60
     * @param string|null $class
61
     * @return InstanceInterface
62
     * @throws FactoryFailureException
63
     */
64
    public function make(string $class = null) : InstanceInterface
65
    {
66
        if (empty($class) && !empty($this->for)) {
67
            return $this->make($this->for);
68
        }
69
        if (empty($class)) {
70
            throw new FactoryFailureException('Please configure or set class to make instance from!');
71
        }
72
        $instatnce = new $class();
73
        if (!($instatnce instanceof InstanceInterface)) {
74
            throw new FactoryFailureException($class.' is not implementing the InstanceInterface!');
75
        }
76
        if (isset(self::$container)) {
77
            $this->setFromContainer($class);
78
        } else {
79
            $this->guessInstantiation($class);
80
        }
81
        foreach ($this->vars as $name => $var) {
82
            $instatnce->set($name, $var);
83
        }
84
        $instatnce->check();
85
        return $instatnce;
86
    }
87
88
    /**
89
     * @param string $for
90
     */
91
    private function setFromContainer(string $for)
92
    {
93
        $req = $for::getRequired();
94
        if (array_values($req) == $req) {
95
            foreach ($req as $field) {
96
                if (!isset($this->vars[$field]) && self::$container->has($this->prefix.$field)) {
97
                    $this->vars[$field] = self::$container->get($this->prefix.$field);
98
                }
99
            }
100
        } else {
101
            foreach ($req as $implementation => $field) {
102
                if (!isset($this->vars[$field])) {
103
                    if (class_exists($implementation)) {
104
                        if (self::$container->has($implementation)) {
105
                            $this->vars[$field] = self::$container->get($implementation);
106
                        } elseif ($this->classInstantiateable($implementation)) {
107
                            $this->vars[$field] = new $implementation();
108
                        }
109
                    } else {
110
                        $this->vars[$field] = self::$container->get($this->prefix.$field);
111
                    }
112
                }
113
            }
114
        }
115
    }
116
117
    /**
118
     * @param $implementation
119
     * @return bool
120
     */
121
    private function classInstantiateable($implementation) : bool
122
    {
123
        $class = new \ReflectionClass($implementation);
124
        if (!$class->hasMethod('__construct')) {
125
            return true;
126
        }
127
        $constructor = $class->getConstructor();
128
        return $constructor->getNumberOfRequiredParameters() == 0;
129
    }
130
131
    /**
132
     * @param string $class
133
     */
134
    private function guessInstantiation(string $class)
135
    {
136
        $req = $class::getRequired();
137
        if (array_values($req) != $req) {
138
            foreach ($req as $implementation => $field) {
139
                if (!is_numeric($implementation)) {
140
                    if (class_exists($implementation) && $this->classInstantiateable($implementation)) {
141
                        $this->vars[$field] = new $implementation();
142
                    }
143
                }
144
            }
145
        }
146
    }
147
148
    /**
149
     * @param $key
150
     * @param $value
151
     * @return Factory
152
     */
153
    public function with($key, $value) : Factory
154
    {
155
        $this->vars[$key] = $value;
156
        return $this;
157
    }
158
159
    /**
160
     * @return array
161
     */
162
    public function getVars() : array
163
    {
164
        return $this->vars;
165
    }
166
167
    /**
168
     * @return string
169
     */
170
    public function getFor() : string
171
    {
172
        return $this->for;
173
    }
174
175
    /**
176
     * @param string $for
177
     * @return Factory
178
     */
179
    public function as (string $for) : Factory
180
    {
181
        $this->for = $for;
182
        return $this;
183
    }
184
}
185