ServiceProvider::findFileInFolderRecursively()   B
last analyzed

Complexity

Conditions 7
Paths 6

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
nc 6
nop 2
dl 0
loc 19
ccs 13
cts 13
cp 1
crap 7
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
namespace Vectorface\SnappyRouter\Di;
4
5
use \Exception;
6
use Vectorface\SnappyRouter\Config\Config;
7
use Vectorface\SnappyRouter\Exception\ServiceNotRegisteredException;
8
9
/**
10
 * A service provider providing dependency injection capabilities to the
11
 * router specifically for the list of services.
12
 * @copyright Copyright (c) 2014, VectorFace, Inc.
13
 * @author Dan Bruce <[email protected]>
14
 */
15
class ServiceProvider extends Di
16
{
17
18
    /** The default mode. Retrieve services from an explicit list. */
19
    const PROVISIONING_MODE_SERVICE_LIST = 1;
20
    /** The mode for retrieving services from a list of namespaces. */
21
    const PROVISIONING_MODE_NAMESPACES   = 2;
22
    /** The mode for retrieving services from a folder recursively. */
23
    const PROVISIONING_MODE_FOLDERS      = 3;
24
25
    /** The Di key for storing the list of registered namespaces */
26
    const KEY_NAMESPACES = 'serviceNamespaces';
27
    /** The Di key for storing the list of folders to scan for controllers */
28
    const KEY_FOLDERS    = 'serviceFolders';
29
30
    // the private provisioning mode
31
    private $provisioningMode = self::PROVISIONING_MODE_SERVICE_LIST;
32
33
    // a cache of service instances
34
    private $instanceCache = array();
35
36
    /**
37
     * Returns the array of all services.
38
     * @return The array of all services.
39
     */
40 1
    public function getServices()
41
    {
42 1
        return $this->allRegisteredElements();
43
    }
44
45
    /**
46
     * Returns the specified service path for the given key.
47
     * @param string $key The key to lookup.
48
     * @return Returns the path to the specified service for the given key.
49
     * @throws ServiceNotFoundForKeyException Throws this exception if the key isn't associated
50
     * with any registered service.
51
     */
52 1
    public function getService($key)
53
    {
54 1
        return $this->get($key);
55
    }
56
57
    /**
58
     * Specifies the mapping between the given key and service.
59
     * @param string $key The key to assign.
60
     * @param mixed $service The service to be assigned to the key.
61
     * @return Returns $this.
62
     */
63 1
    public function setService($key, $service)
64
    {
65 1
        $this->set($key, $service);
66 1
        unset($this->instanceCache[$key]);
67 1
        return $this;
68
    }
69
70
    /**
71
     * Returns an instance of the specified service.
72
     * @param string $key The key to lookup.
73
     * @param boolean $useCache An optional flag indicating whether we should
74
     *        use the cache. True by default.
75
     * @return AbstractController Returns an instance of the specified service.
76
     * @throws ServiceNotFoundForKeyException Throws this exception if the key isn't associated
77
     * with any registered service.
78
     */
79 24
    public function getServiceInstance($key, $useCache = true)
80
    {
81
        // retrieve the service from the instance cache if it exists
82 24
        if ($useCache && isset($this->instanceCache[$key])) {
83 3
            return $this->instanceCache[$key];
84
        }
85
86
        // retrieve the given controller from the key using the proper
87
        // provisioning mode
88 24
        switch ($this->provisioningMode) {
89 24
            case self::PROVISIONING_MODE_NAMESPACES:
90 3
                $this->instanceCache[$key] = $this->getServiceFromNamespaces($key);
91 2
                break;
92 21
            case self::PROVISIONING_MODE_FOLDERS:
93 3
                $this->instanceCache[$key] = $this->getServiceFromFolder($key);
94 2
                break;
95
            default:
96 18
                $this->instanceCache[$key] = $this->getServiceFromServiceList($key);
97
        }
98 20
        return $this->instanceCache[$key];
99
    }
100
101
    /**
102
     * Sets the list of namespaces and switches to namespace provisioning mode.
103
     * @param array $namespaces An array of namespaces.
104
     * @return ServiceProvider Returns $this.
105
     */
106 3
    public function setNamespaces($namespaces)
107
    {
108 3
        $this->set(self::KEY_NAMESPACES, $namespaces);
109 3
        $this->provisioningMode = self::PROVISIONING_MODE_NAMESPACES;
110 3
        return $this;
111
    }
112
113
    /**
114
     * Sets the list of folders and switches to folder provisioning mode.
115
     * @param array $folders An array of folders.
116
     * @return ServiceProvider Returns $this.
117
     */
118 3
    public function setFolders($folders)
119
    {
120 3
        $this->set(self::KEY_FOLDERS, $folders);
121 3
        $this->provisioningMode = self::PROVISIONING_MODE_FOLDERS;
122 3
        return $this;
123
    }
124
125
    /**
126
     * Returns an instance of the specified controller from the list of
127
     * namespaces.
128
     */
129 3
    private function getServiceFromNamespaces($controllerClass)
130
    {
131 3
        foreach ($this->get(self::KEY_NAMESPACES) as $namespace) {
132 2
            $fullClass = sprintf('%s\\%s', $namespace, $controllerClass);
133 2
            if (class_exists($fullClass)) {
134 2
                return new $fullClass();
135
            }
136
        }
137 1
        throw new Exception('Controller class '.$controllerClass.' was not found in any listed namespace.');
138
    }
139
140
    /**
141
     * Returns an instance of the specified controller from the list of
142
     * namespaces.
143
     * @param string $controllerClass The controller class file we are looking for.
144
     * @return AbstractController Returns an instance of the controller.
145
     */
146 3
    private function getServiceFromFolder($controllerClass)
147
    {
148 3
        foreach ($this->get(self::KEY_FOLDERS) as $folder) {
149 3
            $path = $this->findFileInFolderRecursively($controllerClass.'.php', $folder);
150 3
            if (false !== $path) {
151 2
                require_once $path;
152 3
                return new $controllerClass();
153
            }
154
        }
155 1
        throw new Exception('Controller class '.$controllerClass.' not found in any listed folder.');
156
    }
157
158
    /**
159
     * Returns an instance of the specified controller using the existing the
160
     * explicitly specified services list.
161
     * @param string $controllerClass The controller class we are resolving.
162
     * @return AbstractController Returns an instance of the controller.
163
     */
164 18
    private function getServiceFromServiceList($controllerClass)
165
    {
166
        // default provisioning mode uses a hardcoded list of services
167 18
        $serviceClass = $this->get($controllerClass);
168 16
        if (is_string($serviceClass) && !class_exists($serviceClass)) {
169 1
                require_once $serviceClass;
170 1
                $serviceClass = $controllerClass;
171 15
        } elseif (is_array($serviceClass)) {
172 1
            if (isset($serviceClass[Config::KEY_FILE])) {
173 1
                require_once $serviceClass[Config::KEY_FILE];
174
            }
175 1
            if (isset($serviceClass[Config::KEY_CLASS])) {
176 1
                $serviceClass = $serviceClass[Config::KEY_CLASS];
177
            }
178
        }
179 16
        return new $serviceClass();
180
    }
181
182
    /**
183
     * Scan for the specific file recursively.
184
     * @param string $file The file to search for.
185
     * @param string $folder The folder to search inside.
186
     * @return mixed Returns either the full path string to the file or false
187
     *         if the file was not found.
188
     */
189 3
    private function findFileInFolderRecursively($file, $folder)
190
    {
191 3
        $dir = dir($folder);
192 3
        while (false !== ($item = $dir->read())) {
193 3
            if ('.' === $item || '..' === $item) {
194 1
                continue;
195
            }
196 3
            $fullPath = $folder.DIRECTORY_SEPARATOR.$item;
197 3
            if (0 === strcasecmp($item, $file)) {
198 2
                return $fullPath;
199 2
            } elseif (is_dir($fullPath)) {
200 2
                $fullPath = $this->findFileInFolderRecursively($file, $fullPath);
201 2
                if (false !== $fullPath) {
202 1
                    return $fullPath;
203
                }
204
            }
205
        }
206 1
        return false;
207
    }
208
}
209