Autoloader::load()   B
last analyzed

Complexity

Conditions 10
Paths 72

Size

Total Lines 73
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 38
c 1
b 0
f 0
nc 72
nop 1
dl 0
loc 73
rs 7.6666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
namespace Darya\Foundation;
3
4
/**
5
 * Darya's class autoloader.
6
 *
7
 * TODO: Simplify Autoloader::load().
8
 * TODO: Maybe make attempt() a generator!
9
 *
10
 * @author Chris Andrew <[email protected]>
11
 */
12
class Autoloader
13
{
14
	/**
15
	 * Common subdirectories used as a last resort when autoloading.
16
	 *
17
	 * @var array
18
	 */
19
	private $commonSubdirs = array(
20
		'Common', 'Classes', 'Controllers', 'Models', 'Tests'
21
	);
22
23
	/**
24
	 * A map of namespaces to paths to use when autoloading.
25
	 *
26
	 * @var array
27
	 */
28
	private $namespaces = array();
29
30
	/**
31
	 * Base path to use when autoloading.
32
	 *
33
	 * @var string
34
	 */
35
	private $basePath;
36
37
	/**
38
	 * Instantiate an autoloader.
39
	 *
40
	 * @param string $basePath   Base directory path to load from
41
	 * @param array  $namespaces Namespace to directory mappings to register with the autoloader
42
	 */
43
	public function __construct($basePath = null, array $namespaces = array())
44
	{
45
		$this->basePath($basePath);
46
		$this->namespaces($namespaces);
47
	}
48
49
	/**
50
	 * Get and optionally set the base directory to load classes from.
51
	 *
52
	 * @param string $basePath
53
	 * @return string
54
	 */
55
	public function basePath($basePath = null)
56
	{
57
		$this->basePath = $basePath ?: realpath(__DIR__ . '/../../');
58
59
		return $this->basePath;
60
	}
61
62
	/**
63
	 * Register this autoloader.
64
	 *
65
	 * @return bool
66
	 */
67
	public function register()
68
	{
69
		return spl_autoload_register(array($this, 'load'));
70
	}
71
72
	/**
73
	 * Register namespace to directory mappings to attempt before the
74
	 * autoloader's default behaviour.
75
	 *
76
	 * Duplicate namespaces are permitted. Returns the autoloader's currently
77
	 * set namespaces after registering any that are given.
78
	 *
79
	 * @param array $namespaces Namespace keys and directory values
80
	 * @return array
81
	 */
82
	public function namespaces(array $namespaces = array())
83
	{
84
		foreach ($namespaces as $ns => $paths) {
85
			foreach ((array) $paths as $path) {
86
				$this->namespaces[] = array($ns, $path);
87
			}
88
		}
89
90
		return $this->namespaces;
91
	}
92
93
	/**
94
	 * Attempt to load the class at the given path.
95
	 *
96
	 * @param string $path
97
	 * @return bool
98
	 */
99
	public function attempt($path)
100
	{
101
		if (is_file($path)) {
102
			require_once $path;
103
104
			return true;
105
		}
106
107
		return false;
108
	}
109
110
	/**
111
	 * Load a class assuming the namespace is a path.
112
	 *
113
	 * Checks common subdirectory names as a last resort if nothing is found.
114
	 *
115
	 * @param string $class Class name
116
	 * @return bool
117
	 */
118
	public function load($class)
119
	{
120
		// Separate the class name and its namespace
121
		$parts = explode('\\', $class);
122
		$className = array_pop($parts);
123
		$dir = implode('/', $parts);
124
		$paths = array();
125
126
		// Test for potential registered namespace to directory mappings
127
		foreach ($this->namespaces as $registered) {
128
			list($ns, $nsPaths) = $registered;
129
130
			foreach ((array) $nsPaths as $nsPath) {
131
				// Try without and with the autoloader's base path
132
				$nsBasePaths = array('');
133
134
				if ($this->basePath) {
135
					$nsBasePaths[] = $this->basePath . '/';
136
				}
137
138
				foreach ($nsBasePaths as $nsBasePath) {
139
					if ($class === $ns) {
140
						array_push($paths, "$nsBasePath$nsPath");
141
						array_push($paths, "$nsBasePath$nsPath/$className.php");
142
						array_push($paths, "$nsBasePath" . strtolower($nsPath) . "/$className.php");
143
					}
144
145
					if (strpos($class, $ns) === 0) {
146
						array_push($paths, "$nsBasePath$nsPath/$dir/$className.php");
147
						array_push($paths, "$nsBasePath" . strtolower("$nsPath/$dir") . "/$className.php");
148
149
						$nsRemain = str_replace('\\', '/', substr($class, strlen($ns)));
150
						array_push($paths, "$nsBasePath$nsPath/$nsRemain.php");
151
						array_push($paths, "$nsPath/$nsRemain.php");
152
153
						$nsRemainDir = dirname($nsRemain);
154
						$nsRemainFile = basename($nsRemain);
155
						array_push($paths, "$nsBasePath$nsPath/" . strtolower($nsRemainDir) . "/$nsRemainFile.php");
156
					}
157
				}
158
			}
159
		}
160
161
		// Try using the namespace as an exact directory mapping
162
		array_push($paths, $this->basePath . "/$dir/$className.php");
163
164
		// Try using the namespace in lowercase as a directory mapping, with
165
		// only the class name in its original case
166
		$dirLowercase = strtolower($dir);
167
		array_push($paths, $this->basePath . "/$dirLowercase/$className.php");
168
169
		// Last try using the last part of the namespace as a subdirectory, with
170
		// and without a trailing 's', as well as any common subdirectory names
171
		$subdirs = array_merge($this->commonSubdirs, array(
172
			$className,
173
			$className . 's',
174
		));
175
176
		foreach ($subdirs as $subdir) {
177
			array_push($paths, $this->basePath . "/$dir/$subdir/$className.php");
178
179
			$subdirLowercase = strtolower($subdir);
180
			array_push($paths, $this->basePath . "/$dirLowercase/$subdirLowercase/$className.php");
181
		}
182
183
		// Finally, attempt to find the class
184
		foreach ($paths as $path) {
185
			if ($this->attempt($path)) {
186
				return true;
187
			}
188
		}
189
190
		return false;
191
	}
192
193
}
194