Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

engine/classes/Elgg/AutoloadManager.php (1 issue)

1
<?php
2
namespace Elgg;
3
/**
4
 * Manages core autoloading and caching of class maps
5
 *
6
 * @access private
7
 *
8
 * @package    Elgg.Core
9
 * @subpackage Autoloader
10
 */
11
class AutoloadManager {
12
13
	const FILENAME = 'autoload_data.php';
14
	const KEY_CLASSES = 'classes';
15
	const KEY_SCANNED_DIRS = 'scannedDirs';
16
17
	/**
18
	 * @var \Elgg\ClassLoader
19
	 */
20
	protected $loader;
21
22
	/**
23
	 * @var array directories that have already been scanned for classes
24
	 */
25
	protected $scannedDirs = [];
26
27
	/**
28
	 * @var bool was data in the manager altered?
29
	 */
30
	protected $altered = false;
31
32
	/**
33
	 * @var \ElggCache
34
	 */
35
	protected $storage = null;
36
37
	/**
38
	 * Constructor
39
	 *
40
	 * @param \Elgg\ClassLoader $loader Class loader object
41
	 */
42 65
	public function __construct(\Elgg\ClassLoader $loader) {
43 65
		$this->loader = $loader;
44 65
	}
45
46
	/**
47
	 * Add classes found in this directory to the class map and allow classes in
48
	 * subdirectories to be found by PSR-0 rules.
49
	 *
50
	 * We keep track of which dirs were scanned on previous requests so we don't need to
51
	 * rescan unless the cache is emptied.
52
	 *
53
	 * @param string $dir Directory of classes
54
	 * @return \Elgg\AutoloadManager
55
	 */
56 77
	public function addClasses($dir) {
57 77
		if (!in_array($dir, $this->scannedDirs)) {
58 5
			$map = $this->loader->getClassMap();
59 5
			$map->mergeMap($this->scanClassesDir($dir));
60 5
			$this->scannedDirs[] = $dir;
61 5
			$this->altered = true;
62
		}
63 77
		$this->loader->addFallback($dir);
64 77
		return $this;
65
	}
66
67
	/**
68
	 * Scan (non-recursively) a /classes directory for PHP files to map directly to classes.
69
	 *
70
	 * For BC with Elgg 1.8's autoloader we map these files directly, but besides this
71
	 * the autoloader is PSR-0 compatible.
72
	 *
73
	 * @param string $dir Directory of classes
74
	 * @return array
75
	 */
76 5
	protected function scanClassesDir($dir) {
77 5
		$dir = new \DirectoryIterator($dir);
78 5
		$map = [];
79
80 5
		foreach ($dir as $file) {
81
			/* @var \SplFileInfo $file */
82 5
			if (!$file->isFile() || !$file->isReadable()) {
83 5
				continue;
84
			}
85
86 5
			$path = $file->getRealPath();
87
88 5
			if (pathinfo($path, PATHINFO_EXTENSION) !== 'php') {
89
				continue;
90
			}
91
92 5
			$class = $file->getBasename('.php');
93 5
			$map[$class] = $path;
94
		}
95 5
		return $map;
96
	}
97
98
	/**
99
	 * If necessary, save necessary state details
100
	 *
101
	 * @return \Elgg\AutoloadManager
102
	 */
103 6
	public function saveCache() {
104 6
		if ($this->storage) {
105 6
			$map = $this->loader->getClassMap();
106 6
			if ($this->altered || $map->getAltered()) {
107 1
				$spec[self::KEY_CLASSES] = $map->getMap();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$spec was never initialized. Although not strictly required by PHP, it is generally a good practice to add $spec = array(); before regardless.
Loading history...
108 1
				$spec[self::KEY_SCANNED_DIRS] = $this->scannedDirs;
109 1
				$this->storage->save(self::FILENAME, serialize($spec));
110
			}
111
		}
112 6
		return $this;
113
	}
114
115
	/**
116
	 * Set the state of the manager from the cache
117
	 *
118
	 * @return bool was the cache loaded?
119
	 */
120 65
	public function loadCache() {
121 65
		$cache = $this->getCacheFileContents();
122 65
		if ($cache) {
123
			// the cached class map will have the full scanned core classes, so
124
			// don't consider the earlier mappings as "altering" the map
125 64
			$this->loader->getClassMap()
126 64
				->setMap($cache[self::KEY_CLASSES])
127 64
				->setAltered(false);
128 64
			$this->scannedDirs = $cache[self::KEY_SCANNED_DIRS];
129 64
			return true;
130
		}
131 2
		$this->altered = true;
132 2
		return false;
133
	}
134
135
	/**
136
	 * Tries to read the contents of the cache file and if valid returns the content
137
	 *
138
	 * @return false|array
139
	 */
140 65
	protected function getCacheFileContents() {
141 65
		if (!$this->storage) {
142
			return false;
143
		}
144
		
145 65
		$serialization = $this->storage->load(self::FILENAME);
146 65
		if (!$serialization) {
147 2
			return false;
148
		}
149
		
150 64
		$spec = unserialize($serialization);
151 64
		if (isset($spec[self::KEY_CLASSES])) {
152 64
			return $spec;
153
		}
154
		
155
		return false;
156
	}
157
158
	/**
159
	 * Delete the cache file
160
	 *
161
	 * @return \Elgg\AutoloadManager
162
	 */
163
	public function deleteCache() {
164
		if ($this->storage) {
165
			$this->storage->delete(self::FILENAME);
166
		}
167
		return $this;
168
	}
169
170
	/**
171
	 * Get the class loader
172
	 *
173
	 * @return \Elgg\ClassLoader
174
	 */
175
	public function getLoader() {
176
		return $this->loader;
177
	}
178
179
	/**
180
	 * Set the cache storage object
181
	 *
182
	 * @param \ElggCache $storage Cache object
183
	 * @return void
184
	 */
185 65
	public function setStorage(\ElggCache $storage) {
186 65
		$this->storage = $storage;
187 65
	}
188
189
	/**
190
	 * Save the cache on object destruction
191
	 *
192
	 * @return void
193
	 */
194 6
	public function __destruct() {
195 6
		$this->saveCache();
196 6
	}
197
}
198