Packages

  • 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
  • ClassReflection
  • ExtensionReflection
  • FunctionReflection
  • MethodReflection
  • ParameterReflection
  • PropertyReflection

Interfaces

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