Packages

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

Classes

  • NAnnotation
  • NAnnotationsParser
  • NClassReflection
  • NExtensionReflection
  • NFunctionReflection
  • NMethodReflection
  • NParameterReflection
  • NPropertyReflection

Interfaces

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