PageRenderTime 53ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/www/libs/nette-dev/Loaders/RobotLoader.php

https://github.com/bazo/Mokuji
PHP | 391 lines | 235 code | 76 blank | 80 comment | 45 complexity | 529a42721645c967cf8373cb1dfea9a2 MD5 | raw file
Possible License(s): BSD-3-Clause, MIT
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * @copyright Copyright (c) 2004, 2010 David Grudl
  6. * @license http://nettephp.com/license Nette license
  7. * @link http://nettephp.com
  8. * @category Nette
  9. * @package Nette\Loaders
  10. */
  11. /**
  12. * Nette auto loader is responsible for loading classes and interfaces.
  13. *
  14. * @copyright Copyright (c) 2004, 2010 David Grudl
  15. * @package Nette\Loaders
  16. */
  17. class RobotLoader extends AutoLoader
  18. {
  19. /** @var array */
  20. public $scanDirs;
  21. /** @var string comma separated wildcards */
  22. public $ignoreDirs = '.*, *.old, *.bak, *.tmp, temp';
  23. /** @var string comma separated wildcards */
  24. public $acceptFiles = '*.php, *.php5';
  25. /** @var bool */
  26. public $autoRebuild;
  27. /** @var array */
  28. private $list = array();
  29. /** @var array */
  30. private $files;
  31. /** @var bool */
  32. private $rebuilded = FALSE;
  33. /** @var string */
  34. private $acceptMask;
  35. /** @var string */
  36. private $ignoreMask;
  37. /**
  38. */
  39. public function __construct()
  40. {
  41. if (!extension_loaded('tokenizer')) {
  42. throw new Exception("PHP extension Tokenizer is not loaded.");
  43. }
  44. }
  45. /**
  46. * Register autoloader.
  47. * @return void
  48. */
  49. public function register()
  50. {
  51. $cache = $this->getCache();
  52. $key = $this->getKey();
  53. if (isset($cache[$key])) {
  54. $this->list = $cache[$key];
  55. } else {
  56. $this->rebuild();
  57. }
  58. if (isset($this->list[strtolower(__CLASS__)]) && class_exists('NetteLoader', FALSE)) {
  59. NetteLoader::getInstance()->unregister();
  60. }
  61. parent::register();
  62. }
  63. /**
  64. * Handles autoloading of classes or interfaces.
  65. * @param string
  66. * @return void
  67. */
  68. public function tryLoad($type)
  69. {
  70. $type = strtolower($type);
  71. if (isset($this->list[$type])) {
  72. if ($this->list[$type] !== FALSE) {
  73. LimitedScope::load($this->list[$type][0]);
  74. self::$count++;
  75. }
  76. } else {
  77. $this->list[$type] = FALSE;
  78. if ($this->autoRebuild === NULL) {
  79. $this->autoRebuild = !$this->isProduction();
  80. }
  81. if ($this->autoRebuild) {
  82. if ($this->rebuilded) {
  83. $this->getCache()->save($this->getKey(), $this->list);
  84. } else {
  85. $this->rebuild();
  86. }
  87. }
  88. if ($this->list[$type] !== FALSE) {
  89. LimitedScope::load($this->list[$type][0]);
  90. self::$count++;
  91. }
  92. }
  93. }
  94. /**
  95. * Rebuilds class list cache.
  96. * @return void
  97. */
  98. public function rebuild()
  99. {
  100. $this->getCache()->save($this->getKey(), callback($this, '_rebuildCallback'));
  101. $this->rebuilded = TRUE;
  102. }
  103. /**
  104. * @ignore internal
  105. */
  106. public function _rebuildCallback()
  107. {
  108. $this->acceptMask = self::wildcards2re($this->acceptFiles);
  109. $this->ignoreMask = self::wildcards2re($this->ignoreDirs);
  110. foreach ($this->list as $pair) {
  111. if ($pair) $this->files[$pair[0]] = $pair[1];
  112. }
  113. foreach (array_unique($this->scanDirs) as $dir) {
  114. $this->scanDirectory($dir);
  115. }
  116. $this->files = NULL;
  117. return $this->list;
  118. }
  119. /**
  120. * @return array of class => filename
  121. */
  122. public function getIndexedClasses()
  123. {
  124. $res = array();
  125. foreach ($this->list as $class => $pair) {
  126. if ($pair) $res[$class] = $pair[0];
  127. }
  128. return $res;
  129. }
  130. /**
  131. * Add directory (or directories) to list.
  132. * @param string|array
  133. * @return void
  134. * @throws DirectoryNotFoundException if path is not found
  135. */
  136. public function addDirectory($path)
  137. {
  138. foreach ((array) $path as $val) {
  139. $real = realpath($val);
  140. if ($real === FALSE) {
  141. throw new DirectoryNotFoundException("Directory '$val' not found.");
  142. }
  143. $this->scanDirs[] = $real;
  144. }
  145. }
  146. /**
  147. * Add class and file name to the list.
  148. * @param string
  149. * @param string
  150. * @param int
  151. * @return void
  152. */
  153. private function addClass($class, $file, $time)
  154. {
  155. $class = strtolower($class);
  156. if (!empty($this->list[$class]) && $this->list[$class][0] !== $file) {
  157. spl_autoload_call($class); // hack: enables exceptions
  158. throw new InvalidStateException("Ambiguous class '$class' resolution; defined in $file and in " . $this->list[$class][0] . ".");
  159. }
  160. $this->list[$class] = array($file, $time);
  161. }
  162. /**
  163. * Scan a directory for PHP files, subdirectories and 'netterobots.txt' file.
  164. * @param string
  165. * @return void
  166. */
  167. private function scanDirectory($dir)
  168. {
  169. if (is_file($dir)) {
  170. if (!isset($this->files[$dir]) || $this->files[$dir] !== filemtime($dir)) {
  171. $this->scanScript($dir);
  172. }
  173. return;
  174. }
  175. $iterator = dir($dir);
  176. if (!$iterator) return;
  177. $disallow = array();
  178. if (is_file($dir . '/netterobots.txt')) {
  179. foreach (file($dir . '/netterobots.txt') as $s) {
  180. if (preg_match('#^disallow\\s*:\\s*(\\S+)#i', $s, $m)) {
  181. $disallow[trim($m[1], '/')] = TRUE;
  182. }
  183. }
  184. if (isset($disallow[''])) return;
  185. }
  186. while (FALSE !== ($entry = $iterator->read())) {
  187. if ($entry == '.' || $entry == '..' || isset($disallow[$entry])) continue;
  188. $path = $dir . DIRECTORY_SEPARATOR . $entry;
  189. // process subdirectories
  190. if (is_dir($path)) {
  191. // check ignore mask
  192. if (!preg_match($this->ignoreMask, $entry)) {
  193. $this->scanDirectory($path);
  194. }
  195. continue;
  196. }
  197. if (is_file($path) && preg_match($this->acceptMask, $entry)) {
  198. if (!isset($this->files[$path]) || $this->files[$path] !== filemtime($path)) {
  199. $this->scanScript($path);
  200. }
  201. }
  202. }
  203. $iterator->close();
  204. }
  205. /**
  206. * Analyse PHP file.
  207. * @param string
  208. * @return void
  209. */
  210. private function scanScript($file)
  211. {
  212. if (!defined('T_NAMESPACE')) {
  213. define('T_NAMESPACE', -1);
  214. define('T_NS_SEPARATOR', -1);
  215. }
  216. $expected = FALSE;
  217. $namespace = '';
  218. $level = 0;
  219. $time = filemtime($file);
  220. $s = file_get_contents($file);
  221. if (preg_match('#//nette'.'loader=(\S*)#', $s, $matches)) {
  222. foreach (explode(',', $matches[1]) as $name) {
  223. $this->addClass($name, $file, $time);
  224. }
  225. return;
  226. }
  227. foreach (token_get_all($s) as $token)
  228. {
  229. if (is_array($token)) {
  230. switch ($token[0]) {
  231. case T_COMMENT:
  232. case T_DOC_COMMENT:
  233. case T_WHITESPACE:
  234. continue 2;
  235. case T_NS_SEPARATOR:
  236. case T_STRING:
  237. if ($expected) {
  238. $name .= $token[1];
  239. }
  240. continue 2;
  241. case T_NAMESPACE:
  242. case T_CLASS:
  243. case T_INTERFACE:
  244. $expected = $token[0];
  245. $name = '';
  246. continue 2;
  247. case T_CURLY_OPEN:
  248. case T_DOLLAR_OPEN_CURLY_BRACES:
  249. $level++;
  250. }
  251. }
  252. if ($expected) {
  253. switch ($expected) {
  254. case T_CLASS:
  255. case T_INTERFACE:
  256. if ($level === 0) {
  257. $this->addClass($namespace . $name, $file, $time);
  258. }
  259. break;
  260. case T_NAMESPACE:
  261. $namespace = $name . '\\';
  262. }
  263. $expected = NULL;
  264. }
  265. if ($token === '{') {
  266. $level++;
  267. } elseif ($token === '}') {
  268. $level--;
  269. }
  270. }
  271. }
  272. /**
  273. * Converts comma separated wildcards to regular expression.
  274. * @param string
  275. * @return string
  276. */
  277. private static function wildcards2re($wildcards)
  278. {
  279. $mask = array();
  280. foreach (explode(',', $wildcards) as $wildcard) {
  281. $wildcard = trim($wildcard);
  282. $wildcard = addcslashes($wildcard, '.\\+[^]$(){}=!><|:#');
  283. $wildcard = strtr($wildcard, array('*' => '.*', '?' => '.'));
  284. $mask[] = $wildcard;
  285. }
  286. return '#^(' . implode('|', $mask) . ')$#i';
  287. }
  288. /********************* backend ****************d*g**/
  289. /**
  290. * @return Cache
  291. */
  292. protected function getCache()
  293. {
  294. return Environment::getCache('Nette.RobotLoader');
  295. }
  296. /**
  297. * @return string
  298. */
  299. protected function getKey()
  300. {
  301. return md5("v2|$this->ignoreDirs|$this->acceptFiles|" . implode('|', $this->scanDirs));
  302. }
  303. /**
  304. * @return bool
  305. */
  306. protected function isProduction()
  307. {
  308. return Environment::isProduction();
  309. }
  310. }