Completed
Push — master ( 45854b...5ccb75 )
by Flo
38s
created

ServiceLocator::getService()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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