[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/ -> loader.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2005 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8  
   9   * @phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace
  10   */
  11  
  12  defined('JPATH_PLATFORM') or die;
  13  
  14  /**
  15   * Static class to handle loading of libraries.
  16   *
  17   * @since    1.7.0
  18   */
  19  abstract class JLoader
  20  {
  21      /**
  22       * Container for already imported library paths.
  23       *
  24       * @var    array
  25       * @since  1.7.0
  26       */
  27      protected static $classes = array();
  28  
  29      /**
  30       * Container for already imported library paths.
  31       *
  32       * @var    array
  33       * @since  1.7.0
  34       */
  35      protected static $imported = array();
  36  
  37      /**
  38       * Container for registered library class prefixes and path lookups.
  39       *
  40       * @var    array
  41       * @since  3.0.0
  42       */
  43      protected static $prefixes = array();
  44  
  45      /**
  46       * Holds proxy classes and the class names the proxy.
  47       *
  48       * @var    array
  49       * @since  3.2
  50       */
  51      protected static $classAliases = array();
  52  
  53      /**
  54       * Holds the inverse lookup for proxy classes and the class names the proxy.
  55       *
  56       * @var    array
  57       * @since  3.4
  58       */
  59      protected static $classAliasesInverse = array();
  60  
  61      /**
  62       * Container for namespace => path map.
  63       *
  64       * @var    array
  65       * @since  3.1.4
  66       */
  67      protected static $namespaces = array();
  68  
  69      /**
  70       * Holds a reference for all deprecated aliases (mainly for use by a logging platform).
  71       *
  72       * @var    array
  73       * @since  3.6.3
  74       */
  75      protected static $deprecatedAliases = array();
  76  
  77      /**
  78       * The root folders where extensions can be found.
  79       *
  80       * @var    array
  81       * @since  4.0.0
  82       */
  83      protected static $extensionRootFolders = array();
  84  
  85      /**
  86       * Method to discover classes of a given type in a given path.
  87       *
  88       * @param   string   $classPrefix  The class name prefix to use for discovery.
  89       * @param   string   $parentPath   Full path to the parent folder for the classes to discover.
  90       * @param   boolean  $force        True to overwrite the autoload path value for the class if it already exists.
  91       * @param   boolean  $recurse      Recurse through all child directories as well as the parent path.
  92       *
  93       * @return  void
  94       *
  95       * @since       1.7.0
  96       * @deprecated  5.0   Classes should be autoloaded. Use JLoader::registerPrefix() or JLoader::registerNamespace() to register an autoloader for
  97       *                    your files.
  98       */
  99      public static function discover($classPrefix, $parentPath, $force = true, $recurse = false)
 100      {
 101          try {
 102              if ($recurse) {
 103                  $iterator = new RecursiveIteratorIterator(
 104                      new RecursiveDirectoryIterator($parentPath),
 105                      RecursiveIteratorIterator::SELF_FIRST
 106                  );
 107              } else {
 108                  $iterator = new DirectoryIterator($parentPath);
 109              }
 110  
 111              /** @type  $file  DirectoryIterator */
 112              foreach ($iterator as $file) {
 113                  $fileName = $file->getFilename();
 114  
 115                  // Only load for php files.
 116                  if ($file->isFile() && $file->getExtension() === 'php') {
 117                      // Get the class name and full path for each file.
 118                      $class = strtolower($classPrefix . preg_replace('#\.php$#', '', $fileName));
 119  
 120                      // Register the class with the autoloader if not already registered or the force flag is set.
 121                      if ($force || empty(self::$classes[$class])) {
 122                          self::register($class, $file->getPath() . '/' . $fileName);
 123                      }
 124                  }
 125              }
 126          } catch (UnexpectedValueException $e) {
 127              // Exception will be thrown if the path is not a directory. Ignore it.
 128          }
 129      }
 130  
 131      /**
 132       * Method to get the list of registered classes and their respective file paths for the autoloader.
 133       *
 134       * @return  array  The array of class => path values for the autoloader.
 135       *
 136       * @since   1.7.0
 137       */
 138      public static function getClassList()
 139      {
 140          return self::$classes;
 141      }
 142  
 143      /**
 144       * Method to get the list of deprecated class aliases.
 145       *
 146       * @return  array  An associative array with deprecated class alias data.
 147       *
 148       * @since   3.6.3
 149       */
 150      public static function getDeprecatedAliases()
 151      {
 152          return self::$deprecatedAliases;
 153      }
 154  
 155      /**
 156       * Method to get the list of registered namespaces.
 157       *
 158       * @return  array  The array of namespace => path values for the autoloader.
 159       *
 160       * @since   3.1.4
 161       */
 162      public static function getNamespaces()
 163      {
 164          return self::$namespaces;
 165      }
 166  
 167      /**
 168       * Loads a class from specified directories.
 169       *
 170       * @param   string  $key   The class name to look for (dot notation).
 171       * @param   string  $base  Search this directory for the class.
 172       *
 173       * @return  boolean  True on success.
 174       *
 175       * @since       1.7.0
 176       * @deprecated  5.0   Classes should be autoloaded. Use JLoader::registerPrefix() or JLoader::registerNamespace() to register an autoloader for
 177       *                    your files.
 178       */
 179      public static function import($key, $base = null)
 180      {
 181          // Only import the library if not already attempted.
 182          if (!isset(self::$imported[$key])) {
 183              // Setup some variables.
 184              $success = false;
 185              $parts   = explode('.', $key);
 186              $class   = array_pop($parts);
 187              $base    = (!empty($base)) ? $base : __DIR__;
 188              $path    = str_replace('.', DIRECTORY_SEPARATOR, $key);
 189  
 190              // Handle special case for helper classes.
 191              if ($class === 'helper') {
 192                  $class = ucfirst(array_pop($parts)) . ucfirst($class);
 193              } else {
 194                  // Standard class.
 195                  $class = ucfirst($class);
 196              }
 197  
 198              // If we are importing a library from the Joomla namespace set the class to autoload.
 199              if (strpos($path, 'joomla') === 0) {
 200                  // Since we are in the Joomla namespace prepend the classname with J.
 201                  $class = 'J' . $class;
 202  
 203                  // Only register the class for autoloading if the file exists.
 204                  if (is_file($base . '/' . $path . '.php')) {
 205                      self::$classes[strtolower($class)] = $base . '/' . $path . '.php';
 206                      $success = true;
 207                  }
 208              } else {
 209                  /**
 210                   * If we are not importing a library from the Joomla namespace directly include the
 211                   * file since we cannot assert the file/folder naming conventions.
 212                   */
 213                  // If the file exists attempt to include it.
 214                  if (is_file($base . '/' . $path . '.php')) {
 215                      $success = (bool) include_once $base . '/' . $path . '.php';
 216                  }
 217              }
 218  
 219              // Add the import key to the memory cache container.
 220              self::$imported[$key] = $success;
 221          }
 222  
 223          return self::$imported[$key];
 224      }
 225  
 226      /**
 227       * Load the file for a class.
 228       *
 229       * @param   string  $class  The class to be loaded.
 230       *
 231       * @return  boolean  True on success
 232       *
 233       * @since   1.7.0
 234       */
 235      public static function load($class)
 236      {
 237          // Sanitize class name.
 238          $key = strtolower($class);
 239  
 240          // If the class already exists do nothing.
 241          if (class_exists($class, false)) {
 242              return true;
 243          }
 244  
 245          // If the class is registered include the file.
 246          if (isset(self::$classes[$key])) {
 247              $found = (bool) include_once self::$classes[$key];
 248  
 249              if ($found) {
 250                  self::loadAliasFor($class);
 251              }
 252  
 253              // If the class doesn't exists, we probably have a class alias available
 254              if (!class_exists($class, false)) {
 255                  // Search the alias class, first none namespaced and then namespaced
 256                  $original = array_search($class, self::$classAliases) ? : array_search('\\' . $class, self::$classAliases);
 257  
 258                  // When we have an original and the class exists an alias should be created
 259                  if ($original && class_exists($original, false)) {
 260                      class_alias($original, $class);
 261                  }
 262              }
 263  
 264              return true;
 265          }
 266  
 267          return false;
 268      }
 269  
 270      /**
 271       * Directly register a class to the autoload list.
 272       *
 273       * @param   string   $class  The class name to register.
 274       * @param   string   $path   Full path to the file that holds the class to register.
 275       * @param   boolean  $force  True to overwrite the autoload path value for the class if it already exists.
 276       *
 277       * @return  void
 278       *
 279       * @since       1.7.0
 280       * @deprecated  5.0   Classes should be autoloaded. Use JLoader::registerPrefix() or JLoader::registerNamespace() to register an autoloader for
 281       *                    your files.
 282       */
 283      public static function register($class, $path, $force = true)
 284      {
 285          // When an alias exists, register it as well
 286          if (array_key_exists(strtolower($class), self::$classAliases)) {
 287              self::register(self::stripFirstBackslash(self::$classAliases[strtolower($class)]), $path, $force);
 288          }
 289  
 290          // Sanitize class name.
 291          $class = strtolower($class);
 292  
 293          // Only attempt to register the class if the name and file exist.
 294          if (!empty($class) && is_file($path)) {
 295              // Register the class with the autoloader if not already registered or the force flag is set.
 296              if ($force || empty(self::$classes[$class])) {
 297                  self::$classes[$class] = $path;
 298              }
 299          }
 300      }
 301  
 302      /**
 303       * Register a class prefix with lookup path.  This will allow developers to register library
 304       * packages with different class prefixes to the system autoloader.  More than one lookup path
 305       * may be registered for the same class prefix, but if this method is called with the reset flag
 306       * set to true then any registered lookups for the given prefix will be overwritten with the current
 307       * lookup path. When loaded, prefix paths are searched in a "last in, first out" order.
 308       *
 309       * @param   string   $prefix   The class prefix to register.
 310       * @param   string   $path     Absolute file path to the library root where classes with the given prefix can be found.
 311       * @param   boolean  $reset    True to reset the prefix with only the given lookup path.
 312       * @param   boolean  $prepend  If true, push the path to the beginning of the prefix lookup paths array.
 313       *
 314       * @return  void
 315       *
 316       * @throws  RuntimeException
 317       *
 318       * @since   3.0.0
 319       */
 320      public static function registerPrefix($prefix, $path, $reset = false, $prepend = false)
 321      {
 322          // Verify the library path exists.
 323          if (!is_dir($path)) {
 324              $path = (str_replace(JPATH_ROOT, '', $path) == $path) ? basename($path) : str_replace(JPATH_ROOT, '', $path);
 325  
 326              throw new RuntimeException('Library path ' . $path . ' cannot be found.', 500);
 327          }
 328  
 329          // If the prefix is not yet registered or we have an explicit reset flag then set set the path.
 330          if ($reset || !isset(self::$prefixes[$prefix])) {
 331              self::$prefixes[$prefix] = array($path);
 332          } else {
 333              // Otherwise we want to simply add the path to the prefix.
 334              if ($prepend) {
 335                  array_unshift(self::$prefixes[$prefix], $path);
 336              } else {
 337                  self::$prefixes[$prefix][] = $path;
 338              }
 339          }
 340      }
 341  
 342      /**
 343       * Offers the ability for "just in time" usage of `class_alias()`.
 344       * You cannot overwrite an existing alias.
 345       *
 346       * @param   string          $alias     The alias name to register.
 347       * @param   string          $original  The original class to alias.
 348       * @param   string|boolean  $version   The version in which the alias will no longer be present.
 349       *
 350       * @return  boolean  True if registration was successful. False if the alias already exists.
 351       *
 352       * @since   3.2
 353       */
 354      public static function registerAlias($alias, $original, $version = false)
 355      {
 356          // PHP is case insensitive so support all kind of alias combination
 357          $lowercasedAlias = strtolower($alias);
 358  
 359          if (!isset(self::$classAliases[$lowercasedAlias])) {
 360              self::$classAliases[$lowercasedAlias] = $original;
 361  
 362              $original = self::stripFirstBackslash($original);
 363  
 364              if (!isset(self::$classAliasesInverse[$original])) {
 365                  self::$classAliasesInverse[$original] = array($lowercasedAlias);
 366              } else {
 367                  self::$classAliasesInverse[$original][] = $lowercasedAlias;
 368              }
 369  
 370              // If given a version, log this alias as deprecated
 371              if ($version) {
 372                  self::$deprecatedAliases[] = array('old' => $alias, 'new' => $original, 'version' => $version);
 373              }
 374  
 375              return true;
 376          }
 377  
 378          return false;
 379      }
 380  
 381      /**
 382       * Register a namespace to the autoloader. When loaded, namespace paths are searched in a "last in, first out" order.
 383       *
 384       * @param   string   $namespace  A case sensitive Namespace to register.
 385       * @param   string   $path       A case sensitive absolute file path to the library root where classes of the given namespace can be found.
 386       * @param   boolean  $reset      True to reset the namespace with only the given lookup path.
 387       * @param   boolean  $prepend    If true, push the path to the beginning of the namespace lookup paths array.
 388       *
 389       * @return  void
 390       *
 391       * @throws  RuntimeException
 392       *
 393       * @since   3.1.4
 394       */
 395      public static function registerNamespace($namespace, $path, $reset = false, $prepend = false)
 396      {
 397          // Verify the library path exists.
 398          if (!is_dir($path)) {
 399              $path = (str_replace(JPATH_ROOT, '', $path) == $path) ? basename($path) : str_replace(JPATH_ROOT, '', $path);
 400  
 401              throw new RuntimeException('Library path ' . $path . ' cannot be found.', 500);
 402          }
 403  
 404          // Trim leading and trailing backslashes from namespace, allowing "\Parent\Child", "Parent\Child\" and "\Parent\Child\" to be treated the same way.
 405          $namespace = trim($namespace, '\\');
 406  
 407          // If the namespace is not yet registered or we have an explicit reset flag then set the path.
 408          if ($reset || !isset(self::$namespaces[$namespace])) {
 409              self::$namespaces[$namespace] = array($path);
 410          } else {
 411              // Otherwise we want to simply add the path to the namespace.
 412              if ($prepend) {
 413                  array_unshift(self::$namespaces[$namespace], $path);
 414              } else {
 415                  self::$namespaces[$namespace][] = $path;
 416              }
 417          }
 418      }
 419  
 420      /**
 421       * Method to setup the autoloaders for the Joomla Platform.
 422       * Since the SPL autoloaders are called in a queue we will add our explicit
 423       * class-registration based loader first, then fall back on the autoloader based on conventions.
 424       * This will allow people to register a class in a specific location and override platform libraries
 425       * as was previously possible.
 426       *
 427       * @param   boolean  $enablePsr       True to enable autoloading based on PSR-0.
 428       * @param   boolean  $enablePrefixes  True to enable prefix based class loading (needed to auto load the Joomla core).
 429       * @param   boolean  $enableClasses   True to enable class map based class loading (needed to auto load the Joomla core).
 430       *
 431       * @return  void
 432       *
 433       * @since   3.1.4
 434       */
 435      public static function setup($enablePsr = true, $enablePrefixes = true, $enableClasses = true)
 436      {
 437          if ($enableClasses) {
 438              // Register the class map based autoloader.
 439              spl_autoload_register(array('JLoader', 'load'));
 440          }
 441  
 442          if ($enablePrefixes) {
 443              // Register the prefix autoloader.
 444              spl_autoload_register(array('JLoader', '_autoload'));
 445          }
 446  
 447          if ($enablePsr) {
 448              // Register the PSR based autoloader.
 449              spl_autoload_register(array('JLoader', 'loadByPsr'));
 450              spl_autoload_register(array('JLoader', 'loadByAlias'));
 451          }
 452      }
 453  
 454      /**
 455       * Method to autoload classes that are namespaced to the PSR-4 standard.
 456       *
 457       * @param   string  $class  The fully qualified class name to autoload.
 458       *
 459       * @return  boolean  True on success, false otherwise.
 460       *
 461       * @since       3.7.0
 462       * @deprecated  5.0 Use JLoader::loadByPsr instead
 463       */
 464      public static function loadByPsr4($class)
 465      {
 466          return self::loadByPsr($class);
 467      }
 468  
 469      /**
 470       * Method to autoload classes that are namespaced to the PSR-4 standard.
 471       *
 472       * @param   string  $class  The fully qualified class name to autoload.
 473       *
 474       * @return  boolean  True on success, false otherwise.
 475       *
 476       * @since   4.0.0
 477       */
 478      public static function loadByPsr($class)
 479      {
 480          $class = self::stripFirstBackslash($class);
 481  
 482          // Find the location of the last NS separator.
 483          $pos = strrpos($class, '\\');
 484  
 485          // If one is found, we're dealing with a NS'd class.
 486          if ($pos !== false) {
 487              $classPath = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR;
 488              $className = substr($class, $pos + 1);
 489          } else {
 490              // If not, no need to parse path.
 491              $classPath = null;
 492              $className = $class;
 493          }
 494  
 495          $classPath .= $className . '.php';
 496  
 497          // Loop through registered namespaces until we find a match.
 498          foreach (self::$namespaces as $ns => $paths) {
 499              if (strpos($class, "{$ns}\\") === 0) {
 500                  $nsPath = trim(str_replace('\\', DIRECTORY_SEPARATOR, $ns), DIRECTORY_SEPARATOR);
 501  
 502                  // Loop through paths registered to this namespace until we find a match.
 503                  foreach ($paths as $path) {
 504                      $classFilePath = realpath($path . DIRECTORY_SEPARATOR . substr_replace($classPath, '', 0, strlen($nsPath) + 1));
 505  
 506                      // We do not allow files outside the namespace root to be loaded
 507                      if (strpos($classFilePath, realpath($path)) !== 0) {
 508                          continue;
 509                      }
 510  
 511                      // We check for class_exists to handle case-sensitive file systems
 512                      if (is_file($classFilePath) && !class_exists($class, false)) {
 513                          $found = (bool) include_once $classFilePath;
 514  
 515                          if ($found) {
 516                              self::loadAliasFor($class);
 517                          }
 518  
 519                          return $found;
 520                      }
 521                  }
 522              }
 523          }
 524  
 525          return false;
 526      }
 527  
 528      /**
 529       * Method to autoload classes that have been aliased using the registerAlias method.
 530       *
 531       * @param   string  $class  The fully qualified class name to autoload.
 532       *
 533       * @return  boolean  True on success, false otherwise.
 534       *
 535       * @since   3.2
 536       */
 537      public static function loadByAlias($class)
 538      {
 539          $class = strtolower(self::stripFirstBackslash($class));
 540  
 541          if (isset(self::$classAliases[$class])) {
 542              // Force auto-load of the regular class
 543              class_exists(self::$classAliases[$class], true);
 544  
 545              // Normally this shouldn't execute as the autoloader will execute applyAliasFor when the regular class is
 546              // auto-loaded above.
 547              if (!class_exists($class, false) && !interface_exists($class, false)) {
 548                  class_alias(self::$classAliases[$class], $class);
 549              }
 550          }
 551      }
 552  
 553      /**
 554       * Applies a class alias for an already loaded class, if a class alias was created for it.
 555       *
 556       * @param   string  $class  We'll look for and register aliases for this (real) class name
 557       *
 558       * @return  void
 559       *
 560       * @since   3.4
 561       */
 562      public static function applyAliasFor($class)
 563      {
 564          $class = self::stripFirstBackslash($class);
 565  
 566          if (isset(self::$classAliasesInverse[$class])) {
 567              foreach (self::$classAliasesInverse[$class] as $alias) {
 568                  class_alias($class, $alias);
 569              }
 570          }
 571      }
 572  
 573      /**
 574       * Autoload a class based on name.
 575       *
 576       * @param   string  $class  The class to be loaded.
 577       *
 578       * @return  boolean  True if the class was loaded, false otherwise.
 579       *
 580       * @since   1.7.3
 581       */
 582      public static function _autoload($class)
 583      {
 584          foreach (self::$prefixes as $prefix => $lookup) {
 585              $chr = strlen($prefix) < strlen($class) ? $class[strlen($prefix)] : 0;
 586  
 587              if (strpos($class, $prefix) === 0 && ($chr === strtoupper($chr))) {
 588                  return self::_load(substr($class, strlen($prefix)), $lookup);
 589              }
 590          }
 591  
 592          return false;
 593      }
 594  
 595      /**
 596       * Load a class based on name and lookup array.
 597       *
 598       * @param   string  $class   The class to be loaded (without prefix).
 599       * @param   array   $lookup  The array of base paths to use for finding the class file.
 600       *
 601       * @return  boolean  True if the class was loaded, false otherwise.
 602       *
 603       * @since   3.0.0
 604       */
 605      private static function _load($class, $lookup)
 606      {
 607          // Split the class name into parts separated by camelCase.
 608          $parts = preg_split('/(?<=[a-z0-9])(?=[A-Z])/x', $class);
 609          $partsCount = count($parts);
 610  
 611          foreach ($lookup as $base) {
 612              // Generate the path based on the class name parts.
 613              $path = realpath($base . '/' . implode('/', array_map('strtolower', $parts)) . '.php');
 614  
 615              // Load the file if it exists and is in the lookup path.
 616              if (strpos($path, realpath($base)) === 0 && is_file($path)) {
 617                  $found = (bool) include_once $path;
 618  
 619                  if ($found) {
 620                      self::loadAliasFor($class);
 621                  }
 622  
 623                  return $found;
 624              }
 625  
 626              // Backwards compatibility patch
 627  
 628              // If there is only one part we want to duplicate that part for generating the path.
 629              if ($partsCount === 1) {
 630                  // Generate the path based on the class name parts.
 631                  $path = realpath($base . '/' . implode('/', array_map('strtolower', array($parts[0], $parts[0]))) . '.php');
 632  
 633                  // Load the file if it exists and is in the lookup path.
 634                  if (strpos($path, realpath($base)) === 0 && is_file($path)) {
 635                      $found = (bool) include_once $path;
 636  
 637                      if ($found) {
 638                          self::loadAliasFor($class);
 639                      }
 640  
 641                      return $found;
 642                  }
 643              }
 644          }
 645  
 646          return false;
 647      }
 648  
 649      /**
 650       * Loads the aliases for the given class.
 651       *
 652       * @param   string  $class  The class.
 653       *
 654       * @return  void
 655       *
 656       * @since   3.8.0
 657       */
 658      private static function loadAliasFor($class)
 659      {
 660          if (!array_key_exists($class, self::$classAliasesInverse)) {
 661              return;
 662          }
 663  
 664          foreach (self::$classAliasesInverse[$class] as $alias) {
 665              // Force auto-load of the alias class
 666              class_exists($alias, true);
 667          }
 668      }
 669  
 670      /**
 671       * Strips the first backslash from the given class if present.
 672       *
 673       * @param   string  $class  The class to strip the first prefix from.
 674       *
 675       * @return  string  The striped class name.
 676       *
 677       * @since   3.8.0
 678       */
 679      private static function stripFirstBackslash($class)
 680      {
 681          return $class && $class[0] === '\\' ? substr($class, 1) : $class;
 682      }
 683  }
 684  
 685  // Check if jexit is defined first (our unit tests mock this)
 686  if (!function_exists('jexit')) {
 687      /**
 688       * Global application exit.
 689       *
 690       * This function provides a single exit point for the platform.
 691       *
 692       * @param   mixed  $message  Exit code or string. Defaults to zero.
 693       *
 694       * @return  void
 695       *
 696       * @codeCoverageIgnore
 697       * @since   1.7.0
 698       */
 699      function jexit($message = 0)
 700      {
 701          exit($message);
 702      }
 703  }
 704  
 705  /**
 706   * Intelligent file importer.
 707   *
 708   * @param   string  $path  A dot syntax path.
 709   * @param   string  $base  Search this directory for the class.
 710   *
 711   * @return  boolean  True on success.
 712   *
 713   * @since       1.7.0
 714   * @deprecated  5.0   Classes should be autoloaded. Use JLoader::registerPrefix() or JLoader::registerNamespace() to register an autoloader for
 715   *                    your files.
 716   */
 717  function jimport($path, $base = null)
 718  {
 719      return JLoader::import($path, $base);
 720  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer