Completed
Push — master ( f18251...40c839 )
by Flo
15s
created

ServiceLocator::getFactory()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 11
nc 12
nop 1
1
<?php
2
3
namespace Faulancer\ServiceLocator;
4
5
use Faulancer\Exception\FactoryMayIncompatibleException;
6
use Faulancer\Exception\ServiceNotFoundException;
7
8
/**
9
 * Class ServiceLocator | ServiceLocator.php
10
 *
11
 * @package Faulancer\ServiceLocator
12
 * @author Florian Knapp <[email protected]>
13
 */
14
class ServiceLocator implements ServiceLocatorInterface {
15
16
    /**
17
     * Holds the service locator instance
18
     * @var ServiceLocator
19
     */
20
    private static $instance = null;
21
22
    /**
23
     * Holds the requested services
24
     * @var array
25
     */
26
    private static $services = [];
27
28
    /** @var array */
29
    private static $backup = [];
30
31
    /**
32
     * ServiceLocator private constructor.
33
     */
34
    private function __construct() {}
35
36
    /**
37
     * Return the instance of the service locator
38
     * @return ServiceLocator
39
     */
40
    public static function instance()
41
    {
42
        if (self::$instance === null) {
43
            self::$instance = new self;
44
        }
45
46
        return self::$instance;
47
    }
48
49
    /**
50
     * Try to get the service.
51
     * Returns per default always a new instance of the service/factory.
52
     *
53
     * @param string  $service
54
     * @param boolean $shared
55
     * @return ServiceInterface|FactoryInterface
56
     * @throws ServiceNotFoundException
57
     */
58
    public function get(string $service = '', $shared = true)
59
    {
60
        if (in_array($service, array_keys(self::$backup))) {
61
62
            $overriddenService = self::$services[$service];
63
            self::$services[$service] = self::$backup[$service];
64
            unset(self::$backup[$service]);
65
            return $overriddenService;
66
67
        }
68
69
        if ($shared && !empty(self::$services[$service])) {
70
            return self::$services[$service];
71
        }
72
73
        try {
74
            $class = $this->_getFactory($service)->createService($this);
75
        } catch (FactoryMayIncompatibleException $e) {
76
            $class = $this->_getService($service);
77
        }
78
79
        self::$services[$service] = $class;
80
81
        return $class;
82
    }
83
84
    /**
85
     * Get specific service by class name
86
     *
87
     * @param  string $service
88
     * @return mixed
89
     * @throws ServiceNotFoundException
90
     */
91
    private function _getService(string $service)
92
    {
93
        if (!class_exists($service)) {
94
            throw new ServiceNotFoundException($service . ' not found');
95
        }
96
97
        return new $service();
98
    }
99
100
    /**
101
     * Check if we have a factory for this service
102
     *
103
     * @param string $service
104
     * @return FactoryInterface|null
105
     * @throws FactoryMayIncompatibleException
106
     */
107
    private function _getFactory(string $service)
108
    {
109
        $parts     = explode('\\', $service);
110
        $className = array_splice($parts, count($parts) - 1, 1);
111
        $class     = implode('\\', $parts) . '\\Factory\\' . $className[0] . 'Factory';
112
113
        $isAutoDetected = class_exists($class) && in_array(FactoryInterface::class, class_implements($class));
114
        $isDirectAccess = class_exists($service) && in_array(FactoryInterface::class, class_implements($service));
115
116
        if ($isAutoDetected) {
117
            return new $class();
118
        }
119
120
        // This is a direct factory access
121
        if ($isDirectAccess) {
122
            return new $service();
123
        }
124
125
        throw new FactoryMayIncompatibleException();
126
    }
127
128
    /**
129
     * @param string           $name
130
     * @param ServiceInterface $service
131
     * @internal
132
     */
133
    public function set($name, $service)
134
    {
135
        if (isset(self::$services[$name])) {
136
            self::$backup[$name] = self::$services[$name];
137
        }
138
139
        self::$services[$name] = $service;
140
    }
141
142
    /**
143
     * Reset the service locators instance
144
     *
145
     * @internal
146
     */
147
    public static function destroy()
148
    {
149
        self::$instance = null;
150
    }
151
152
}