Completed
Push — master ( 99c28a...4c4331 )
by Robin
47:48
created

Autoloader::load()   D

Complexity

Conditions 10
Paths 12

Size

Total Lines 31
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 10

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 31
ccs 24
cts 24
cp 1
rs 4.8197
cc 10
eloc 18
nc 12
nop 1
crap 10

How to fix   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
/**
3
 * @author Andreas Fischer <[email protected]>
4
 * @author Georg Ehrke <[email protected]>
5
 * @author Lukas Reschke <[email protected]>
6
 * @author Markus Goetz <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Robin Appelman <[email protected]>
9
 * @author Robin McCorkell <[email protected]>
10
 * @author Thomas Müller <[email protected]>
11
 * @author Victor Dubiniuk <[email protected]>
12
 *
13
 * @copyright Copyright (c) 2015, ownCloud, Inc.
14
 * @license AGPL-3.0
15
 *
16
 * This code is free software: you can redistribute it and/or modify
17
 * it under the terms of the GNU Affero General Public License, version 3,
18
 * as published by the Free Software Foundation.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23
 * GNU Affero General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU Affero General Public License, version 3,
26
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
27
 *
28
 */
29
30
namespace OC;
31
32
use \OCP\AutoloadNotAllowedException;
33
34
class Autoloader {
35
	/** @var bool */
36
	private $useGlobalClassPath = true;
37
	/** @var array */
38
	private $validRoots = [];
39
40
	/**
41
	 * Optional low-latency memory cache for class to path mapping.
42
	 *
43
	 * @var \OC\Memcache\Cache
44
	 */
45
	protected $memoryCache;
46
47
	/**
48
	 * Autoloader constructor.
49
	 *
50
	 * @param string[] $validRoots
51
	 */
52 32
	public function __construct(array $validRoots) {
53 32
		foreach ($validRoots as $root) {
54 21
			$this->validRoots[$root] = true;
55 32
		}
56 32
	}
57
58
	/**
59
	 * Add a path to the list of valid php roots for auto loading
60
	 *
61
	 * @param string $root
62
	 */
63 1074
	public function addValidRoot($root) {
64 1074
		$root = stream_resolve_include_path($root);
65 1074
		$this->validRoots[$root] = true;
66 1074
	}
67
68
	/**
69
	 * disable the usage of the global classpath \OC::$CLASSPATH
70
	 */
71
	public function disableGlobalClassPath() {
72
		$this->useGlobalClassPath = false;
73
	}
74
75
	/**
76
	 * enable the usage of the global classpath \OC::$CLASSPATH
77
	 */
78
	public function enableGlobalClassPath() {
79
		$this->useGlobalClassPath = true;
80
	}
81
82
	/**
83
	 * get the possible paths for a class
84
	 *
85
	 * @param string $class
86
	 * @return array|bool an array of possible paths or false if the class is not part of ownCloud
87
	 */
88 273
	public function findClass($class) {
89 273
		$class = trim($class, '\\');
90
91 273
		$paths = array();
92 273
		if ($this->useGlobalClassPath && array_key_exists($class, \OC::$CLASSPATH)) {
93 4
			$paths[] = \OC::$CLASSPATH[$class];
94
			/**
95
			 * @TODO: Remove this when necessary
96
			 * Remove "apps/" from inclusion path for smooth migration to mutli app dir
97
			 */
98 4
			if (strpos(\OC::$CLASSPATH[$class], 'apps/') === 0) {
99
				\OCP\Util::writeLog('core', 'include path for class "' . $class . '" starts with "apps/"', \OCP\Util::DEBUG);
100
				$paths[] = str_replace('apps/', '', \OC::$CLASSPATH[$class]);
101
			}
102 273
		} elseif (strpos($class, 'OC_') === 0) {
103 37
			$paths[] = \OC::$SERVERROOT . '/lib/private/legacy/' . strtolower(str_replace('_', '/', substr($class, 3)) . '.php');
104 37
			$paths[] = \OC::$SERVERROOT . '/lib/private/' . strtolower(str_replace('_', '/', substr($class, 3)) . '.php');
105 272
		} elseif (strpos($class, 'OC\\') === 0) {
106 144
			$split = explode('\\', $class, 3);
107
108 144
			if (count($split) === 3) {
109 131
				$split[1] = strtolower($split[1]);
110
111 131
				if ($split[1] === 'core') {
112 27
					$paths[] = \OC::$SERVERROOT . '/core/' . strtolower(str_replace('\\', '/', $split[2])) . '.php';
113 131
				} else if ($split[1] === 'settings') {
114 12
					$paths[] = \OC::$SERVERROOT . '/settings/' . strtolower(str_replace('\\', '/', $split[2])) . '.php';
115 105
				} else if ($split[1] === 'repair') {
116 11
					$paths[] = \OC::$SERVERROOT . '/lib/repair/' . strtolower(str_replace('\\', '/', $split[2])) . '.php';
117
118 11
				} else {
119 83
					$paths[] = \OC::$SERVERROOT . '/lib/private/' . $split[1] . '/' . strtolower(str_replace('\\', '/', $split[2])) . '.php';
120
				}
121
122 131
			} else {
123 16
				$paths[] = \OC::$SERVERROOT . '/lib/private/' . strtolower(str_replace('\\', '/', $split[1])) . '.php';
124
			}
125 239 View Code Duplication
		} elseif (strpos($class, 'OCP\\') === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
126 56
			$paths[] = \OC::$SERVERROOT . '/lib/public/' . strtolower(str_replace('\\', '/', substr($class, 4)) . '.php');
127 129
		} elseif (strpos($class, 'OCA\\') === 0) {
128 57
			list(, $app, $rest) = explode('\\', $class, 3);
129 57
			$app = strtolower($app);
130 57
			$appPath = \OC_App::getAppPath($app);
131 57
			if ($appPath && stream_resolve_include_path($appPath)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $appPath of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
132 57
				$paths[] = $appPath . '/' . strtolower(str_replace('\\', '/', $rest) . '.php');
133
				// If not found in the root of the app directory, insert '/lib' after app id and try again.
134 57
				$paths[] = $appPath . '/lib/' . strtolower(str_replace('\\', '/', $rest) . '.php');
135 57
			}
136 77 View Code Duplication
		} elseif (strpos($class, 'Test_') === 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
137 1
			$paths[] = \OC::$SERVERROOT . '/tests/lib/' . strtolower(str_replace('_', '/', substr($class, 5)) . '.php');
138 20
		} elseif (strpos($class, 'Test\\') === 0) {
139 1
			$paths[] = \OC::$SERVERROOT . '/tests/lib/' . strtolower(str_replace('\\', '/', substr($class, 5)) . '.php');
140 1
		}
141 273
		return $paths;
142
	}
143
144
	/**
145
	 * @param string $fullPath
146
	 * @return bool
147
	 */
148 244
	protected function isValidPath($fullPath) {
149 244
		foreach ($this->validRoots as $root => $true) {
150 244
			if (substr($fullPath, 0, strlen($root) + 1) === $root . '/') {
151 244
				return true;
152
			}
153 93
		}
154
		throw new AutoloadNotAllowedException($fullPath);
155
	}
156
157
	/**
158
	 * Load the specified class
159
	 *
160
	 * @param string $class
161
	 * @return bool
162
	 */
163 261
	public function load($class) {
164 261
		$pathsToRequire = null;
165 261
		if ($this->memoryCache) {
166 241
			$pathsToRequire = $this->memoryCache->get($class);
167 241
		}
168
169 261
		if (!is_array($pathsToRequire)) {
170
			// No cache or cache miss
171 261
			$pathsToRequire = array();
172 261
			foreach ($this->findClass($class) as $path) {
173 246
				if ($path[0] === '/') {
174 245
					$fullPath = file_exists($path) ? $path : false;
175 245
				} else {
176 4
					$fullPath = stream_resolve_include_path($path);
177
				}
178 246
				if ($fullPath && $this->isValidPath($fullPath)) {
179 244
					$pathsToRequire[] = $fullPath;
180 244
				}
181 261
			}
182
183 261
			if ($this->memoryCache) {
184 241
				$this->memoryCache->set($class, $pathsToRequire, 60); // cache 60 sec
185 241
			}
186 261
		}
187
188 261
		foreach ($pathsToRequire as $fullPath) {
189 244
			require_once $fullPath;
190 261
		}
191
192 261
		return false;
193
	}
194
195
	/**
196
	 * Sets the optional low-latency cache for class to path mapping.
197
	 *
198
	 * @param \OC\Memcache\Cache $memoryCache Instance of memory cache.
199
	 */
200
	public function setMemoryCache(\OC\Memcache\Cache $memoryCache = null) {
201
		$this->memoryCache = $memoryCache;
202
	}
203
}
204