Joomla Platform  13.1
Documentation des API du framework Joomla Platform
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
loader.php
Aller à la documentation de ce fichier.
1 <?php
2 /**
3  * @package Joomla.Platform
4  *
5  * @copyright Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
6  * @license GNU General Public License version 2 or later; see LICENSE
7  */
8 
9 defined('JPATH_PLATFORM') or die;
10 
11 /**
12  * Static class to handle loading of libraries.
13  *
14  * @package Joomla.Platform
15  * @since 11.1
16  */
17 abstract class JLoader
18 {
19  /**
20  * Container for already imported library paths.
21  *
22  * @var array
23  * @since 11.1
24  */
25  protected static $classes = array();
26 
27  /**
28  * Container for already imported library paths.
29  *
30  * @var array
31  * @since 11.1
32  */
33  protected static $imported = array();
34 
35  /**
36  * Container for registered library class prefixes and path lookups.
37  *
38  * @var array
39  * @since 12.1
40  */
41  protected static $prefixes = array();
42 
43  /**
44  * Holds proxy classes and the class names the proxy.
45  *
46  * @var array
47  * @since 3.2
48  */
49  protected static $classAliases = array();
50 
51  /**
52  * Container for namespace => path map.
53  *
54  * @var array
55  * @since 12.3
56  */
57  protected static $namespaces = array();
58 
59  /**
60  * Method to discover classes of a given type in a given path.
61  *
62  * @param string $classPrefix The class name prefix to use for discovery.
63  * @param string $parentPath Full path to the parent folder for the classes to discover.
64  * @param boolean $force True to overwrite the autoload path value for the class if it already exists.
65  * @param boolean $recurse Recurse through all child directories as well as the parent path.
66  *
67  * @return void
68  *
69  * @since 11.1
70  */
71  public static function discover($classPrefix, $parentPath, $force = true, $recurse = false)
72  {
73  try
74  {
75  if ($recurse)
76  {
77  $iterator = new RecursiveIteratorIterator(
78  new RecursiveDirectoryIterator($parentPath),
79  RecursiveIteratorIterator::SELF_FIRST
80  );
81  }
82  else
83  {
84  $iterator = new DirectoryIterator($parentPath);
85  }
86 
87  foreach ($iterator as $file)
88  {
89  $fileName = $file->getFilename();
90 
91  // Only load for php files.
92  // Note: DirectoryIterator::getExtension only available PHP >= 5.3.6
93  if ($file->isFile() && substr($fileName, strrpos($fileName, '.') + 1) == 'php')
94  {
95  // Get the class name and full path for each file.
96  $class = strtolower($classPrefix . preg_replace('#\.php$#', '', $fileName));
97 
98  // Register the class with the autoloader if not already registered or the force flag is set.
99  if (empty(self::$classes[$class]) || $force)
100  {
101  self::register($class, $file->getPath() . '/' . $fileName);
102  }
103  }
104  }
105  }
106  catch (UnexpectedValueException $e)
107  {
108  // Exception will be thrown if the path is not a directory. Ignore it.
109  }
110  }
111 
112  /**
113  * Method to get the list of registered classes and their respective file paths for the autoloader.
114  *
115  * @return array The array of class => path values for the autoloader.
116  *
117  * @since 11.1
118  */
119  public static function getClassList()
120  {
121  return self::$classes;
122  }
123 
124  /**
125  * Method to get the list of registered namespaces.
126  *
127  * @return array The array of namespace => path values for the autoloader.
128  *
129  * @since 12.3
130  */
131  public static function getNamespaces()
132  {
133  return self::$namespaces;
134  }
135 
136  /**
137  * Loads a class from specified directories.
138  *
139  * @param string $key The class name to look for (dot notation).
140  * @param string $base Search this directory for the class.
141  *
142  * @return boolean True on success.
143  *
144  * @since 11.1
145  */
146  public static function import($key, $base = null)
147  {
148  // Only import the library if not already attempted.
149  if (!isset(self::$imported[$key]))
150  {
151  // Setup some variables.
152  $success = false;
153  $parts = explode('.', $key);
154  $class = array_pop($parts);
155  $base = (!empty($base)) ? $base : __DIR__;
156  $path = str_replace('.', DIRECTORY_SEPARATOR, $key);
157 
158  // Handle special case for helper classes.
159  if ($class == 'helper')
160  {
161  $class = ucfirst(array_pop($parts)) . ucfirst($class);
162  }
163  // Standard class.
164  else
165  {
166  $class = ucfirst($class);
167  }
168 
169  // If we are importing a library from the Joomla namespace set the class to autoload.
170  if (strpos($path, 'joomla') === 0)
171  {
172  // Since we are in the Joomla namespace prepend the classname with J.
173  $class = 'J' . $class;
174 
175  // Only register the class for autoloading if the file exists.
176  if (is_file($base . '/' . $path . '.php'))
177  {
178  self::$classes[strtolower($class)] = $base . '/' . $path . '.php';
179  $success = true;
180  }
181  }
182  /*
183  * If we are not importing a library from the Joomla namespace directly include the
184  * file since we cannot assert the file/folder naming conventions.
185  */
186  else
187  {
188  // If the file exists attempt to include it.
189  if (is_file($base . '/' . $path . '.php'))
190  {
191  $success = (bool) include_once $base . '/' . $path . '.php';
192  }
193  }
194 
195  // Add the import key to the memory cache container.
196  self::$imported[$key] = $success;
197  }
198 
199  return self::$imported[$key];
200  }
201 
202  /**
203  * Load the file for a class.
204  *
205  * @param string $class The class to be loaded.
206  *
207  * @return boolean True on success
208  *
209  * @since 11.1
210  */
211  public static function load($class)
212  {
213  // Sanitize class name.
214  $class = strtolower($class);
215 
216  // If the class already exists do nothing.
217  if (class_exists($class, false))
218  {
219  return true;
220  }
221 
222  // If the class is registered include the file.
223  if (isset(self::$classes[$class]))
224  {
225  include_once self::$classes[$class];
226 
227  return true;
228  }
229 
230  return false;
231  }
232 
233  /**
234  * Directly register a class to the autoload list.
235  *
236  * @param string $class The class name to register.
237  * @param string $path Full path to the file that holds the class to register.
238  * @param boolean $force True to overwrite the autoload path value for the class if it already exists.
239  *
240  * @return void
241  *
242  * @since 11.1
243  */
244  public static function register($class, $path, $force = true)
245  {
246  // Sanitize class name.
247  $class = strtolower($class);
248 
249  // Only attempt to register the class if the name and file exist.
250  if (!empty($class) && is_file($path))
251  {
252  // Register the class with the autoloader if not already registered or the force flag is set.
253  if (empty(self::$classes[$class]) || $force)
254  {
255  self::$classes[$class] = $path;
256  }
257  }
258  }
259 
260  /**
261  * Register a class prefix with lookup path. This will allow developers to register library
262  * packages with different class prefixes to the system autoloader. More than one lookup path
263  * may be registered for the same class prefix, but if this method is called with the reset flag
264  * set to true then any registered lookups for the given prefix will be overwritten with the current
265  * lookup path. When loaded, prefix paths are searched in a "last in, first out" order.
266  *
267  * @param string $prefix The class prefix to register.
268  * @param string $path Absolute file path to the library root where classes with the given prefix can be found.
269  * @param boolean $reset True to reset the prefix with only the given lookup path.
270  * @param boolean $prepend If true, push the path to the beginning of the prefix lookup paths array.
271  *
272  * @return void
273  *
274  * @throws RuntimeException
275  *
276  * @since 12.1
277  */
278  public static function registerPrefix($prefix, $path, $reset = false, $prepend = false)
279  {
280  // Verify the library path exists.
281  if (!file_exists($path))
282  {
283  throw new RuntimeException('Library path ' . $path . ' cannot be found.', 500);
284  }
285 
286  // If the prefix is not yet registered or we have an explicit reset flag then set set the path.
287  if (!isset(self::$prefixes[$prefix]) || $reset)
288  {
289  self::$prefixes[$prefix] = array($path);
290  }
291  // Otherwise we want to simply add the path to the prefix.
292  else
293  {
294  if ($prepend)
295  {
296  array_unshift(self::$prefixes[$prefix], $path);
297  }
298  else
299  {
300  self::$prefixes[$prefix][] = $path;
301  }
302  }
303  }
304 
305  /**
306  * Offers the ability for "just in time" usage of `class_alias()`.
307  * You cannot overwrite an existing alias.
308  *
309  * @param string $alias The alias name to register.
310  * @param string $original The original class to alias.
311  *
312  * @return boolean True if registration was successful. False if the alias already exists.
313  *
314  * @since 3.2
315  */
316  public static function registerAlias($alias, $original)
317  {
318  if (!isset(self::$classAliases[$alias]))
319  {
320  self::$classAliases[$alias] = $original;
321 
322  return true;
323  }
324 
325  return false;
326  }
327 
328  /**
329  * Register a namespace to the autoloader. When loaded, namespace paths are searched in a "last in, first out" order.
330  *
331  * @param string $namespace A case sensitive Namespace to register.
332  * @param string $path A case sensitive absolute file path to the library root where classes of the given namespace can be found.
333  * @param boolean $reset True to reset the namespace with only the given lookup path.
334  * @param boolean $prepend If true, push the path to the beginning of the namespace lookup paths array.
335  *
336  * @return void
337  *
338  * @throws RuntimeException
339  *
340  * @since 12.3
341  */
342  public static function registerNamespace($namespace, $path, $reset = false, $prepend = false)
343  {
344  // Verify the library path exists.
345  if (!file_exists($path))
346  {
347  throw new RuntimeException('Library path ' . $path . ' cannot be found.', 500);
348  }
349 
350  // If the namespace is not yet registered or we have an explicit reset flag then set the path.
351  if (!isset(self::$namespaces[$namespace]) || $reset)
352  {
353  self::$namespaces[$namespace] = array($path);
354  }
355 
356  // Otherwise we want to simply add the path to the namespace.
357  else
358  {
359  if ($prepend)
360  {
361  array_unshift(self::$namespaces[$namespace], $path);
362  }
363  else
364  {
365  self::$namespaces[$namespace][] = $path;
366  }
367  }
368  }
369 
370  /**
371  * Method to setup the autoloaders for the Joomla Platform.
372  * Since the SPL autoloaders are called in a queue we will add our explicit
373  * class-registration based loader first, then fall back on the autoloader based on conventions.
374  * This will allow people to register a class in a specific location and override platform libraries
375  * as was previously possible.
376  *
377  * @param boolean $enablePsr True to enable autoloading based on PSR-0.
378  * @param boolean $enablePrefixes True to enable prefix based class loading (needed to auto load the Joomla core).
379  * @param boolean $enableClasses True to enable class map based class loading (needed to auto load the Joomla core).
380  *
381  * @return void
382  *
383  * @since 12.3
384  */
385  public static function setup($enablePsr = true, $enablePrefixes = true, $enableClasses = true)
386  {
387  if ($enableClasses)
388  {
389  // Register the class map based autoloader.
390  spl_autoload_register(array('JLoader', 'load'));
391  }
392 
393  if ($enablePrefixes)
394  {
395  // Register the J prefix and base path for Joomla platform libraries.
396  self::registerPrefix('J', JPATH_PLATFORM . '/joomla');
397 
398  // Register the prefix autoloader.
399  spl_autoload_register(array('JLoader', '_autoload'));
400  }
401 
402  if ($enablePsr)
403  {
404  // Register the PSR-0 based autoloader.
405  spl_autoload_register(array('JLoader', 'loadByPsr0'));
406  spl_autoload_register(array('JLoader', 'loadByAlias'));
407  }
408  }
409 
410  /**
411  * Method to autoload classes that are namespaced to the PSR-0 standard.
412  *
413  * @param string $class The fully qualified class name to autoload.
414  *
415  * @return boolean True on success, false otherwise.
416  *
417  * @since 13.1
418  */
419  public static function loadByPsr0($class)
420  {
421  // Remove the root backslash if present.
422  if ($class[0] == '\\')
423  {
424  $class = substr($class, 1);
425  }
426 
427  // Find the location of the last NS separator.
428  $pos = strrpos($class, '\\');
429 
430  // If one is found, we're dealing with a NS'd class.
431  if ($pos !== false)
432  {
433  $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR;
434  $className = substr($class, $pos + 1);
435  }
436  // If not, no need to parse path.
437  else
438  {
439  $classPath = null;
440  $className = $class;
441  }
442 
443  $classPath .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
444 
445  // Loop through registered namespaces until we find a match.
446  foreach (self::$namespaces as $ns => $paths)
447  {
448  if (strpos($class, $ns) === 0)
449  {
450  // Loop through paths registered to this namespace until we find a match.
451  foreach ($paths as $path)
452  {
453  $classFilePath = $path . DIRECTORY_SEPARATOR . $classPath;
454 
455  // We check for class_exists to handle case-sensitive file systems
456  if (file_exists($classFilePath) && !class_exists($class, false))
457  {
458  return (bool) include_once $classFilePath;
459  }
460  }
461  }
462  }
463 
464  return false;
465  }
466 
467  /**
468  * Method to autoload classes that have been aliased using the registerAlias method.
469  *
470  * @param string $class The fully qualified class name to autoload.
471  *
472  * @return boolean True on success, false otherwise.
473  *
474  * @since 3.2
475  */
476  public static function loadByAlias($class)
477  {
478  // Remove the root backslash if present.
479  if ($class[0] == '\\')
480  {
481  $class = substr($class, 1);
482  }
483 
484  if (isset(self::$classAliases[$class]))
485  {
486  class_alias(self::$classAliases[$class], $class);
487  }
488  }
489 
490  /**
491  * Autoload a class based on name.
492  *
493  * @param string $class The class to be loaded.
494  *
495  * @return boolean True if the class was loaded, false otherwise.
496  *
497  * @since 11.3
498  */
499  private static function _autoload($class)
500  {
501  foreach (self::$prefixes as $prefix => $lookup)
502  {
503  $chr = strlen($prefix) < strlen($class) ? $class[strlen($prefix)] : 0;
504 
505  if (strpos($class, $prefix) === 0 && ($chr === strtoupper($chr)))
506  {
507  return self::_load(substr($class, strlen($prefix)), $lookup);
508  }
509  }
510 
511  return false;
512  }
513 
514  /**
515  * Load a class based on name and lookup array.
516  *
517  * @param string $class The class to be loaded (wihtout prefix).
518  * @param array $lookup The array of base paths to use for finding the class file.
519  *
520  * @return boolean True if the class was loaded, false otherwise.
521  *
522  * @since 12.1
523  */
524  private static function _load($class, $lookup)
525  {
526  // Split the class name into parts separated by camelCase.
527  $parts = preg_split('/(?<=[a-z0-9])(?=[A-Z])/x', $class);
528 
529  // If there is only one part we want to duplicate that part for generating the path.
530  $parts = (count($parts) === 1) ? array($parts[0], $parts[0]) : $parts;
531 
532  foreach ($lookup as $base)
533  {
534  // Generate the path based on the class name parts.
535  $path = $base . '/' . implode('/', array_map('strtolower', $parts)) . '.php';
536 
537  // Load the file if it exists.
538  if (file_exists($path))
539  {
540  return include $path;
541  }
542  }
543 
544  return false;
545  }
546 }
547 
548 /**
549  * Global application exit.
550  *
551  * This function provides a single exit point for the platform.
552  *
553  * @param mixed $message Exit code or string. Defaults to zero.
554  *
555  * @return void
556  *
557  * @codeCoverageIgnore
558  * @since 11.1
559  */
560 function jexit($message = 0)
561 {
562  exit($message);
563 }
564 
565 /**
566  * Intelligent file importer.
567  *
568  * @param string $path A dot syntax path.
569  *
570  * @return boolean True on success.
571  *
572  * @since 11.1
573  */
574 function jimport($path)
575 {
576  return JLoader::import($path);
577 }