No Description

Autoloader.php 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. <?php
  2. class LtAutoloader
  3. {
  4. public $conf = array(
  5. /**
  6. * 是否自动加载定义了函数的文件
  7. *
  8. * 可选项:
  9. * # true 自动加载
  10. * # false 跳过函数,只自动加载定义了class或者interface的文件
  11. */
  12. "load_function" => true,
  13. /**
  14. * 要扫描的文件类型
  15. *
  16. * 若该属性设置为array("php","inc","php3"),
  17. * 则扩展名为"php","inc","php3"的文件会被扫描,
  18. * 其它扩展名的文件会被忽略
  19. */
  20. "allow_file_extension" => array("php", "inc"),
  21. /**
  22. * 不扫描的目录
  23. *
  24. * 若该属性设置为array(".svn", ".setting"),
  25. * 则所有名为".setting"的目录也会被忽略
  26. */
  27. "skip_dir_names" => array(".svn"),
  28. );
  29. public $storeHandle;
  30. public $autoloadPath;
  31. protected $functionFileMapping;
  32. protected $fileStore;
  33. public function init()
  34. {
  35. if (!is_object($this->storeHandle))
  36. {
  37. $this->storeHandle = new LtStoreMemory;
  38. $this->fileStore = new LtStoreFile;
  39. $this->fileStore->prefix = 'LtAutoloader-token-cache';
  40. $this->fileStore->useSerialize = true;
  41. $this->fileStore->init();
  42. }
  43. // Whether scanning directory
  44. if (0 == $this->storeHandle->get(".class_total") && 0 == $this->storeHandle->get(".function_total"))
  45. {
  46. $this->storeHandle->add(".class_total", 0);
  47. $this->storeHandle->add(".function_total", 0);
  48. $this->storeHandle->add(".functions", array(), 0);
  49. $autoloadPath = $this->preparePath($this->autoloadPath);
  50. foreach($autoloadPath as $key => $path)
  51. {
  52. if (is_file($path))
  53. {
  54. $this->addFileMap($path);
  55. unset($autoloadPath[$key]);
  56. }
  57. }
  58. $this->scanDirs($autoloadPath);
  59. unset($autoloadPath);
  60. }
  61. // Whether loading function files
  62. if ($this->conf["load_function"])
  63. {
  64. $this->loadFunction();
  65. }
  66. spl_autoload_register(array($this, "loadClass"));
  67. }
  68. public function loadFunction()
  69. {
  70. if ($functionFiles = $this->storeHandle->get(".functions"))
  71. {
  72. foreach ($functionFiles as $functionFile)
  73. {
  74. include($functionFile);
  75. }
  76. }
  77. }
  78. public function loadClass($className)
  79. {
  80. if ($classFile = $this->storeHandle->get(strtolower($className)))
  81. {
  82. include($classFile);
  83. }
  84. }
  85. protected function convertPath($path)
  86. {
  87. $path = str_replace("\\", "/", $path);
  88. if (!is_readable($path))
  89. {
  90. trigger_error("Directory is not exists/readable: {$path}");
  91. return false;
  92. }
  93. $path = rtrim(realpath($path), '\\/');
  94. if ("WINNT" != PHP_OS && preg_match("/\s/i", $path))
  95. {
  96. trigger_error("Directory contains space/tab/newline is not supported: {$path}");
  97. return false;
  98. }
  99. return $path;
  100. }
  101. /**
  102. * The string or an Multidimensional array into a one-dimensional array
  103. *
  104. * @param array $ or string $var
  105. * @return array one-dimensional array
  106. */
  107. protected function preparePath($var)
  108. {
  109. $ret = array();
  110. if (!is_array($var))
  111. {
  112. $var = array($var);
  113. }
  114. $i = 0;
  115. while (isset($var[$i]))
  116. {
  117. if (!is_array($var[$i]) && $path = $this->convertPath($var[$i]))
  118. {
  119. $ret[] = $path;
  120. }
  121. else
  122. {
  123. foreach($var[$i] as $v)
  124. {
  125. $var[] = $v;
  126. }
  127. }
  128. unset($var[$i]);
  129. $i ++;
  130. }
  131. return $ret;
  132. }
  133. /**
  134. * Using iterative algorithm scanning subdirectories
  135. * save autoloader filemap
  136. *
  137. * @param array $dirs one-dimensional
  138. * @return
  139. */
  140. protected function scanDirs($dirs)
  141. {
  142. $i = 0;
  143. while (isset($dirs[$i]))
  144. {
  145. $dir = $dirs[$i];
  146. $files = scandir($dir);
  147. foreach ($files as $file)
  148. {
  149. if (in_array($file, array(".", "..")) || in_array($file, $this->conf["skip_dir_names"]))
  150. {
  151. continue;
  152. }
  153. $currentFile = $dir . DIRECTORY_SEPARATOR . $file;
  154. if (is_file($currentFile))
  155. {
  156. $this->addFileMap($currentFile);
  157. }
  158. else if (is_dir($currentFile))
  159. {
  160. // if $currentFile is a directory, pass through the next loop.
  161. $dirs[] = $currentFile;
  162. }
  163. else
  164. {
  165. trigger_error("$currentFile is not a file or a directory.");
  166. }
  167. } //end foreach
  168. unset($dirs[$i]);
  169. $i ++;
  170. } //end while
  171. }
  172. protected function parseLibNames($src)
  173. {
  174. $libNames = array();
  175. $tokens = token_get_all($src);
  176. $level = 0;
  177. $found = false;
  178. $name = '';
  179. foreach ($tokens as $token)
  180. {
  181. if (is_string($token))
  182. {
  183. if ('{' == $token)
  184. {
  185. $level ++;
  186. }
  187. else if ('}' == $token)
  188. {
  189. $level --;
  190. }
  191. }
  192. else
  193. {
  194. list($id, $text) = $token;
  195. if (T_CURLY_OPEN == $id || T_DOLLAR_OPEN_CURLY_BRACES == $id)
  196. {
  197. $level ++;
  198. }
  199. if (0 < $level)
  200. {
  201. continue;
  202. }
  203. switch ($id)
  204. {
  205. case T_STRING:
  206. if ($found)
  207. {
  208. $libNames[strtolower($name)][] = $text;
  209. $found = false;
  210. }
  211. break;
  212. case T_CLASS:
  213. case T_INTERFACE:
  214. case T_FUNCTION:
  215. $found = true;
  216. $name = $text;
  217. break;
  218. }
  219. }
  220. }
  221. return $libNames;
  222. }
  223. protected function addClass($className, $file)
  224. {
  225. $key = strtolower($className);
  226. if ($existedClassFile = $this->storeHandle->get($key))
  227. {
  228. trigger_error("duplicate class [$className] found in:\n$existedClassFile\n$file\n");
  229. return false;
  230. }
  231. else
  232. {
  233. $this->storeHandle->add($key, $file);
  234. $this->storeHandle->update(".class_total", $this->storeHandle->get(".class_total") + 1);
  235. return true;
  236. }
  237. }
  238. protected function addFunction($functionName, $file)
  239. {
  240. $functionName = strtolower($functionName);
  241. if (isset($this->functionFileMapping[$functionName]))
  242. {
  243. $existedFunctionFile = $this->functionFileMapping[$functionName];
  244. trigger_error("duplicate function [$functionName] found in:\n$existedFunctionFile\n$file\n");
  245. return false;
  246. }
  247. else
  248. {
  249. $this->functionFileMapping[$functionName] = $file;
  250. $this->storeHandle->update(".functions", array_unique(array_values($this->functionFileMapping)));
  251. $this->storeHandle->update(".function_total", count($this->functionFileMapping));
  252. return true;
  253. }
  254. }
  255. protected function addFileMap($file)
  256. {
  257. if (!in_array(pathinfo($file, PATHINFO_EXTENSION), $this->conf["allow_file_extension"]))
  258. {
  259. return false;
  260. }
  261. $libNames = array();
  262. if ($this->fileStore instanceof LtStore)
  263. {
  264. $cachedFileLastModified = (int) @filemtime($this->fileStore->getFilePath($file));
  265. if ($cachedFileLastModified < filemtime($file) || !is_array(($libNames = $this->fileStore->get($file))))
  266. {
  267. $libNames = $this->parseLibNames(trim(file_get_contents($file)));
  268. if (0 < $cachedFileLastModified)
  269. {
  270. $this->fileStore->update($file, $libNames);
  271. }
  272. else
  273. {
  274. $this->fileStore->add($file, $libNames);
  275. }
  276. }
  277. }
  278. else
  279. {
  280. $libNames = $this->parseLibNames(trim(file_get_contents($file)));
  281. }
  282. foreach ($libNames as $libType => $libArray)
  283. {
  284. $method = "function" == $libType ? "addFunction" : "addClass";
  285. foreach ($libArray as $libName)
  286. {
  287. $this->$method($libName, $file);
  288. }
  289. }
  290. return true;
  291. }
  292. }