ClassLoader::getModule()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
ccs 6
cts 6
cp 1
rs 9.2
cc 4
eloc 5
nc 3
nop 2
crap 4
1
<?php namespace BuildR\ClassLoader;
2
3
use BuildR\ClassLoader\Exception\ClassLoaderException;
4
use BuildR\ClassLoader\Exception\ModuleException;
5
use BuildR\ClassLoader\Modules\ClassLoaderModuleInterface;
6
7
/**
8
 * The class loader. This class has multiple purpose, first its handle
9
 * the module registration, secondly this is responsible for loading classes.
10
 *
11
 * BuildR PHP Framework
12
 *
13
 * @author Zoltán Borsos <[email protected]>
14
 * @package ClassLoader
15
 *
16
 * @copyright    Copyright 2015, Zoltán Borsos.
17
 * @license      https://github.com/Zolli/BuildR/blob/master/LICENSE.md
18
 * @link         https://github.com/Zolli/BuildR
19
 */
20
class ClassLoader {
21
22
    /**
23
     * How many times try to increase the priority of a given module, before
24
     * throws an exception
25
     */
26
    const PRIORITY_INCREASE_RETRY_COUNT = 5;
27
28
    /**
29
     * How many times we tried to increase the priority in one registration session
30
     *
31
     * @type int
32
     */
33
    private $priorityIncreaseCounter = 0;
34
35
    /**
36
     * The module stack
37
     *
38
     * @type \BuildR\ClassLoader\Modules\ClassLoaderModuleInterface[]
39
     */
40
    private $modules = [];
41
42
    /**
43
     * The module preloader class
44
     *
45
     * @type \BuildR\ClassLoader\ModuleLoader
46
     */
47
    private $moduleLoader;
48
49
    /**
50
     * Creates a new instance from the class loader.
51
     *
52
     * @return \BuildR\ClassLoader\ClassLoader
53
     */
54 36
    public static function create() {
55 36
        $moduleLoader = new ModuleLoader();
56
57 36
        return new self($moduleLoader);
58
    }
59
60
    /**
61
     * ClassLoader constructor.
62
     *
63
     * @param \BuildR\ClassLoader\ModuleLoader $moduleLoader
64
     */
65 36
    protected function __construct(ModuleLoader $moduleLoader) {
66 36
        $this->moduleLoader = $moduleLoader;
67 36
    }
68
69
    /**
70
     * Attempts to load the given class loader module. When the module is not found
71
     * throws an exception.
72
     *
73
     * @param string $moduleFile Absolute location to the module
74
     * @param string $moduleClassName The module class FQCN
75
     * @param int|NULL $priorityOverride Overrides the module default priority
76
     *
77
     * @return \BuildR\ClassLoader\Modules\ClassLoaderModuleInterface
78
     *
79
     * @throws \BuildR\ClassLoader\Exception\ModuleException
80
     * @throws \BuildR\ClassLoader\Exception\ClassLoaderException
81
     */
82 34
    public function loadModule($moduleFile, $moduleClassName, $priorityOverride = NULL) {
83 34
        include_once $moduleFile;
84
85
        /** @type \BuildR\ClassLoader\Modules\ClassLoaderModuleInterface $module */
86 34
        $module = $this->moduleLoader->preLoad($moduleClassName);
87
88 34
        if($priorityOverride !== NULL) {
89 1
            $module->setPriority($priorityOverride);
90 1
        }
91
92 34
        return $this->registerModule($module);
93
    }
94
95
    /**
96
     * Get a registered loader module from the stack by its name.
97
     * To determine a module name use the modules getName() method.
98
     *
99
     * @param string $moduleName The module name
100
     * @param int $priority The target module priority
101
     *
102
     * @return \BuildR\ClassLoader\Modules\ClassLoaderModuleInterface
103
     *
104
     * @throws \BuildR\ClassLoader\Exception\ModuleException;
105
     */
106 2
    public function getModule($moduleName, $priority) {
107 2
        foreach ($this->modules as $modulePriority => $module) {
108 2
            if(call_user_func([$module, 'getName']) === $moduleName && $modulePriority == $priority) {
109 1
                return $module;
110
            }
111 1
        }
112
113 1
        throw ModuleException::notFound('Name: ' . $moduleName);
114
    }
115
116
    /**
117
     * Remove a registered loader module from the stack by its name.
118
     * To determine a module name use the modules getName() method.
119
     *
120
     * @param string $moduleName The module name
121
     * @param inst $priority The priority
122
     *
123
     * @return bool
124
     *
125
     * @throws \BuildR\ClassLoader\Exception\ModuleException
126
     */
127 2
    public function removeModule($moduleName, $priority) {
128 2
        foreach ($this->modules as $modulePriority => $module) {
129 2
            if(call_user_func([$module, 'getName']) === $moduleName && $modulePriority == $priority) {
130 1
                unset($this->modules[$priority]);
131 1
                ksort($this->modules);
132
133 1
                return TRUE;
134
            }
135 1
        }
136
137 1
        throw ModuleException::notFound('Name: ' . $moduleName . ' Priority: ' . $priority);
138
    }
139
140
    /**
141
     * Returns the loader entire module stack.
142
     *
143
     * @return \BuildR\ClassLoader\Modules\ClassLoaderModuleInterface[]
144
     */
145 3
    public function getModuleStack() {
146 3
        return $this->modules;
147
    }
148
149
    /**
150
     * Register a new module in class loader. and
151
     * returns the module when the registration is success.
152
     *
153
     * @param \BuildR\ClassLoader\Modules\ClassLoaderModuleInterface $module
154
     *
155
     * @return \BuildR\ClassLoader\Modules\ClassLoaderModuleInterface
156
     *
157
     * @throws \BuildR\ClassLoader\Exception\ClassLoaderException
158
     */
159 34
    protected function registerModule(ClassLoaderModuleInterface $module) {
160 34
        $priority = $module->getPriority();
161
162 34
        if(!isset($this->modules[$priority])) {
163 34
            $this->modules[$priority] = $module;
164 34
            $this->priorityIncreaseCounter = 0;
165 34
            ksort($this->modules);
166 34
            $module->onRegistered();
167
168 34
            return $module;
169
        }
170
171 1
        if($this->priorityIncreaseCounter >= self::PRIORITY_INCREASE_RETRY_COUNT) {
172 1
            $this->priorityIncreaseCounter = 0;
173 1
            throw ClassLoaderException::priorityIncreaseLimit();
174
        }
175
176 1
        $module->setPriority($priority + 1);
177 1
        $this->priorityIncreaseCounter++;
178 1
        $this->registerModule($module);
179
180 1
        $errorMessage = "Another class Loader module is registered with priority {$priority}! ";
181 1
        $errorMessage .= "Increasing priority by one, to find a new spot.";
182 1
        trigger_error($errorMessage, E_USER_NOTICE);
183 1
    }
184
185
    /**
186
     * This function is registered as auto-load method with spl_autoload_register()
187
     * The method try to load the given FQCN with all registered class loader module,
188
     * modules are sorted in registration phase, if a module able to load the class properly
189
     * the remaining loaders will not be queried for loading the class.
190
     *
191
     * @param string $className The FQCN.
192
     *
193
     * @return bool
194
     */
195 6
    public function loadClass($className) {
196 6
        foreach($this->modules as $loader) {
197 5
            if($loader->load($className) === TRUE) {
198 5
                return TRUE;
199
            }
200 2
        }
201
202 1
        return FALSE;
203
    }
204
205
    /**
206
     * Register the loader as valid SPL auto-loader
207
     *
208
     * @param bool $prepend Prepend the loader to the queue instead of appending it.
209
     */
210 36
    public function registerLoader($prepend = FALSE) {
211 36
        spl_autoload_register([$this, 'loadClass'], TRUE, $prepend);
212 36
    }
213
214
    /**
215
     * Try to remove this class from the spl_autoload queue.
216
     */
217 36
    public function unRegisterLoader() {
218 36
        spl_autoload_unregister([$this, 'loadClass']);
219 36
    }
220
221
}
222