Packages

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

Classes

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