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