Namespaces

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