Completed
Push — master ( d377d1...8216ee )
by Aimeos
12:16
created

Bootstrap::getManifests()   C

Complexity

Conditions 7
Paths 3

Size

Total Lines 30
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 14
nc 3
nop 1
dl 0
loc 30
rs 6.7272
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2011
6
 * @copyright Aimeos (aimeos.org), 2015-2016
7
 */
8
9
10
namespace Aimeos;
11
12
13
/**
14
 * Global starting point for applicatons.
15
 */
16
class Bootstrap
17
{
18
	private $manifests = array();
19
	private $extensions = array();
20
	private $extensionsDone = array();
21
	private $dependencies = array();
22
	private static $includePaths = array();
23
	private static $autoloader = false;
24
25
26
	/**
27
	 * Initialises the object.
28
	 *
29
	 * @param array $extdirs List of directories to look for manifest files (or sub-directories thereof)
30
	 * @param boolean $defaultdir If default extension directory should be included automatically
31
	 * @param string|null $basedir Aimeos core path (optional, __DIR__ if null)
32
	 */
33
	public function __construct( array $extdirs = array(), $defaultdir = true, $basedir = null )
34
	{
35
		if( $basedir === null ) {
36
			$basedir = __DIR__;
37
		}
38
39
		if( $defaultdir === true && is_dir( $basedir . DIRECTORY_SEPARATOR . 'ext' ) === true ) {
40
			$extdirs[] = realpath( $basedir . DIRECTORY_SEPARATOR . 'ext' );
41
		}
42
43
		$this->manifests[$basedir] = $this->getManifestFile( $basedir );
44
45
		self::$includePaths = $this->getIncludePaths();
46
		$this->registerAutoloader();
47
		$this->addDependencies( $extdirs );
48
		$this->addManifests( $this->dependencies );
49
		self::$includePaths = $this->getIncludePaths();
50
	}
51
52
53
	/**
54
	 * Loads the class files for a given class name.
55
	 *
56
	 * @param string $className Name of the class
57
	 * @return boolean True if file was found, false if not
58
	 */
59
	public static function autoload( $className )
60
	{
61
		$fileName = strtr( ltrim( $className, '\\' ), '\\_', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR ) . '.php';
62
63
		if( strncmp( $fileName, 'Aimeos' . DIRECTORY_SEPARATOR, 7 ) === 0 ) {
64
			$fileName = substr( $fileName, 7 );
65
		}
66
67
		foreach( self::$includePaths as $path )
68
		{
69
			$file = $path . DIRECTORY_SEPARATOR . $fileName;
70
71
			if( file_exists( $file ) === true && ( include_once $file ) !== false ) {
72
				return true;
73
			}
74
		}
75
76
		foreach( explode( PATH_SEPARATOR, get_include_path() ) as $path )
77
		{
78
			$file = $path . DIRECTORY_SEPARATOR . $fileName;
79
80
			if( file_exists( $file ) === true && ( include_once $file ) !== false ) {
81
				return true;
82
			}
83
		}
84
85
		return false;
86
	}
87
88
89
	/**
90
	 * Returns the list of paths for each domain where the translation files are located.
91
	 *
92
	 * @return array Associative list of i18n domains and lists of absolute paths to the translation directories
93
	 */
94
	public function getI18nPaths()
95
	{
96
		$paths = array();
97
98
		foreach( $this->manifests as $basePath => $manifest )
99
		{
100
			if( !isset( $manifest['i18n'] ) ) {
101
				continue;
102
			}
103
104
			foreach( $manifest['i18n'] as $domain => $location ) {
105
				$paths[$domain][] = $basePath . DIRECTORY_SEPARATOR . $location;
106
			}
107
		}
108
109
		return $paths;
110
	}
111
112
113
	/**
114
	 * Returns the include paths containing the required class files.
115
	 *
116
	 * @return array List of include paths
117
	 */
118
	public function getIncludePaths()
119
	{
120
		$includes = array();
121
122
		foreach( $this->manifests as $path => $manifest )
123
		{
124
			if( !isset( $manifest['include'] ) ) {
125
				continue;
126
			}
127
128
			foreach( $manifest['include'] as $paths ) {
129
				$includes[] = $path . DIRECTORY_SEPARATOR . $paths;
130
			}
131
		}
132
133
		return $includes;
134
	}
135
136
137
	/**
138
	 * Returns the paths containing the required configuration files.
139
	 *
140
	 * @return string[] List of configuration paths
141
	 */
142
	public function getConfigPaths()
143
	{
144
		$confpaths = array();
145
146
		foreach( $this->manifests as $path => $manifest )
147
		{
148
			if( !isset( $manifest['config'] ) ) {
149
				continue;
150
			}
151
152
			foreach( (array) $manifest['config'] as $relpath ) {
153
				$confpaths[] = $path . DIRECTORY_SEPARATOR . $relpath;
154
			}
155
		}
156
157
		return $confpaths;
158
	}
159
160
161
	/**
162
	 * Returns the paths stored in the manifest file for the given custom section.
163
	 *
164
	 * @param string $section Name of the section like in the manifest file
165
	 * @return array List of paths
166
	 */
167
	public function getCustomPaths( $section )
168
	{
169
		$paths = array();
170
171
		foreach( $this->manifests as $path => $manifest )
172
		{
173
			if( isset( $manifest['custom'][$section] ) ) {
174
				$paths[$path] = $manifest['custom'][$section];
175
			}
176
		}
177
178
		return $paths;
179
	}
180
181
182
	/**
183
	 * Returns the available extensions
184
	 *
185
	 * @return array List of available extension names
186
	 */
187
	public function getExtensions()
188
	{
189
		$list = [];
190
191
		foreach( $this->manifests as $path => $manifest )
192
		{
193
			if( isset( $manifest['name'] ) && $manifest['name'] != '' ) {
194
				$list[] = $manifest['name'];
195
			} else {
196
				$list[] = basename( $path );
197
			}
198
		}
199
200
		return $list;
201
	}
202
203
204
	/**
205
	 * Returns the list of paths where setup tasks are stored.
206
	 *
207
	 * @param string $site Name of the site like "default", "unitperf" and "unittest"
208
	 * @return array List of setup paths
209
	 */
210
	public function getSetupPaths( $site )
211
	{
212
		$setupPaths = array();
213
214
		foreach( $this->manifests as $path => $manifest )
215
		{
216
			if( !isset( $manifest['setup'] ) ) {
217
				continue;
218
			}
219
220
			foreach( $manifest['setup'] as $relpath )
221
			{
222
				$setupPaths[] = $path . DIRECTORY_SEPARATOR . $relpath;
223
224
				$sitePath = $path . DIRECTORY_SEPARATOR . $relpath . DIRECTORY_SEPARATOR . $site;
225
226
				if( is_dir( realpath( $sitePath ) ) ) {
227
					$setupPaths[] = $sitePath;
228
				}
229
			}
230
		}
231
232
		return $setupPaths;
233
	}
234
235
236
	/**
237
	 * Returns the language IDs for the available translations
238
	 *
239
	 * @param string $section Section name in the i18n paths
240
	 * @return array List of ISO language codes
241
	 */
242
	public function getI18nList( $section )
243
	{
244
		$list = array();
245
		$paths = $this->getI18nPaths();
246
		$paths = ( isset( $paths[$section] ) ? (array) $paths[$section] : array() );
247
248
		foreach( $paths as $path )
249
		{
250
			$iter = new \DirectoryIterator( $path );
251
252
			foreach( $iter as $file )
253
			{
254
				$name = $file->getFilename();
255
256
				if( $file->isFile() && preg_match('/^[a-z]{2,3}(_[A-Z]{2})?$/', $name ) ) {
257
					$list[$name] = null;
258
				}
259
			}
260
		}
261
262
		ksort( $list );
263
		return array_keys( $list );
264
	}
265
266
267
	/**
268
	 * Returns the configurations of the manifest files in the given directories.
269
	 *
270
	 * @param array $directories List of directories where the manifest files are stored
271
	 * @return array Associative list of directory / configuration array pairs
272
	 */
273
	protected function getManifests( array $directories )
274
	{
275
		$manifests = array();
276
277
		foreach( $directories as $directory )
278
		{
279
			$manifest = $this->getManifestFile( $directory );
280
281
			if( $manifest !== false )
282
			{
283
				$manifests[$directory] = $manifest;
284
				continue;
285
			}
286
287
			$dir = new \DirectoryIterator( $directory );
288
289
			foreach( $dir as $dirinfo )
290
			{
291
				if( $dirinfo->isDir() === false || $dirinfo->isDot() !== false
292
					|| ( $manifest = $this->getManifestFile( $dirinfo->getPathName() ) ) === false
293
				) {
294
					continue;
295
				}
296
297
				$manifests[$dirinfo->getPathName()] = $manifest;
298
			}
299
		}
300
301
		return $manifests;
302
	}
303
304
305
	/**
306
	 * Loads the manifest file from the given directory.
307
	 *
308
	 * @param string $dir Directory that includes the manifest file
309
	 * @return array|false Associative list of configurations or false if the file doesn't exist
310
	 */
311
	protected function getManifestFile( $dir )
312
	{
313
		$manifestFile = $dir . DIRECTORY_SEPARATOR . 'manifest.php';
314
315
		if( file_exists( $manifestFile ) ) {
316
			return include $manifestFile;
317
		}
318
319
		return false;
320
	}
321
322
323
	/**
324
	 * Registers the Aimeos autoloader.
325
	 */
326
	protected function registerAutoloader()
327
	{
328
		if( self::$autoloader === false )
329
		{
330
			spl_autoload_register( array( $this, 'autoload' ), true, false );
331
			self::$autoloader = true;
332
		}
333
334
		$ds = DIRECTORY_SEPARATOR;
335
336
		if( is_file( __DIR__ . $ds . 'vendor' . $ds . 'autoload.php' ) ) {
337
			require __DIR__ . $ds . 'vendor' . $ds . 'autoload.php';
338
		}
339
	}
340
341
342
	/**
343
	 * Adds the dependencies from the extensions
344
	 *
345
	 * @param array $extdirs List of extension directories
346
	 * @throws \Exception If dependencies are incorrectly configured
347
	 */
348
	private function addDependencies( array $extdirs )
349
	{
350
		foreach( $this->getManifests( $extdirs ) as $location => $manifest )
351
		{
352
			if( isset( $this->extensions[$manifest['name']] ) )
353
			{
354
				$location2 = $this->extensions[$manifest['name']]['location'];
355
				$msg = 'Extension "%1$s" exists twice in "%2$s" and in "%3$s"';
356
				throw new \Exception( sprintf( $msg, $manifest['name'], $location, $location2 ) );
357
			}
358
359
			if( !isset( $manifest['depends'] ) || !is_array( $manifest['depends'] ) ) {
360
				throw new \Exception( sprintf( 'Incorrect dependency configuration in manifest "%1$s"', $location ) );
361
			}
362
363
			$manifest['location'] = $location;
364
			$this->extensions[$manifest['name']] = $manifest;
365
366
			foreach( $manifest['depends'] as $name ) {
367
				$this->dependencies[$manifest['name']][$name] = $name;
368
			}
369
		}
370
	}
371
372
373
	/**
374
	 * Re-order the given dependencies of each manifest configuration.
375
	 *
376
	 * @param array $deps List of dependencies
377
	 * @param array $stack List of task names that are scheduled after this task
378
	 * @todo version checks
379
	 */
380
	private function addManifests( array $deps, array $stack = array( ) )
381
	{
382
		foreach( $deps as $extName => $name )
383
		{
384
			if( in_array( $extName, $this->extensionsDone ) ) {
385
				continue;
386
			}
387
388
			if( in_array( $extName, $stack ) ) {
389
				throw new \Exception( sprintf( 'Circular dependency for "%1$s" detected', $extName ) );
390
			}
391
392
			$stack[] = $extName;
393
394
			if( isset( $this->dependencies[$extName] ) ) {
395
				$this->addManifests( (array) $this->dependencies[$extName], $stack );
396
			}
397
398
			if( isset( $this->extensions[$extName] ) ) {
399
				$this->manifests[$this->extensions[$extName]['location']] = $this->extensions[$extName];
400
			}
401
402
			$this->extensionsDone[] = $extName;
403
		}
404
	}
405
}
406