Namespaces

  • Nette
    • Application
    • Caching
    • Collections
    • Config
    • Forms
    • IO
    • Loaders
    • Mail
    • Reflection
    • Security
    • Templates
    • Web
  • None
  • PHP

Classes

  • Annotation
  • AnnotationsParser
  • ClassReflection
  • ExtensionReflection
  • FunctionReflection
  • MethodReflection
  • ParameterReflection
  • PropertyReflection

Interfaces

  • IAnnotation
  • Overview
  • Namespace
  • Class
  • Tree
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://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: 
 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 PHP 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:     private static $cache;
 37: 
 38:     /** @var array */
 39:     private static $timestamps;
 40: 
 41: 
 42: 
 43:     /**
 44:      * Static class - cannot be instantiated.
 45:      */
 46:     final public function __construct()
 47:     {
 48:         throw new \LogicException("Cannot instantiate static class " . get_class($this));
 49:     }
 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) Nette\Reflection\ClassReflection::from(__CLASS__)->getDocComment();
 87:         }
 88: 
 89:         if (self::$useReflection) {
 90:             return self::$cache[$type][$member] = self::parseComment($r->getDocComment());
 91: 
 92:         } else {
 93:             if (self::$cache === NULL) {
 94:                 self::$cache = (array) self::getCache()->offsetGet('list');
 95:                 self::$timestamps = isset(self::$cache['*']) ? self::$cache['*'] : array();
 96:             }
 97: 
 98:             if (!isset(self::$cache[$type]) && $file) {
 99:                 self::$cache['*'][$file] = filemtime($file);
100:                 self::parseScript($file);
101:                 self::getCache()->save('list', self::$cache);
102:             }
103: 
104:             if (isset(self::$cache[$type][$member])) {
105:                 return self::$cache[$type][$member];
106:             } else {
107:                 return self::$cache[$type][$member] = array();
108:             }
109:         }
110:     }
111: 
112: 
113: 
114:     /**
115:      * Parses phpDoc comment.
116:      * @param  string
117:      * @return array
118:      */
119:     private static function parseComment($comment)
120:     {
121:         static $tokens = array('true' => TRUE, 'false' => FALSE, 'null' => NULL, '' => TRUE);
122: 
123:         preg_match_all('~
124:             @('.self::RE_IDENTIFIER.')[ \t]*             ##  annotation
125:             (
126:                 \((?>'.self::RE_STRING.'|[^\'")@]+)+\)|  ##  (value)
127:                 [^(@\r\n][^@\r\n]*|)                     ##  value
128:         ~xi', trim($comment, '/*'), $matches, PREG_SET_ORDER);
129: 
130:         $res = array();
131:         foreach ($matches as $match)
132:         {
133:             list(, $name, $value) = $match;
134: 
135:             if (substr($value, 0, 1) === '(') {
136:                 $items = array();
137:                 $key = '';
138:                 $val = TRUE;
139:                 $value[0] = ',';
140:                 while (preg_match('#\s*,\s*(?>('.self::RE_IDENTIFIER.')\s*=\s*)?('.self::RE_STRING.'|[^\'"),\s][^\'"),]*)#A', $value, $m)) {
141:                     $value = substr($value, strlen($m[0]));
142:                     list(, $key, $val) = $m;
143:                     if ($val[0] === "'" || $val[0] === '"') {
144:                         $val = substr($val, 1, -1);
145: 
146:                     } elseif (is_numeric($val)) {
147:                         $val = 1 * $val;
148: 
149:                     } else {
150:                         $lval = strtolower($val);
151:                         $val = array_key_exists($lval, $tokens) ? $tokens[$lval] : $val;
152:                     }
153: 
154:                     if ($key === '') {
155:                         $items[] = $val;
156: 
157:                     } else {
158:                         $items[$key] = $val;
159:                     }
160:                 }
161: 
162:                 $value = count($items) < 2 && $key === '' ? $val : $items;
163: 
164:             } else {
165:                 $value = trim($value);
166:                 if (is_numeric($value)) {
167:                     $value = 1 * $value;
168: 
169:                 } else {
170:                     $lval = strtolower($value);
171:                     $value = array_key_exists($lval, $tokens) ? $tokens[$lval] : $value;
172:                 }
173:             }
174: 
175:             $class = $name . 'Annotation';
176:             if (class_exists($class)) {
177:                 $res[$name][] = new $class(is_array($value) ? $value : array('value' => $value));
178: 
179:             } else {
180:                 $res[$name][] = is_array($value) ? new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS) : $value;
181:             }
182:         }
183: 
184:         return $res;
185:     }
186: 
187: 
188: 
189:     /**
190:      * Parses PHP file.
191:      * @param  string
192:      * @return void
193:      */
194:     private static function parseScript($file)
195:     {
196:         $T_NAMESPACE = PHP_VERSION_ID < 50300 ? -1 : T_NAMESPACE;
197:         $T_NS_SEPARATOR = PHP_VERSION_ID < 50300 ? -1 : T_NS_SEPARATOR;
198: 
199:         $s = file_get_contents($file);
200: 
201:         if (preg_match('#//nette'.'loader=(\S*)#', $s)) {
202:             return; // TODO: allways ignore?
203:         }
204: 
205:         $expected = $namespace = $class = $docComment = NULL;
206:         $level = $classLevel = 0;
207: 
208:         foreach (token_get_all($s) as $token)
209:         {
210:             if (is_array($token)) {
211:                 switch ($token[0]) {
212:                 case T_DOC_COMMENT:
213:                     $docComment = $token[1];
214:                 case T_WHITESPACE:
215:                 case T_COMMENT:
216:                     continue 2;
217: 
218:                 case T_STRING:
219:                 case $T_NS_SEPARATOR:
220:                 case T_VARIABLE:
221:                     if ($expected) {
222:                         $name .= $token[1];
223:                     }
224:                     continue 2;
225: 
226:                 case T_FUNCTION:
227:                 case T_VAR:
228:                 case T_PUBLIC:
229:                 case T_PROTECTED:
230:                 case $T_NAMESPACE:
231:                 case T_CLASS:
232:                 case T_INTERFACE:
233:                     $expected = $token[0];
234:                     $name = NULL;
235:                     continue 2;
236: 
237:                 case T_STATIC:
238:                 case T_ABSTRACT:
239:                 case T_FINAL:
240:                     continue 2; // ignore in expectation
241: 
242:                 case T_CURLY_OPEN:
243:                 case T_DOLLAR_OPEN_CURLY_BRACES:
244:                     $level++;
245:                 }
246:             }
247: 
248:             if ($expected) {
249:                 switch ($expected) {
250:                 case T_CLASS:
251:                 case T_INTERFACE:
252:                     $class = $namespace . $name;
253:                     $classLevel = $level;
254:                     $name = '';
255:                     // break intentionally omitted
256:                 case T_FUNCTION:
257:                     if ($token === '&') continue 2; // ignore
258:                 case T_VAR:
259:                 case T_PUBLIC:
260:                 case T_PROTECTED:
261:                     if ($class && $name !== NULL && $docComment) {
262:                         self::$cache[$class][$name] = self::parseComment($docComment);
263:                     }
264:                     break;
265: 
266:                 case $T_NAMESPACE:
267:                     $namespace = $name . '\\';
268:                 }
269: 
270:                 $expected = $docComment = NULL;
271:             }
272: 
273:             if ($token === ';') {
274:                 $docComment = NULL;
275:             } elseif ($token === '{') {
276:                 $docComment = NULL;
277:                 $level++;
278:             } elseif ($token === '}') {
279:                 $level--;
280:                 if ($level === $classLevel) {
281:                     $class = NULL;
282:                 }
283:             }
284:         }
285:     }
286: 
287: 
288: 
289:     /********************* backend ****************d*g**/
290: 
291: 
292: 
293:     /**
294:      * @return Nette\Caching\Cache
295:      */
296:     protected static function getCache()
297:     {
298:         return Nette\Environment::getCache('Nette.Annotations');
299:     }
300: 
301: }
302: 
Nette Framework 0.9.7 API documentation generated by ApiGen 2.3.0