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