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

Classes

Interfaces

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