Packages

  • Nette
    • Application
    • Caching
    • Collections
    • Config
    • Forms
    • IO
    • Loaders
    • Mail
    • Reflection
    • Security
    • Templates
    • Web
  • None
  • PHP

Classes

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