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
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://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:                 self::$cacheStorage = new NDevNullStorage;
 93:             }
 94:             $outerCache = new NCache(self::$cacheStorage, 'Nette.Reflection.Annotations');
 95: 
 96:             if (self::$cache === NULL) {
 97:                 self::$cache = (array) $outerCache->offsetGet('list');
 98:                 self::$timestamps = isset(self::$cache['*']) ? self::$cache['*'] : array();
 99:             }
100: 
101:             if (!isset(self::$cache[$type]) && $file) {
102:                 self::$cache['*'][$file] = filemtime($file);
103:                 self::parseScript($file);
104:                 $outerCache->save('list', self::$cache);
105:             }
106: 
107:             if (isset(self::$cache[$type][$member])) {
108:                 $annotations = self::$cache[$type][$member];
109:             } else {
110:                 $annotations = array();
111:             }
112:         }
113: 
114:         if ($r instanceof ReflectionMethod && !$r->isPrivate()
115:             && (!$r->isConstructor() || !empty($annotations['inheritdoc'][0])))
116:         {
117:             try {
118:                 $inherited = self::getAll(new ReflectionMethod(get_parent_class($type), $member));
119:             } catch (ReflectionException $e) {
120:                 try {
121:                     $inherited = self::getAll($r->getPrototype());
122:                 } catch (ReflectionException $e) {
123:                     $inherited = array();
124:                 }
125:             }
126:             $annotations += array_intersect_key($inherited, array_flip(self::$inherited));
127:         }
128: 
129:         return self::$cache[$type][$member] = $annotations;
130:     }
131: 
132: 
133:     /**
134:      * Parses phpDoc comment.
135:      * @param  string
136:      * @return array
137:      */
138:     private static function parseComment($comment)
139:     {
140:         static $tokens = array('true' => TRUE, 'false' => FALSE, 'null' => NULL, '' => TRUE);
141: 
142:         $res = array();
143:         $comment = preg_replace('#^\s*\*\s?#ms', '', trim($comment, '/*'));
144:         $parts = preg_split('#^\s*(?=@'.self::RE_IDENTIFIER.')#m', $comment, 2);
145: 
146:         $description = trim($parts[0]);
147:         if ($description !== '') {
148:             $res['description'] = array($description);
149:         }
150: 
151:         $matches = NStrings::matchAll(
152:             isset($parts[1]) ? $parts[1] : '',
153:             '~
154:                 (?<=\s|^)@('.self::RE_IDENTIFIER.')[ \t]*      ##  annotation
155:                 (
156:                     \((?>'.self::RE_STRING.'|[^\'")@]+)+\)|  ##  (value)
157:                     [^(@\r\n][^@\r\n]*|)                     ##  value
158:             ~xi'
159:         );
160: 
161:         foreach ($matches as $match) {
162:             list(, $name, $value) = $match;
163: 
164:             if (substr($value, 0, 1) === '(') {
165:                 $items = array();
166:                 $key = '';
167:                 $val = TRUE;
168:                 $value[0] = ',';
169:                 while ($m = NStrings::match(
170:                     $value,
171:                     '#\s*,\s*(?>(' . self::RE_IDENTIFIER . ')\s*=\s*)?(' . self::RE_STRING . '|[^\'"),\s][^\'"),]*)#A')
172:                 ) {
173:                     $value = substr($value, strlen($m[0]));
174:                     list(, $key, $val) = $m;
175:                     $val = rtrim($val);
176:                     if ($val[0] === "'" || $val[0] === '"') {
177:                         $val = substr($val, 1, -1);
178: 
179:                     } elseif (is_numeric($val)) {
180:                         $val = 1 * $val;
181: 
182:                     } else {
183:                         $lval = strtolower($val);
184:                         $val = array_key_exists($lval, $tokens) ? $tokens[$lval] : $val;
185:                     }
186: 
187:                     if ($key === '') {
188:                         $items[] = $val;
189: 
190:                     } else {
191:                         $items[$key] = $val;
192:                     }
193:                 }
194: 
195:                 $value = count($items) < 2 && $key === '' ? $val : $items;
196: 
197:             } else {
198:                 $value = trim($value);
199:                 if (is_numeric($value)) {
200:                     $value = 1 * $value;
201: 
202:                 } else {
203:                     $lval = strtolower($value);
204:                     $value = array_key_exists($lval, $tokens) ? $tokens[$lval] : $value;
205:                 }
206:             }
207: 
208:             $class = $name . 'Annotation';
209:             if (class_exists($class)) {
210:                 $res[$name][] = new $class(is_array($value) ? $value : array('value' => $value));
211: 
212:             } else {
213:                 $res[$name][] = is_array($value) ? new ArrayObject($value, ArrayObject::ARRAY_AS_PROPS) : $value;
214:             }
215:         }
216: 
217:         return $res;
218:     }
219: 
220: 
221:     /**
222:      * Parses PHP file.
223:      * @param  string
224:      * @return void
225:      */
226:     private static function parseScript($file)
227:     {
228:         $T_NAMESPACE = PHP_VERSION_ID < 50300 ? -1 : T_NAMESPACE;
229:         $T_NS_SEPARATOR = PHP_VERSION_ID < 50300 ? -1 : T_NS_SEPARATOR;
230: 
231:         $s = file_get_contents($file);
232: 
233:         if (NStrings::match($s, '#//nette'.'loader=(\S*)#')) {
234:             return;
235:         }
236: 
237:         $expected = $namespace = $class = $docComment = NULL;
238:         $level = $classLevel = 0;
239: 
240:         foreach (token_get_all($s) as $token) {
241: 
242:             if (is_array($token)) {
243:                 switch ($token[0]) {
244:                     case T_DOC_COMMENT:
245:                         $docComment = $token[1];
246:                         // break intentionally omitted
247:                     case T_WHITESPACE:
248:                     case T_COMMENT:
249:                         continue 2;
250: 
251:                     case T_STRING:
252:                     case $T_NS_SEPARATOR:
253:                     case T_VARIABLE:
254:                         if ($expected) {
255:                             $name .= $token[1];
256:                         }
257:                         continue 2;
258: 
259:                     case T_FUNCTION:
260:                     case T_VAR:
261:                     case T_PUBLIC:
262:                     case T_PROTECTED:
263:                     case $T_NAMESPACE:
264:                     case T_CLASS:
265:                     case T_INTERFACE:
266:                         $expected = $token[0];
267:                         $name = NULL;
268:                         continue 2;
269: 
270:                     case T_STATIC:
271:                     case T_ABSTRACT:
272:                     case T_FINAL:
273:                         continue 2; // ignore in expectation
274: 
275:                     case T_CURLY_OPEN:
276:                     case T_DOLLAR_OPEN_CURLY_BRACES:
277:                         $level++;
278:                 }
279:             }
280: 
281:             if ($expected) {
282:                 switch ($expected) {
283:                     case T_CLASS:
284:                     case T_INTERFACE:
285:                         $class = $namespace . $name;
286:                         $classLevel = $level;
287:                         $name = '';
288:                         // break intentionally omitted
289:                     case T_FUNCTION:
290:                         if ($token === '&') {
291:                             continue 2; // ignore
292:                         }
293:                     case T_VAR:
294:                     case T_PUBLIC:
295:                     case T_PROTECTED:
296:                         if ($class && $name !== NULL && $docComment) {
297:                             self::$cache[$class][$name] = self::parseComment($docComment);
298:                         }
299:                         break;
300: 
301:                     case $T_NAMESPACE:
302:                         $namespace = $name . '\\';
303:                 }
304: 
305:                 $expected = $docComment = NULL;
306:             }
307: 
308:             if ($token === ';') {
309:                 $docComment = NULL;
310:             } elseif ($token === '{') {
311:                 $docComment = NULL;
312:                 $level++;
313:             } elseif ($token === '}') {
314:                 $level--;
315:                 if ($level === $classLevel) {
316:                     $class = NULL;
317:                 }
318:             }
319:         }
320:     }
321: 
322: 
323:     /********************* backend ****************d*g**/
324: 
325: 
326:     /**
327:      * @return void
328:      */
329:     public static function setCacheStorage(ICacheStorage $storage)
330:     {
331:         self::$cacheStorage = $storage;
332:     }
333: 
334: 
335:     /**
336:      * @return ICacheStorage
337:      */
338:     public static function getCacheStorage()
339:     {
340:         return self::$cacheStorage;
341:     }
342: 
343: }
344: 
Nette Framework 2.0.18 (for PHP 5.2, prefixed) API documentation generated by ApiGen 2.8.0