Completed
Push — master ( 8a02a1...c877ec )
by Fran
04:56 queued 14s
created

Singleton::init()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 22
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7.0099

Importance

Changes 7
Bugs 1 Features 0
Metric Value
cc 7
eloc 14
c 7
b 1
f 0
nc 5
nop 0
dl 0
loc 22
rs 6.9811
ccs 16
cts 17
cp 0.9412
crap 7.0099
1
<?php
2
3
namespace PSFS\base;
4
5
use PSFS\base\config\Config;
6
use PSFS\base\types\SingletonTrait;
7
8
/**
9
 * Class Singleton
10
 * @package PSFS\base
11
 */
12
class Singleton
13
{
14
    use SingletonTrait;
15
    /**
16
     * @var bool Flag that indicated if the class is already loaded
17
     */
18
    protected $loaded = false;
19
20 1
    public function __construct()
21
    {
22 1
        Logger::log(get_class($this) . ' constructor invoked');
23 1
    }
24
25
    /**
26
     * prevent the instance from being cloned
27
     *
28
     * @return void
29
     */
30
    private function __clone()
31
    {
32
    }
33
34
    /**
35
     * prevent from being unserialized
36
     *
37
     * @return void
38
     */
39
    private function __wakeup()
40
    {
41
    }
42
43
    /**
44
     * Magic setter
45
     * @param $variable
46
     * @param $value
47
     */
48 7
    public function __set($variable, $value)
49
    {
50 7
        if (property_exists(get_class($this), $variable)) {
51 7
            $this->$variable = $value;
52 7
        }
53 7
    }
54
55
    /**
56
     * Magic getter
57
     * @param string $variable
58
     * @return $mixed
59
     */
60
    public function __get($variable)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
61
    {
62
        return property_exists(get_class($this), $variable) ? $this->$variable : null;
63
    }
64
65
    /**
66
     * Método que devuelve si una clase está isntanciada correctamente
67
     * @return bool
68
     */
69 1
    public function isLoaded()
70
    {
71 1
        return $this->loaded;
72
    }
73
74
    /**
75
     * Método que configura como cargada una clase
76
     * @param bool $loaded
77
     */
78 1
    public function setLoaded($loaded = true)
79
    {
80 1
        $this->loaded = $loaded;
81 1
    }
82
83
    /**
84
     * HELPERS
85
     */
86
87
    /**
88
     * Método que extrae el nombre de la clase
89
     * @return string
90
     */
91
    public function getShortName()
92
    {
93
        $reflector = new \ReflectionClass(get_class($this));
94
        return $reflector->getShortName();
95
    }
96
97
    /**
98
     * Dependency inyector service invoker
99
     * @param string $variable
100
     * @param bool $singleton
101
     * @param string $classNameSpace
0 ignored issues
show
Documentation introduced by
Should the type for parameter $classNameSpace not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
102
     * @return $this
103
     */
104 1
    public function load($variable, $singleton = true, $classNameSpace = null)
105
    {
106 1
        $calledClass = get_called_class();
107
        try {
108 1
            $instance = $this->constructInyectableInstance($variable, $singleton, $classNameSpace, $calledClass);
109 1
            $setter = "set" . ucfirst($variable);
110 1
            if (method_exists($calledClass, $setter)) {
111
                $this->$setter($instance);
112
            } else {
113 1
                $this->$variable = $instance;
114
            }
115 1
        } catch (\Exception $e) {
116 1
            Logger::log($e->getMessage() . ': ' . $e->getFile() . ' [' . $e->getLine() . ']', LOG_ERR);
117
        }
118 1
        return $this;
119
    }
120
121
    /**
122
     * Método que inyecta automáticamente las dependencias en la clase
123
     */
124 1
    public function init()
125
    {
126 1
        if (!$this->isLoaded()) {
127 1
            $cacheFilename = "reflections" . DIRECTORY_SEPARATOR . sha1(get_class($this)) . ".json";
128
            /** @var \PSFS\base\Cache $cacheService */
129 1
            $cacheService = Cache::getInstance();
130
            /** @var \PSFS\base\config\Config $configService */
131 1
            $configService = Config::getInstance();
132 1
            $properties = $cacheService->getDataFromFile($cacheFilename, Cache::JSON);
133 1
            if (true === $configService->getDebugMode() || null === $properties) {
134 1
                $properties = $this->getClassProperties();
135 1
                $cacheService->storeData($cacheFilename, $properties, Cache::JSON);
136 1
            }
137
            /** @var \ReflectionProperty $property */
138 1
            if (!empty($properties) && is_array($properties)) foreach ($properties as $property => $class) {
139 1
                $this->load($property, true, $class);
140 1
            }
141 1
            $this->setLoaded();
142 1
        } else {
143
            Logger::log(get_class($this) . ' already loaded', LOG_INFO);
144
        }
145 1
    }
146
147
    /**
148
     * Método que extrae todas las propiedades inyectables de una clase
149
     * @param null $class
150
     * @return array
151
     */
152 1
    private function getClassProperties($class = null)
153
    {
154 1
        $properties = array();
155 1
        if (null === $class) {
156 1
            $class = get_class($this);
157 1
        }
158 1
        Logger::log('Extracting annotations properties from class ' . $class);
159 1
        $selfReflector = new \ReflectionClass($class);
160 1
        if (false !== $selfReflector->getParentClass()) {
161 1
            $properties = $this->getClassProperties($selfReflector->getParentClass()->getName());
162 1
        }
163 1 View Code Duplication
        foreach ($selfReflector->getProperties(\ReflectionProperty::IS_PROTECTED) as $property) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
164 1
            $doc = $property->getDocComment();
165 1
            if (preg_match('/@Inyectable/im', $doc)) {
166 1
                $instanceType = $this->extractVarType($property->getDocComment());
167 1
                if (null !== $instanceType) {
168 1
                    $properties[$property->getName()] = $instanceType;
169 1
                }
170 1
            }
171 1
        }
172 1
        return $properties;
173
    }
174
175
    /**
176
     * Método que extrae el tipo de instancia de la variable
177
     * @param $doc
178
     * @return null|string
179
     */
180 1
    private function extractVarType($doc)
181
    {
182 1
        $type = null;
183 1
        if (false !== preg_match('/@var\s+([^\s]+)/', $doc, $matches)) {
184 1
            list(, $type) = $matches;
185 1
        }
186 1
        return $type;
187
    }
188
189
    /**
190
     * Create the depecency injected
191
     * @param string $variable
192
     * @param bool $singleton
193
     * @param string $classNameSpace
194
     * @param string $calledClass
195
     * @return mixed
196
     */
197 1
    private function constructInyectableInstance($variable, $singleton, $classNameSpace, $calledClass)
198
    {
199 1
        Logger::log('Create inyectable instance for ' . $classNameSpace . ' into ' . get_class($this));
200 1
        $reflector = new \ReflectionClass($calledClass);
201 1
        $property = $reflector->getProperty($variable);
202 1
        $varInstanceType = (null === $classNameSpace) ? $this->extractVarType($property->getDocComment()) : $classNameSpace;
0 ignored issues
show
Coding Style introduced by
This line exceeds maximum limit of 120 characters; contains 124 characters

Overly long lines are hard to read on any screen. Most code styles therefor impose a maximum limit on the number of characters in a line.

Loading history...
203 1
        if (true === $singleton && method_exists($varInstanceType, "getInstance")) {
204 1
            $instance = $varInstanceType::getInstance();
205 1
        } else {
206
            $instance = new $varInstanceType();
207
        }
208 1
        return $instance;
209
    }
210
}
211