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

  • Annotation
  • AnnotationsParser
  • ClassType
  • Extension
  • GlobalFunction
  • Method
  • Parameter
  • Property

Interfaces

  • IAnnotation
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  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\Reflection;
 13: 
 14: use Nette,
 15:     Nette\Utils\Strings;
 16: 
 17: 
 18: 
 19: /**
 20:  * Annotations support for PHP.
 21:  *
 22:  * @author     David Grudl
 23:  * @Annotation
 24:  */
 25: final class AnnotationsParser
 26: {
 27:     /** @internal single & double quoted PHP string */
 28:     const RE_STRING = '\'(?:\\\\.|[^\'\\\\])*\'|"(?:\\\\.|[^"\\\\])*"';
 29: 
 30:     /** @internal identifier */
 31:     const RE_IDENTIFIER = '[_a-zA-Z\x7F-\xFF][_a-zA-Z0-9\x7F-\xFF-\\\]*';
 32: 
 33:     /** @var bool */
 34:     public static $useReflection;
 35: 
 36:     /** @var array */
 37:     public static $inherited = array('description', 'param', 'return');
 38: 
 39:     /** @var array */
 40:     private static $cache;
 41: 
 42:     /** @var array */
 43:     private static $timestamps;
 44: 
 45:     /** @var Nette\Caching\IStorage */
 46:     private static $cacheStorage;
 47: 
 48: 
 49: 
 50:     /**
 51:      * Static class - cannot be instantiated.
 52:      */
 53:     final public function __construct()
 54:     {
 55:         throw new Nette\StaticClassException;
 56:     }
 57: 
 58: 
 59: 
 60:     /**
 61:      * Returns annotations.
 62:      * @param  \ReflectionClass|\ReflectionMethod|\ReflectionProperty
 63:      * @return array
 64:      */
 65:     public static function getAll(\Reflector $r)
 66:     {
 67:         if ($r instanceof \ReflectionClass) {
 68:             $type = $r->getName();
 69:             $member = '';
 70: 
 71:         } elseif ($r instanceof \ReflectionMethod) {
 72:             $type = $r->getDeclaringClass()->getName();
 73:             $member = $r->getName();
 74: 
 75:         } else {
 76:             $type = $r->getDeclaringClass()->getName();
 77:             $member = '$' . $r->getName();
 78:         }
 79: 
 80:         if (!self::$useReflection) { // auto-expire cache
 81:             $file = $r instanceof \ReflectionClass ? $r->getFileName() : $r->getDeclaringClass()->getFileName(); // will be used later
 82:             if ($file && isset(self::$timestamps[$file]) && self::$timestamps[$file] !== filemtime($file)) {
 83:                 unset(self::$cache[$type]);
 84:             }
 85:             unset(self::$timestamps[$file]);
 86:         }
 87: 
 88:         if (isset(self::$cache[$type][$member])) { // is value cached?
 89:             return self::$cache[$type][$member];
 90:         }
 91: 
 92:         if (self::$useReflection === NULL) { // detects whether is reflection available
 93:             self::$useReflection = (bool) ClassType::from(__CLASS__)->getDocComment();
 94:         }
 95: 
 96:         if (self::$useReflection) {
 97:             $annotations = self::parseComment($r->getDocComment());
 98: 
 99:         } else {
100:             if (!self::$cacheStorage) {
101:                 // trigger_error('Set a cache storage for annotations parser via Nette\Reflection\AnnotationParser::setCacheStorage().', E_USER_WARNING);
102:                 self::$cacheStorage = new Nette\Caching\Storages\DevNullStorage;
103:             }
104:             $outerCache = new Nette\Caching\Cache(self::$cacheStorage, 'Nette.Reflection.Annotations');
105: 
106:             if (self::$cache === NULL) {
107:                 self::$cache = (array) $outerCache->offsetGet('list');
108:                 self::$timestamps = isset(self::$cache['*']) ? self::$cache['*'] : array();
109:             }
110: 
111:             if (!isset(self::$cache[$type]) && $file) {
112:                 self::$cache['*'][$file] = filemtime($file);
113:                 self::parseScript($file);
114:                 $outerCache->save('list', self::$cache);
115:             }
116: 
117:             if (isset(self::$cache[$type][$member])) {
118:                 $annotations = self::$cache[$type][$member];
119:             } else {
120:                 $annotations = array();
121:             }
122:         }
123: 
124:         if ($r instanceof \ReflectionMethod && !$r->isPrivate()
125:             && (!$r->isConstructor() || !empty($annotations['inheritdoc'][0])))
126:         {
127:             try {
128:                 $inherited = self::getAll(new \ReflectionMethod(get_parent_class($type), $member));
129:             } catch (\ReflectionException $e) {
130:                 try {
131:                     $inherited = self::getAll($r->getPrototype());
132:                 } catch (\ReflectionException $e) {
133:                     $inherited = array();
134:                 }
135:             }
136:             $annotations += array_intersect_key($inherited, array_flip(self::$inherited));
137:         }
138: 
139:         return self::$cache[$type][$member] = $annotations;
140:     }
141: 
142: 
143: 
144:     /**
145:      * Parses phpDoc comment.
146:      * @param  string
147:      * @return array
148:      */
149:     private static function parseComment($comment)
150:     {
151:         static $tokens = array('true' => TRUE, 'false' => FALSE, 'null' => NULL, '' => TRUE);
152: 
153:         $res = array();
154:         $comment = preg_replace('#^\s*\*\s?#ms', '', trim($comment, '/*'));
155:         $parts = preg_split('#^\s*(?=@'.self::RE_IDENTIFIER.')#m', $comment, 2);
156: 
157:         $description = trim($parts[0]);
158:         if ($description !== '') {
159:             $res['description'] = array($description);
160:         }
161: 
162:         $matches = Strings::matchAll(
163:             isset($parts[1]) ? $parts[1] : '',
164:             '~
165:                 (?<=\s|^)@('.self::RE_IDENTIFIER.')[ \t]*      ##  annotation
166:                 (
167:                     \((?>'.self::RE_STRING.'|[^\'")@]+)+\)|  ##  (value)
168:                     [^(@\r\n][^@\r\n]*|)                     ##  value
169:             ~xi'
170:         );
171: 
172:         foreach ($matches as $match) {
173:             list(, $name, $value) = $match;
174: 
175:             if (substr($value, 0, 1) === '(') {
176:                 $items = array();
177:                 $key = '';
178:                 $val = TRUE;
179:                 $value[0] = ',';
180:                 while ($m = Strings::match(
181:                     $value,
182:                     '#\s*,\s*(?>(' . self::RE_IDENTIFIER . ')\s*=\s*)?(' . self::RE_STRING . '|[^\'"),\s][^\'"),]*)#A')
183:                 ) {
184:                     $value = substr($value, strlen($m[0]));
185:                     list(, $key, $val) = $m;
186:                     $val = rtrim($val);
187:                     if ($val[0] === "'" || $val[0] === '"') {
188:                         $val = substr($val, 1, -1);
189: 
190:                     } elseif (is_numeric($val)) {
191:                         $val = 1 * $val;
192: 
193:                     } else {
194:                         $lval = strtolower($val);
195:                         $val = array_key_exists($lval, $tokens) ? $tokens[$lval] : $val;
196:                     }
197: 
198:                     if ($key === '') {
199:                         $items[] = $val;
200: 
201:                     } else {
202:                         $items[$key] = $val;
203:                     }
204:                 }
205: 
206:                 $value = count($items) < 2 && $key === '' ? $val : $items;
207: 
208:             } else {
209:                 $value = trim($value);
210:                 if (is_numeric($value)) {
211:                     $value = 1 * $value;
212: 
213:                 } else {
214:                     $lval = strtolower($value);
215:                     $value = array_key_exists($lval, $tokens) ? $tokens[$lval] : $value;
216:                 }
217:             }
218: 
219:             $class = $name . 'Annotation';
220:             if (class_exists($class)) {
221:                 $res[$name][] = new $class(is_array($value) ? $value : array('value' => $value));
222: 
223:             } else {
224:                 $res[$name][] = is_array($value) ? new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS) : $value;
225:             }
226:         }
227: 
228:         return $res;
229:     }
230: 
231: 
232: 
233:     /**
234:      * Parses PHP file.
235:      * @param  string
236:      * @return void
237:      */
238:     private static function parseScript($file)
239:     {
240:         $T_NAMESPACE = PHP_VERSION_ID < 50300 ? -1 : T_NAMESPACE;
241:         $T_NS_SEPARATOR = PHP_VERSION_ID < 50300 ? -1 : T_NS_SEPARATOR;
242: 
243:         $s = file_get_contents($file);
244: 
245:         if (Strings::match($s, '#//nette'.'loader=(\S*)#')) {
246:             return; // TODO: allways ignore?
247:         }
248: 
249:         $expected = $namespace = $class = $docComment = NULL;
250:         $level = $classLevel = 0;
251: 
252:         foreach (token_get_all($s) as $token) {
253: 
254:             if (is_array($token)) {
255:                 switch ($token[0]) {
256:                 case T_DOC_COMMENT:
257:                     $docComment = $token[1];
258:                 case T_WHITESPACE:
259:                 case T_COMMENT:
260:                     continue 2;
261: 
262:                 case T_STRING:
263:                 case $T_NS_SEPARATOR:
264:                 case T_VARIABLE:
265:                     if ($expected) {
266:                         $name .= $token[1];
267:                     }
268:                     continue 2;
269: 
270:                 case T_FUNCTION:
271:                 case T_VAR:
272:                 case T_PUBLIC:
273:                 case T_PROTECTED:
274:                 case $T_NAMESPACE:
275:                 case T_CLASS:
276:                 case T_INTERFACE:
277:                     $expected = $token[0];
278:                     $name = NULL;
279:                     continue 2;
280: 
281:                 case T_STATIC:
282:                 case T_ABSTRACT:
283:                 case T_FINAL:
284:                     continue 2; // ignore in expectation
285: 
286:                 case T_CURLY_OPEN:
287:                 case T_DOLLAR_OPEN_CURLY_BRACES:
288:                     $level++;
289:                 }
290:             }
291: 
292:             if ($expected) {
293:                 switch ($expected) {
294:                 case T_CLASS:
295:                 case T_INTERFACE:
296:                     $class = $namespace . $name;
297:                     $classLevel = $level;
298:                     $name = '';
299:                     // break intentionally omitted
300:                 case T_FUNCTION:
301:                     if ($token === '&') {
302:                         continue 2; // ignore
303:                     }
304:                 case T_VAR:
305:                 case T_PUBLIC:
306:                 case T_PROTECTED:
307:                     if ($class && $name !== NULL && $docComment) {
308:                         self::$cache[$class][$name] = self::parseComment($docComment);
309:                     }
310:                     break;
311: 
312:                 case $T_NAMESPACE:
313:                     $namespace = $name . '\\';
314:                 }
315: 
316:                 $expected = $docComment = NULL;
317:             }
318: 
319:             if ($token === ';') {
320:                 $docComment = NULL;
321:             } elseif ($token === '{') {
322:                 $docComment = NULL;
323:                 $level++;
324:             } elseif ($token === '}') {
325:                 $level--;
326:                 if ($level === $classLevel) {
327:                     $class = NULL;
328:                 }
329:             }
330:         }
331:     }
332: 
333: 
334: 
335:     /********************* backend ****************d*g**/
336: 
337: 
338: 
339:     /**
340:      * @return void
341:      */
342:     public static function setCacheStorage(Nette\Caching\IStorage $storage)
343:     {
344:         self::$cacheStorage = $storage;
345:     }
346: 
347: 
348: 
349:     /**
350:      * @return Nette\Caching\IStorage
351:      */
352:     public static function getCacheStorage()
353:     {
354:         return self::$cacheStorage;
355:     }
356: 
357: }
358: 
Nette Framework 2.0.7 API API documentation generated by ApiGen 2.8.0