PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Codeception/Util/Autoload.php

http://github.com/Codeception/Codeception
PHP | 148 lines | 89 code | 13 blank | 46 comment | 8 complexity | c242c30facc4ca85e6a6f68330061a44 MD5 | raw file
  1. <?php
  2. namespace Codeception\Util;
  3. /**
  4. * Autoloader, which is fully compatible with PSR-4,
  5. * and can be used to autoload your `Helper`, `Page`, and `Step` classes.
  6. */
  7. class Autoload
  8. {
  9. protected static $registered = false;
  10. /**
  11. * An associative array where the key is a namespace prefix and the value
  12. * is an array of base directories for classes in that namespace.
  13. * @var array
  14. */
  15. protected static $map = [];
  16. private function __construct()
  17. {
  18. }
  19. /**
  20. * Adds a base directory for a namespace prefix.
  21. *
  22. * Example:
  23. *
  24. * ```php
  25. * <?php
  26. * // app\Codeception\UserHelper will be loaded from '/path/to/helpers/UserHelper.php'
  27. * Autoload::addNamespace('app\Codeception', '/path/to/helpers');
  28. *
  29. * // LoginPage will be loaded from '/path/to/pageobjects/LoginPage.php'
  30. * Autoload::addNamespace('', '/path/to/pageobjects');
  31. *
  32. * Autoload::addNamespace('app\Codeception', '/path/to/controllers');
  33. * ?>
  34. * ```
  35. *
  36. * @param string $prefix The namespace prefix.
  37. * @param string $base_dir A base directory for class files in the namespace.
  38. * @param bool $prepend If true, prepend the base directory to the stack instead of appending it;
  39. * this causes it to be searched first rather than last.
  40. * @return void
  41. */
  42. public static function addNamespace($prefix, $base_dir, $prepend = false)
  43. {
  44. if (!self::$registered) {
  45. spl_autoload_register([__CLASS__, 'load']);
  46. self::$registered = true;
  47. }
  48. // normalize namespace prefix
  49. $prefix = trim($prefix, '\\') . '\\';
  50. // normalize the base directory with a trailing separator
  51. $base_dir = rtrim($base_dir, '/') . DIRECTORY_SEPARATOR;
  52. $base_dir = rtrim($base_dir, DIRECTORY_SEPARATOR) . '/';
  53. // initialize the namespace prefix array
  54. if (isset(self::$map[$prefix]) === false) {
  55. self::$map[$prefix] = [];
  56. }
  57. // retain the base directory for the namespace prefix
  58. if ($prepend) {
  59. array_unshift(self::$map[$prefix], $base_dir);
  60. } else {
  61. self::$map[$prefix][] = $base_dir;
  62. }
  63. }
  64. public static function load($class)
  65. {
  66. // the current namespace prefix
  67. $prefix = $class;
  68. // work backwards through the namespace names of the fully-qualified class name to find a mapped file name
  69. while (false !== ($pos = strrpos($prefix, '\\'))) {
  70. // retain the trailing namespace separator in the prefix
  71. $prefix = substr($class, 0, $pos + 1);
  72. // the rest is the relative class name
  73. $relative_class = substr($class, $pos + 1);
  74. // try to load a mapped file for the prefix and relative class
  75. $mapped_file = self::loadMappedFile($prefix, $relative_class);
  76. if ($mapped_file) {
  77. return $mapped_file;
  78. }
  79. // remove the trailing namespace separator for the next iteration of strrpos()
  80. $prefix = rtrim($prefix, '\\');
  81. }
  82. // fix for empty prefix
  83. if (isset(self::$map['\\']) && ($class[0] != '\\')) {
  84. return self::load('\\' . $class);
  85. }
  86. // backwards compatibility with old autoloader
  87. // :TODO: it should be removed
  88. if (strpos($class, '\\') !== false) {
  89. $relative_class = substr(strrchr($class, '\\'), 1); // Foo\Bar\ClassName -> ClassName
  90. $mapped_file = self::loadMappedFile('\\', $relative_class);
  91. if ($mapped_file) {
  92. return $mapped_file;
  93. }
  94. }
  95. return false;
  96. }
  97. /**
  98. * Load the mapped file for a namespace prefix and relative class.
  99. *
  100. * @param string $prefix The namespace prefix.
  101. * @param string $relative_class The relative class name.
  102. * @return mixed Boolean false if no mapped file can be loaded, or the name of the mapped file that was loaded.
  103. */
  104. protected static function loadMappedFile($prefix, $relative_class)
  105. {
  106. if (!isset(self::$map[$prefix])) {
  107. return false;
  108. }
  109. foreach (self::$map[$prefix] as $base_dir) {
  110. $file = $base_dir
  111. . str_replace('\\', '/', $relative_class)
  112. . '.php';
  113. // 'static' is for testing purposes
  114. if (static::requireFile($file)) {
  115. return $file;
  116. }
  117. }
  118. return false;
  119. }
  120. protected static function requireFile($file)
  121. {
  122. if (file_exists($file)) {
  123. require_once $file;
  124. return true;
  125. }
  126. return false;
  127. }
  128. }