Namespaces

  • Nette
    • Application
      • Diagnostics
      • Responses
      • Routers
      • UI
    • Caching
      • Storages
    • ComponentModel
    • Config
      • Adapters
      • Extensions
    • Database
      • Diagnostics
      • Drivers
      • Reflection
      • Table
    • DI
      • Diagnostics
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
      • Macros
    • Loaders
    • Localization
    • Mail
    • Reflection
    • Security
      • Diagnostics
    • Templating
    • Utils
      • PhpGenerator
  • NetteModule
  • None
  • PHP

Classes

  • AutoLoader
  • NetteLoader
  • RobotLoader
  • Overview
  • Namespace
  • Class
  • Tree
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (http://nette.org)
  5:  *
  6:  * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
  7:  *
  8:  * For the full copyright and license information, please view
  9:  * the file license.txt that was distributed with this source code.
 10:  */
 11: 
 12: namespace Nette\Loaders;
 13: 
 14: use Nette,
 15:     Nette\Utils\Strings,
 16:     Nette\Caching\Cache;
 17: 
 18: 
 19: 
 20: /**
 21:  * Nette auto loader is responsible for loading classes and interfaces.
 22:  *
 23:  * @author     David Grudl
 24:  *
 25:  * @property-read array $indexedClasses
 26:  * @property   Nette\Caching\IStorage $cacheStorage
 27:  */
 28: class RobotLoader extends AutoLoader
 29: {
 30:     const RETRY_LIMIT = 3;
 31: 
 32:     /** @var array */
 33:     public $scanDirs = array();
 34: 
 35:     /** @var string|array  comma separated wildcards */
 36:     public $ignoreDirs = '.*, *.old, *.bak, *.tmp, temp';
 37: 
 38:     /** @var string|array  comma separated wildcards */
 39:     public $acceptFiles = '*.php, *.php5';
 40: 
 41:     /** @var bool */
 42:     public $autoRebuild = TRUE;
 43: 
 44:     /** @var array of lowered-class => [file, mtime, class] or FALSE */
 45:     private $list = array();
 46: 
 47:     /** @var array of file => mtime */
 48:     private $files;
 49: 
 50:     /** @var bool */
 51:     private $rebuilt = FALSE;
 52: 
 53:     /** @var array of checked classes in this request */
 54:     private $checked = array();
 55: 
 56:     /** @var Nette\Caching\IStorage */
 57:     private $cacheStorage;
 58: 
 59: 
 60: 
 61:     /**
 62:      */
 63:     public function __construct()
 64:     {
 65:         if (!extension_loaded('tokenizer')) {
 66:             throw new Nette\NotSupportedException("PHP extension Tokenizer is not loaded.");
 67:         }
 68:     }
 69: 
 70: 
 71: 
 72:     /**
 73:      * Register autoloader.
 74:      * @return RobotLoader  provides a fluent interface
 75:      */
 76:     public function register()
 77:     {
 78:         $this->list = $this->getCache()->load($this->getKey(), new Nette\Callback($this, '_rebuildCallback'));
 79:         parent::register();
 80:         return $this;
 81:     }
 82: 
 83: 
 84: 
 85:     /**
 86:      * Handles autoloading of classes, interfaces or traits.
 87:      * @param  string
 88:      * @return void
 89:      */
 90:     public function tryLoad($type)
 91:     {
 92:         $type = ltrim(strtolower($type), '\\'); // PHP namespace bug #49143
 93:         $info = & $this->list[$type];
 94: 
 95:         if ($this->autoRebuild && empty($this->checked[$type]) && (is_array($info) ? !is_file($info[0]) : $info < self::RETRY_LIMIT)) {
 96:             $info = is_int($info) ? $info + 1 : 0;
 97:             $this->checked[$type] = TRUE;
 98:             if ($this->rebuilt) {
 99:                 $this->getCache()->save($this->getKey(), $this->list, array(
100:                     Cache::CONSTS => 'Nette\Framework::REVISION',
101:                 ));
102:             } else {
103:                 $this->rebuild();
104:             }
105:         }
106: 
107:         if (isset($info[0])) {
108:             Nette\Utils\LimitedScope::load($info[0], TRUE);
109: 
110:             if ($this->autoRebuild && !class_exists($type, FALSE) && !interface_exists($type, FALSE) && (PHP_VERSION_ID < 50400 || !trait_exists($type, FALSE))) {
111:                 $info = 0;
112:                 $this->checked[$type] = TRUE;
113:                 if ($this->rebuilt) {
114:                     $this->getCache()->save($this->getKey(), $this->list, array(
115:                         Cache::CONSTS => 'Nette\Framework::REVISION',
116:                     ));
117:                 } else {
118:                     $this->rebuild();
119:                 }
120:             }
121:             self::$count++;
122:         }
123:     }
124: 
125: 
126: 
127:     /**
128:      * Rebuilds class list cache.
129:      * @return void
130:      */
131:     public function rebuild()
132:     {
133:         $this->getCache()->save($this->getKey(), new Nette\Callback($this, '_rebuildCallback'));
134:         $this->rebuilt = TRUE;
135:     }
136: 
137: 
138: 
139:     /**
140:      * @internal
141:      */
142:     public function _rebuildCallback(& $dp)
143:     {
144:         foreach ($this->list as $pair) {
145:             if (is_array($pair)) {
146:                 $this->files[$pair[0]] = $pair[1];
147:             }
148:         }
149:         foreach (array_unique($this->scanDirs) as $dir) {
150:             $this->scanDirectory($dir);
151:         }
152:         $this->files = NULL;
153:         $dp = array(
154:             Cache::CONSTS => 'Nette\Framework::REVISION'
155:         );
156:         return $this->list;
157:     }
158: 
159: 
160: 
161:     /**
162:      * @return array of class => filename
163:      */
164:     public function getIndexedClasses()
165:     {
166:         $res = array();
167:         foreach ($this->list as $class => $pair) {
168:             if (is_array($pair)) {
169:                 $res[$pair[2]] = $pair[0];
170:             }
171:         }
172:         return $res;
173:     }
174: 
175: 
176: 
177:     /**
178:      * Add directory (or directories) to list.
179:      * @param  string|array
180:      * @return RobotLoader  provides a fluent interface
181:      * @throws Nette\DirectoryNotFoundException if path is not found
182:      */
183:     public function addDirectory($path)
184:     {
185:         foreach ((array) $path as $val) {
186:             $real = realpath($val);
187:             if ($real === FALSE) {
188:                 throw new Nette\DirectoryNotFoundException("Directory '$val' not found.");
189:             }
190:             $this->scanDirs[] = $real;
191:         }
192:         return $this;
193:     }
194: 
195: 
196: 
197:     /**
198:      * Add class and file name to the list.
199:      * @param  string
200:      * @param  string
201:      * @param  int
202:      * @return void
203:      */
204:     private function addClass($class, $file, $time)
205:     {
206:         $lClass = strtolower($class);
207:         if (isset($this->list[$lClass][0]) && ($file2 = $this->list[$lClass][0]) !== $file && is_file($file2)) {
208:             if ($this->files[$file2] !== filemtime($file2)) {
209:                 $this->scanScript($file2);
210:                 return $this->addClass($class, $file, $time);
211:             }
212:             $e = new Nette\InvalidStateException("Ambiguous class '$class' resolution; defined in $file and in " . $this->list[$lClass][0] . ".");
213:             {
214:                 throw $e;
215:             }
216:         }
217:         $this->list[$lClass] = array($file, $time, $class);
218:         $this->files[$file] = $time;
219:     }
220: 
221: 
222: 
223:     /**
224:      * Scan a directory for PHP files, subdirectories and 'netterobots.txt' file.
225:      * @param  string
226:      * @return void
227:      */
228:     private function scanDirectory($dir)
229:     {
230:         if (is_dir($dir)) {
231:             $ignoreDirs = is_array($this->ignoreDirs) ? $this->ignoreDirs : Strings::split($this->ignoreDirs, '#[,\s]+#');
232:             $disallow = array();
233:             foreach ($ignoreDirs as $item) {
234:                 if ($item = realpath($item)) {
235:                     $disallow[$item] = TRUE;
236:                 }
237:             }
238:             $iterator = Nette\Utils\Finder::findFiles(is_array($this->acceptFiles) ? $this->acceptFiles : Strings::split($this->acceptFiles, '#[,\s]+#'))
239:                 ->filter(function($file) use (&$disallow){
240:                     return !isset($disallow[$file->getPathname()]);
241:                 })
242:                 ->from($dir)
243:                 ->exclude($ignoreDirs)
244:                 ->filter($filter = function($dir) use (&$disallow){
245:                     $path = $dir->getPathname();
246:                     if (is_file("$path/netterobots.txt")) {
247:                         foreach (file("$path/netterobots.txt") as $s) {
248:                             if ($matches = Strings::match($s, '#^(?:disallow\\s*:)?\\s*(\\S+)#i')) {
249:                                 $disallow[$path . str_replace('/', DIRECTORY_SEPARATOR, rtrim('/' . ltrim($matches[1], '/'), '/'))] = TRUE;
250:                             }
251:                         }
252:                     }
253:                     return !isset($disallow[$path]);
254:                 });
255:             $filter(new \SplFileInfo($dir));
256:         } else {
257:             $iterator = new \ArrayIterator(array(new \SplFileInfo($dir)));
258:         }
259: 
260:         foreach ($iterator as $entry) {
261:             $path = $entry->getPathname();
262:             if (!isset($this->files[$path]) || $this->files[$path] !== $entry->getMTime()) {
263:                 $this->scanScript($path);
264:             }
265:         }
266:     }
267: 
268: 
269: 
270:     /**
271:      * Analyse PHP file.
272:      * @param  string
273:      * @return void
274:      */
275:     private function scanScript($file)
276:     {
277:         $T_NAMESPACE = PHP_VERSION_ID < 50300 ? -1 : T_NAMESPACE;
278:         $T_NS_SEPARATOR = PHP_VERSION_ID < 50300 ? -1 : T_NS_SEPARATOR;
279:         $T_TRAIT = PHP_VERSION_ID < 50400 ? -1 : T_TRAIT;
280: 
281:         $expected = FALSE;
282:         $namespace = '';
283:         $level = $minLevel = 0;
284:         $time = filemtime($file);
285:         $s = file_get_contents($file);
286: 
287:         foreach ($this->list as $class => $pair) {
288:             if (is_array($pair) && $pair[0] === $file) {
289:                 unset($this->list[$class]);
290:             }
291:         }
292: 
293:         if ($matches = Strings::match($s, '#//nette'.'loader=(\S*)#')) {
294:             foreach (explode(',', $matches[1]) as $name) {
295:                 $this->addClass($name, $file, $time);
296:             }
297:             return;
298:         }
299: 
300:         foreach (@token_get_all($s) as $token) { // intentionally @
301:             if (is_array($token)) {
302:                 switch ($token[0]) {
303:                 case T_COMMENT:
304:                 case T_DOC_COMMENT:
305:                 case T_WHITESPACE:
306:                     continue 2;
307: 
308:                 case $T_NS_SEPARATOR:
309:                 case T_STRING:
310:                     if ($expected) {
311:                         $name .= $token[1];
312:                     }
313:                     continue 2;
314: 
315:                 case $T_NAMESPACE:
316:                 case T_CLASS:
317:                 case T_INTERFACE:
318:                 case $T_TRAIT:
319:                     $expected = $token[0];
320:                     $name = '';
321:                     continue 2;
322:                 case T_CURLY_OPEN:
323:                 case T_DOLLAR_OPEN_CURLY_BRACES:
324:                     $level++;
325:                 }
326:             }
327: 
328:             if ($expected) {
329:                 switch ($expected) {
330:                 case T_CLASS:
331:                 case T_INTERFACE:
332:                 case $T_TRAIT:
333:                     if ($level === $minLevel) {
334:                         $this->addClass($namespace . $name, $file, $time);
335:                     }
336:                     break;
337: 
338:                 case $T_NAMESPACE:
339:                     $namespace = $name ? $name . '\\' : '';
340:                     $minLevel = $token === '{' ? 1 : 0;
341:                 }
342: 
343:                 $expected = NULL;
344:             }
345: 
346:             if ($token === '{') {
347:                 $level++;
348:             } elseif ($token === '}') {
349:                 $level--;
350:             }
351:         }
352:     }
353: 
354: 
355: 
356:     /********************* backend ****************d*g**/
357: 
358: 
359: 
360:     /**
361:      * @param  Nette\Caching\IStorage
362:      * @return RobotLoader
363:      */
364:     public function setCacheStorage(Nette\Caching\IStorage $storage)
365:     {
366:         $this->cacheStorage = $storage;
367:         return $this;
368:     }
369: 
370: 
371: 
372:     /**
373:      * @return Nette\Caching\IStorage
374:      */
375:     public function getCacheStorage()
376:     {
377:         return $this->cacheStorage;
378:     }
379: 
380: 
381: 
382:     /**
383:      * @return Nette\Caching\Cache
384:      */
385:     protected function getCache()
386:     {
387:         if (!$this->cacheStorage) {
388:             trigger_error('Missing cache storage.', E_USER_WARNING);
389:             $this->cacheStorage = new Nette\Caching\Storages\DevNullStorage;
390:         }
391:         return new Cache($this->cacheStorage, 'Nette.RobotLoader');
392:     }
393: 
394: 
395: 
396:     /**
397:      * @return string
398:      */
399:     protected function getKey()
400:     {
401:         return array($this->ignoreDirs, $this->acceptFiles, $this->scanDirs);
402:     }
403: 
404: }
405: 
Nette Framework 2.0.5 API API documentation generated by ApiGen 2.7.0