%PDF- %PDF-
Direktori : /www/old2/_music/diplomka/diplomka/src/API/libs/Nette/Reflection/ |
Current File : /www/old2/_music/diplomka/diplomka/src/API/libs/Nette/Reflection/AnnotationsParser.php |
<?php /** * This file is part of the Nette Framework (http://nette.org) * * Copyright (c) 2004 David Grudl (http://davidgrudl.com) * * For the full copyright and license information, please view * the file license.txt that was distributed with this source code. */ namespace Nette\Reflection; use Nette, Nette\Utils\Strings; /** * Annotations support for PHP. * * @author David Grudl * @Annotation */ final class AnnotationsParser { /** @internal single & double quoted PHP string */ const RE_STRING = '\'(?:\\\\.|[^\'\\\\])*\'|"(?:\\\\.|[^"\\\\])*"'; /** @internal identifier */ const RE_IDENTIFIER = '[_a-zA-Z\x7F-\xFF][_a-zA-Z0-9\x7F-\xFF-\\\]*'; /** @var bool */ public static $useReflection; /** @var array */ public static $inherited = array('description', 'param', 'return'); /** @var array */ private static $cache; /** @var array */ private static $timestamps; /** @var Nette\Caching\IStorage */ private static $cacheStorage; /** * Static class - cannot be instantiated. */ final public function __construct() { throw new Nette\StaticClassException; } /** * Returns annotations. * @param \ReflectionClass|\ReflectionMethod|\ReflectionProperty * @return array */ public static function getAll(\Reflector $r) { if ($r instanceof \ReflectionClass) { $type = $r->getName(); $member = ''; } elseif ($r instanceof \ReflectionMethod) { $type = $r->getDeclaringClass()->getName(); $member = $r->getName(); } else { $type = $r->getDeclaringClass()->getName(); $member = '$' . $r->getName(); } if (!self::$useReflection) { // auto-expire cache $file = $r instanceof \ReflectionClass ? $r->getFileName() : $r->getDeclaringClass()->getFileName(); // will be used later if ($file && isset(self::$timestamps[$file]) && self::$timestamps[$file] !== filemtime($file)) { unset(self::$cache[$type]); } unset(self::$timestamps[$file]); } if (isset(self::$cache[$type][$member])) { // is value cached? return self::$cache[$type][$member]; } if (self::$useReflection === NULL) { // detects whether is reflection available self::$useReflection = (bool) ClassType::from(__CLASS__)->getDocComment(); } if (self::$useReflection) { $annotations = self::parseComment($r->getDocComment()); } else { if (!self::$cacheStorage) { // trigger_error('Set a cache storage for annotations parser via Nette\Reflection\AnnotationParser::setCacheStorage().', E_USER_WARNING); self::$cacheStorage = new Nette\Caching\Storages\DevNullStorage; } $outerCache = new Nette\Caching\Cache(self::$cacheStorage, 'Nette.Reflection.Annotations'); if (self::$cache === NULL) { self::$cache = (array) $outerCache->offsetGet('list'); self::$timestamps = isset(self::$cache['*']) ? self::$cache['*'] : array(); } if (!isset(self::$cache[$type]) && $file) { self::$cache['*'][$file] = filemtime($file); self::parseScript($file); $outerCache->save('list', self::$cache); } if (isset(self::$cache[$type][$member])) { $annotations = self::$cache[$type][$member]; } else { $annotations = array(); } } if ($r instanceof \ReflectionMethod && !$r->isPrivate() && (!$r->isConstructor() || !empty($annotations['inheritdoc'][0]))) { try { $inherited = self::getAll(new \ReflectionMethod(get_parent_class($type), $member)); } catch (\ReflectionException $e) { try { $inherited = self::getAll($r->getPrototype()); } catch (\ReflectionException $e) { $inherited = array(); } } $annotations += array_intersect_key($inherited, array_flip(self::$inherited)); } return self::$cache[$type][$member] = $annotations; } /** * Parses phpDoc comment. * @param string * @return array */ private static function parseComment($comment) { static $tokens = array('true' => TRUE, 'false' => FALSE, 'null' => NULL, '' => TRUE); $res = array(); $comment = preg_replace('#^\s*\*\s?#ms', '', trim($comment, '/*')); $parts = preg_split('#^\s*(?=@'.self::RE_IDENTIFIER.')#m', $comment, 2); $description = trim($parts[0]); if ($description !== '') { $res['description'] = array($description); } $matches = Strings::matchAll( isset($parts[1]) ? $parts[1] : '', '~ (?<=\s|^)@('.self::RE_IDENTIFIER.')[ \t]* ## annotation ( \((?>'.self::RE_STRING.'|[^\'")@]+)+\)| ## (value) [^(@\r\n][^@\r\n]*|) ## value ~xi' ); foreach ($matches as $match) { list(, $name, $value) = $match; if (substr($value, 0, 1) === '(') { $items = array(); $key = ''; $val = TRUE; $value[0] = ','; while ($m = Strings::match( $value, '#\s*,\s*(?>(' . self::RE_IDENTIFIER . ')\s*=\s*)?(' . self::RE_STRING . '|[^\'"),\s][^\'"),]*)#A') ) { $value = substr($value, strlen($m[0])); list(, $key, $val) = $m; $val = rtrim($val); if ($val[0] === "'" || $val[0] === '"') { $val = substr($val, 1, -1); } elseif (is_numeric($val)) { $val = 1 * $val; } else { $lval = strtolower($val); $val = array_key_exists($lval, $tokens) ? $tokens[$lval] : $val; } if ($key === '') { $items[] = $val; } else { $items[$key] = $val; } } $value = count($items) < 2 && $key === '' ? $val : $items; } else { $value = trim($value); if (is_numeric($value)) { $value = 1 * $value; } else { $lval = strtolower($value); $value = array_key_exists($lval, $tokens) ? $tokens[$lval] : $value; } } $class = $name . 'Annotation'; if (class_exists($class)) { $res[$name][] = new $class(is_array($value) ? $value : array('value' => $value)); } else { $res[$name][] = is_array($value) ? new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS) : $value; } } return $res; } /** * Parses PHP file. * @param string * @return void */ private static function parseScript($file) { $T_NAMESPACE = PHP_VERSION_ID < 50300 ? -1 : T_NAMESPACE; $T_NS_SEPARATOR = PHP_VERSION_ID < 50300 ? -1 : T_NS_SEPARATOR; $s = file_get_contents($file); if (Strings::match($s, '#//nette'.'loader=(\S*)#')) { return; // TODO: allways ignore? } $expected = $namespace = $class = $docComment = NULL; $level = $classLevel = 0; foreach (token_get_all($s) as $token) { if (is_array($token)) { switch ($token[0]) { case T_DOC_COMMENT: $docComment = $token[1]; case T_WHITESPACE: case T_COMMENT: continue 2; case T_STRING: case $T_NS_SEPARATOR: case T_VARIABLE: if ($expected) { $name .= $token[1]; } continue 2; case T_FUNCTION: case T_VAR: case T_PUBLIC: case T_PROTECTED: case $T_NAMESPACE: case T_CLASS: case T_INTERFACE: $expected = $token[0]; $name = NULL; continue 2; case T_STATIC: case T_ABSTRACT: case T_FINAL: continue 2; // ignore in expectation case T_CURLY_OPEN: case T_DOLLAR_OPEN_CURLY_BRACES: $level++; } } if ($expected) { switch ($expected) { case T_CLASS: case T_INTERFACE: $class = $namespace . $name; $classLevel = $level; $name = ''; // break intentionally omitted case T_FUNCTION: if ($token === '&') { continue 2; // ignore } case T_VAR: case T_PUBLIC: case T_PROTECTED: if ($class && $name !== NULL && $docComment) { self::$cache[$class][$name] = self::parseComment($docComment); } break; case $T_NAMESPACE: $namespace = $name . '\\'; } $expected = $docComment = NULL; } if ($token === ';') { $docComment = NULL; } elseif ($token === '{') { $docComment = NULL; $level++; } elseif ($token === '}') { $level--; if ($level === $classLevel) { $class = NULL; } } } } /********************* backend ****************d*g**/ /** * @return void */ public static function setCacheStorage(Nette\Caching\IStorage $storage) { self::$cacheStorage = $storage; } /** * @return Nette\Caching\IStorage */ public static function getCacheStorage() { return self::$cacheStorage; } }