PageRenderTime 29ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/environment/classes/AutoLoader.class.php

https://github.com/cj/Project-Pier
PHP | 300 lines | 184 code | 22 blank | 94 comment | 27 complexity | 0eafc30e203609e92551355f6690315a MD5 | raw file
  1. <?php
  2. /**
  3. * Fancy class loader which uses an index
  4. *
  5. * This class is based on SmartLoader
  6. *
  7. * @package Environment
  8. */
  9. class AutoLoader {
  10. /**
  11. * mod settings for the index file
  12. */
  13. const INDEX_FILE_MOD = 0775;
  14. /**
  15. * Name of the $GLOBALS var where we'll store class names
  16. *
  17. * @var string
  18. */
  19. const GLOBAL_VAR = 'autoloader_classes';
  20. /**
  21. * Filename of index file
  22. *
  23. * @var string
  24. */
  25. private $index_filename = 'autoloader_index.php';
  26. /**
  27. * Array of directory paths that need to be parsed
  28. *
  29. * @var array
  30. */
  31. private $parse_directories = array();
  32. /**
  33. * Extension of files that need to be scaned
  34. *
  35. * @var string
  36. */
  37. private $scan_file_extension = 'class.php';
  38. /**
  39. * Ignore hidden and system files
  40. *
  41. * @var boolean
  42. */
  43. private $ignore_hidden_files = true;
  44. /**
  45. * Cached array of parsed directories, keep it from endless loop
  46. *
  47. * @var array
  48. */
  49. private $parsed_directories = array();
  50. /**
  51. * Class index
  52. *
  53. * @var array
  54. */
  55. private $class_index = array();
  56. // ---------------------------------------------------
  57. // Doers
  58. // ---------------------------------------------------
  59. /**
  60. * Loads a class by its name
  61. *
  62. * @access public
  63. * @param string $class_name
  64. * @throws Exception
  65. * @return boolean Success
  66. */
  67. public function loadClass($load_class_name) {
  68. static $retrying = false; // is this our second loading attempt?
  69. $class_name = strtoupper($load_class_name);
  70. /* Recreate the index file, if outdated */
  71. if (!isset($GLOBALS[self::GLOBAL_VAR])) {
  72. if ($retrying || !is_readable($this->getIndexFilename())) {
  73. $this->createCache();
  74. if (!is_readable($this->getIndexFilename())) {
  75. throw new Exception('SmartLoader index file "'.$this->indexFilename.'" is not readable!');
  76. } // if
  77. } // if
  78. include $this->getIndexFilename();
  79. } // if
  80. /* include the needed file or retry on failure */
  81. if (isset($GLOBALS[self::GLOBAL_VAR][$class_name])) {
  82. if (@include($GLOBALS[self::GLOBAL_VAR][$class_name])) {
  83. return true;
  84. } else {
  85. if ($retrying) {
  86. throw new Exception('Class file "' . $GLOBALS[self::GLOBAL_VAR][$class_name] . '" for class "'.$class_name.'" cannot be included!');
  87. } // if
  88. } // if
  89. } elseif ($retrying) {
  90. /* we failed while retrying. this is bad. */
  91. throw new Exception('Could not find class file for "'.$class_name.'"');
  92. } // if
  93. /* including failed. try again. */
  94. unset($GLOBALS[self::GLOBAL_VAR]);
  95. $retrying = true;
  96. return $this->loadClass($class_name);
  97. } // loadClass
  98. /**
  99. * - Scans the class dirs for class/interface definitions and
  100. * creates an associative array (class name => class file)
  101. * - Generates the array in PHP code and saves it as index file
  102. *
  103. * @access private
  104. * @param param_type $param_name
  105. * @throws Exception
  106. */
  107. private function createCache() {
  108. if (is_array($this->parse_directories) && count($this->parse_directories)) {
  109. foreach ($this->parse_directories as $dir) {
  110. $this->parseDir($dir);
  111. }
  112. } // if
  113. $this->createIndexFile();
  114. } // createCache
  115. /**
  116. * Write out to the index file
  117. *
  118. * @access private
  119. * @throws Exception
  120. */
  121. private function createIndexFile() {
  122. /* generate php index file */
  123. $index_content = "<?php\n";
  124. foreach ($this->class_index as $class_name => $class_file) {
  125. $index_content .= "\t\$GLOBALS['autoloader_classes'][". var_export(strtoupper($class_name), true) . "] = " . var_export($class_file, true) . ";\n";
  126. } // foreach
  127. $index_content .= "?>";
  128. if (!@file_put_contents($this->getIndexFilename(), $index_content)) {
  129. throw new Exception('Could not write to "'.$this->getIndexFilename().'". Make sure, that your webserver has write access to it.');
  130. } // if
  131. /* Apply mod rights */
  132. @chmod($this->getIndexFilename(), self::INDEX_FILE_MOD );
  133. } // createIndexFile
  134. /**
  135. * Parses a directory for class/interface definitions. Saves found definitions
  136. * in $classIndex
  137. *
  138. * @access private
  139. * @param string $directory_path
  140. * @throws Exception
  141. * @return boolean Success
  142. */
  143. private function parseDir($directory_path) {
  144. $directory_path = with_slash($directory_path);
  145. if (in_array($directory_path, $this->parsed_directories)) {
  146. return;
  147. } else {
  148. $this->parsed_directories[] = $directory_path;
  149. } // if
  150. $dir = dir($directory_path);
  151. while (false !== ($entry = $dir->read())) {
  152. if ($entry == '.' || $entry == '..') {
  153. continue;
  154. } // if
  155. $path = $directory_path . $entry;
  156. if (is_dir($path)) {
  157. if ($this->getIgnoreHiddenFiles() && ($entry[0] == '.')) {
  158. continue;
  159. } // if
  160. if (!is_readable($path)) {
  161. continue;
  162. } // if
  163. $this->parseDir($path);
  164. } elseif (is_file($path)) {
  165. if (!is_readable($path)) {
  166. continue;
  167. } // if
  168. if (str_ends_with($path, $this->getScanFileExtension())) {
  169. $this->parseFile($path);
  170. } // if
  171. } // if
  172. } // if
  173. $dir->close();
  174. } // parseDir
  175. /**
  176. * Parse a file for PHP classes and add them to our classIndex
  177. *
  178. * @access private
  179. * @param string path to file
  180. * @throws Exception
  181. */
  182. private function parseFile($path) {
  183. if (!$buf = @file_get_contents($path)) {
  184. throw new Exception('Couldn\'t read file contents from "'.$path.'".');
  185. } // if
  186. /* searching for classes */
  187. if (preg_match_all("%(interface|class)\s+(\w+)\s+(extends\s+(\w+)\s+)?(implements\s+\w+\s*(,\s*\w+\s*)*)?{%im", $buf, $result)) {
  188. foreach ($result[2] as $class_name) {
  189. $this->class_index[$class_name] = str_replace('\\', '/', $path);
  190. } // if
  191. } // if
  192. } // parseFile
  193. // ---------------------------------------------------
  194. // Getters and setters
  195. // ---------------------------------------------------
  196. /**
  197. * Add directory that need to be scaned
  198. *
  199. * @param stirng $path Direcotry path
  200. * @return null
  201. */
  202. function addDir($path) {
  203. if (is_dir($path)) {
  204. $this->parse_directories[] = $path;
  205. } // if
  206. } // addDir
  207. /**
  208. * Get index_filename
  209. *
  210. * @access public
  211. * @param null
  212. * @return string
  213. */
  214. function getIndexFilename() {
  215. return $this->index_filename;
  216. } // getIndexFilename
  217. /**
  218. * Set index_filename value
  219. *
  220. * @access public
  221. * @param string $value
  222. * @return null
  223. */
  224. function setIndexFilename($value) {
  225. $this->index_filename = $value;
  226. } // setIndexFilename
  227. /**
  228. * Get scan_file_extension
  229. *
  230. * @access public
  231. * @param null
  232. * @return string
  233. */
  234. function getScanFileExtension() {
  235. return $this->scan_file_extension;
  236. } // getScanFileExtension
  237. /**
  238. * Set scan_file_extension value
  239. *
  240. * @access public
  241. * @param string $value
  242. * @return null
  243. */
  244. function setScanFileExtension($value) {
  245. $this->scan_file_extension = $value;
  246. } // setScanFileExtension
  247. /**
  248. * Get ignore_hidden_files
  249. *
  250. * @access public
  251. * @param null
  252. * @return boolean
  253. */
  254. function getIgnoreHiddenFiles() {
  255. return $this->ignore_hidden_files;
  256. } // getIgnoreHiddenFiles
  257. /**
  258. * Set ignore_hidden_files value
  259. *
  260. * @access public
  261. * @param boolean $value
  262. * @return null
  263. */
  264. function setIgnoreHiddenFiles($value) {
  265. $this->ignore_hidden_files = $value;
  266. } // setIgnoreHiddenFiles
  267. } // AutoLoader
  268. ?>