├── composer.json ├── contributing.md ├── license.md ├── readme.md └── src ├── Bridges └── ReflectionDI │ └── ReflectionExtension.php └── Reflection ├── Annotation.php ├── AnnotationsParser.php ├── ClassType.php ├── Extension.php ├── GlobalFunction.php ├── Helpers.php ├── IAnnotation.php ├── Method.php ├── Parameter.php └── Property.php /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nette/reflection", 3 | "description": "Nette Reflection: docblock annotations parser and common reflection classes", 4 | "keywords": ["nette", "annotation", "reflection"], 5 | "homepage": "https://nette.org", 6 | "license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"], 7 | "authors": [ 8 | { 9 | "name": "David Grudl", 10 | "homepage": "https://davidgrudl.com" 11 | }, 12 | { 13 | "name": "Nette Community", 14 | "homepage": "https://nette.org/contributors" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=5.6.0", 19 | "ext-tokenizer": "*", 20 | "nette/caching": "^2.2 || ^3.0", 21 | "nette/utils": "^2.4 || ^3.0" 22 | }, 23 | "require-dev": { 24 | "nette/di": "^2.4 || ^3.0", 25 | "nette/tester": "^2.0", 26 | "tracy/tracy": "^2.4" 27 | }, 28 | "conflict": { 29 | "nette/nette": "<2.2" 30 | }, 31 | "autoload": { 32 | "classmap": ["src/"] 33 | }, 34 | "minimum-stability": "dev", 35 | "extra": { 36 | "branch-alias": { 37 | "dev-master": "2.4-dev" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | How to contribute & use the issue tracker 2 | ========================================= 3 | 4 | Nette welcomes your contributions. There are several ways to help out: 5 | 6 | * Create an issue on GitHub, if you have found a bug 7 | * Write test cases for open bug issues 8 | * Write fixes for open bug/feature issues, preferably with test cases included 9 | * Contribute to the [documentation](https://nette.org/en/writing) 10 | 11 | Issues 12 | ------ 13 | 14 | Please **do not use the issue tracker to ask questions**. We will be happy to help you 15 | on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette). 16 | 17 | A good bug report shouldn't leave others needing to chase you up for more 18 | information. Please try to be as detailed as possible in your report. 19 | 20 | **Feature requests** are welcome. But take a moment to find out whether your idea 21 | fits with the scope and aims of the project. It's up to *you* to make a strong 22 | case to convince the project's developers of the merits of this feature. 23 | 24 | Contributing 25 | ------------ 26 | 27 | If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing). 28 | 29 | The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them. 30 | 31 | Please do not fix whitespace, format code, or make a purely cosmetic patch. 32 | 33 | Thanks! :heart: 34 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Licenses 2 | ======== 3 | 4 | Good news! You may use Nette Framework under the terms of either 5 | the New BSD License or the GNU General Public License (GPL) version 2 or 3. 6 | 7 | The BSD License is recommended for most projects. It is easy to understand and it 8 | places almost no restrictions on what you can do with the framework. If the GPL 9 | fits better to your project, you can use the framework under this license. 10 | 11 | You don't have to notify anyone which license you are using. You can freely 12 | use Nette Framework in commercial projects as long as the copyright header 13 | remains intact. 14 | 15 | Please be advised that the name "Nette Framework" is a protected trademark and its 16 | usage has some limitations. So please do not use word "Nette" in the name of your 17 | project or top-level domain, and choose a name that stands on its own merits. 18 | If your stuff is good, it will not take long to establish a reputation for yourselves. 19 | 20 | 21 | New BSD License 22 | --------------- 23 | 24 | Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com) 25 | All rights reserved. 26 | 27 | Redistribution and use in source and binary forms, with or without modification, 28 | are permitted provided that the following conditions are met: 29 | 30 | * Redistributions of source code must retain the above copyright notice, 31 | this list of conditions and the following disclaimer. 32 | 33 | * Redistributions in binary form must reproduce the above copyright notice, 34 | this list of conditions and the following disclaimer in the documentation 35 | and/or other materials provided with the distribution. 36 | 37 | * Neither the name of "Nette Framework" nor the names of its contributors 38 | may be used to endorse or promote products derived from this software 39 | without specific prior written permission. 40 | 41 | This software is provided by the copyright holders and contributors "as is" and 42 | any express or implied warranties, including, but not limited to, the implied 43 | warranties of merchantability and fitness for a particular purpose are 44 | disclaimed. In no event shall the copyright owner or contributors be liable for 45 | any direct, indirect, incidental, special, exemplary, or consequential damages 46 | (including, but not limited to, procurement of substitute goods or services; 47 | loss of use, data, or profits; or business interruption) however caused and on 48 | any theory of liability, whether in contract, strict liability, or tort 49 | (including negligence or otherwise) arising in any way out of the use of this 50 | software, even if advised of the possibility of such damage. 51 | 52 | 53 | GNU General Public License 54 | -------------------------- 55 | 56 | GPL licenses are very very long, so instead of including them here we offer 57 | you URLs with full text: 58 | 59 | - [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html) 60 | - [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html) 61 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Nette PHP Reflection 2 | ==================== 3 | 4 | [![Downloads this Month](https://img.shields.io/packagist/dm/nette/reflection.svg)](https://packagist.org/packages/nette/reflection) 5 | [![Build Status](https://travis-ci.org/nette/reflection.svg?branch=master)](https://travis-ci.org/nette/reflection) 6 | [![Coverage Status](https://coveralls.io/repos/github/nette/reflection/badge.svg?branch=master)](https://coveralls.io/github/nette/reflection?branch=master) 7 | [![Latest Stable Version](https://poser.pugx.org/nette/reflection/v/stable)](https://github.com/nette/reflection/releases) 8 | [![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/nette/reflection/blob/master/license.md) 9 | 10 | Install it using Composer: 11 | 12 | ``` 13 | composer require nette/reflection 14 | ``` 15 | 16 | The last stable release requires PHP version 5.6 or newer (is compatible with PHP 7.0 and 7.1). 17 | 18 | If you like Nette, **[please make a donation now](https://nette.org/donate)**. Thank you! 19 | 20 | If you need to find every information about any class, reflection is the right tool to do it. You can easily find out which methods does any class have, what parameters do those methods accept, etc. 21 | 22 | ```php 23 | // getting PDO class reflection 24 | $classReflection = new Nette\Reflection\ClassType('PDO'); 25 | 26 | // getting PDO::query method reflection 27 | $methodReflection = new Nette\Reflection\Method('PDO', 'query'); 28 | ``` 29 | 30 | Annotations 31 | ----------- 32 | 33 | Reflection has really a lot to do with annotations. The annotations are written into phpDoc comments (two opening asterisks are mandatory!) and start with `@`. You can annotate classes, variables and methods: 34 | 35 | ```php 36 | /** 37 | * @author John Doe 38 | * @author Tomas Marny 39 | * @secured 40 | */ 41 | class FooClass 42 | { 43 | /** @Persistent */ 44 | public $foo; 45 | 46 | /** @User(loggedIn, role=Admin) */ 47 | public function bar() {} 48 | } 49 | ``` 50 | 51 | In the code there are these annotations: 52 | 53 | - `@author John Doe` - string, contains text value `'John Doe'` 54 | - `@Persistent` - boolean, its presence means `true` 55 | - `@User(loggedIn, role=Admin)` - contains associative `array('loggedIn', 'role' => 'Admin')` 56 | 57 | 58 | The existence of a class annotation can be checked via `hasAnnotation()` method: 59 | 60 | 61 | ```php 62 | $fooReflection = new Nette\Reflection\ClassType('FooClass'); 63 | $fooReflection->hasAnnotation('author'); // returns true 64 | $fooReflection->hasAnnotation('copyright'); // returns false 65 | ``` 66 | 67 | Values can be acquired with `getAnnotation()`: 68 | 69 | ```php 70 | $fooReflection->getAnnotation('author'); // returns string 'Tomas Marny' 71 | 72 | $fooReflection->getMethod('bar')->getAnnotation('User'); 73 | // returns array('loggedIn', 'role' => 'Admin') 74 | ``` 75 | 76 | .[caution] 77 | Previous definitions are overwritten with the latter ones, sou you will always get the last one. 78 | 79 | All annotations can be obtained with `getAnnotations()`: 80 | 81 | ``` 82 | array(3) { 83 | "author" => array(2) { 84 | 0 => string(8) "John Doe" 85 | 1 => string(11) "Tomas Marny" 86 | } 87 | "secured" => array(1) { 88 | 0 => bool(true) 89 | } 90 | } 91 | ``` 92 | -------------------------------------------------------------------------------- /src/Bridges/ReflectionDI/ReflectionExtension.php: -------------------------------------------------------------------------------- 1 | debugMode = $debugMode; 25 | } 26 | 27 | 28 | public function afterCompile(Nette\PhpGenerator\ClassType $class) 29 | { 30 | $class->getMethod('initialize') 31 | ->addBody('Nette\Reflection\AnnotationsParser::setCacheStorage($this->getByType(Nette\Caching\IStorage::class));') 32 | ->addBody('Nette\Reflection\AnnotationsParser::$autoRefresh = ?;', [$this->debugMode]); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Reflection/Annotation.php: -------------------------------------------------------------------------------- 1 | $v) { 23 | $this->$k = $v; 24 | } 25 | } 26 | 27 | 28 | /** 29 | * Returns default annotation. 30 | * @return string 31 | */ 32 | public function __toString() 33 | { 34 | return $this->value; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Reflection/AnnotationsParser.php: -------------------------------------------------------------------------------- 1 | getName(); 56 | $member = 'class'; 57 | $file = $r->getFileName(); 58 | 59 | } elseif ($r instanceof \ReflectionMethod) { 60 | $type = $r->getDeclaringClass()->getName(); 61 | $member = $r->getName(); 62 | $file = $r->getFileName(); 63 | 64 | } elseif ($r instanceof \ReflectionFunction) { 65 | $type = null; 66 | $member = $r->getName(); 67 | $file = $r->getFileName(); 68 | 69 | } else { 70 | $type = $r->getDeclaringClass()->getName(); 71 | $member = '$' . $r->getName(); 72 | $file = $r->getDeclaringClass()->getFileName(); 73 | } 74 | 75 | if (self::$useReflection === null) { // detects whether is reflection available 76 | self::$useReflection = (bool) ClassType::from(__CLASS__)->getDocComment(); 77 | } 78 | 79 | if (!self::$useReflection) { // auto-expire cache 80 | if ($file && isset(self::$timestamps[$file]) && self::$timestamps[$file] !== filemtime($file)) { 81 | unset(self::$cache[$type]); 82 | } 83 | unset(self::$timestamps[$file]); 84 | } 85 | 86 | if (isset(self::$cache[$type][$member])) { // is value cached? 87 | return self::$cache[$type][$member]; 88 | } 89 | 90 | if (self::$useReflection) { 91 | $annotations = self::parseComment($r->getDocComment()); 92 | 93 | } else { 94 | $outerCache = self::getCache(); 95 | 96 | if (self::$cache === null) { 97 | self::$cache = (array) $outerCache->load('list'); 98 | self::$timestamps = isset(self::$cache['*']) ? self::$cache['*'] : []; 99 | } 100 | 101 | if (!isset(self::$cache[$type]) && $file) { 102 | self::$cache['*'][$file] = filemtime($file); 103 | foreach (static::parsePhp(file_get_contents($file)) as $class => $info) { 104 | foreach ($info as $prop => $comment) { 105 | if ($prop !== 'use') { 106 | self::$cache[$class][$prop] = self::parseComment($comment); 107 | } 108 | } 109 | } 110 | $outerCache->save('list', self::$cache); 111 | } 112 | 113 | if (isset(self::$cache[$type][$member])) { 114 | $annotations = self::$cache[$type][$member]; 115 | } else { 116 | $annotations = []; 117 | } 118 | } 119 | 120 | if ($r instanceof \ReflectionMethod && !$r->isPrivate() 121 | && (!$r->isConstructor() || !empty($annotations['inheritdoc'][0])) 122 | ) { 123 | try { 124 | $inherited = self::getAll(new \ReflectionMethod(get_parent_class($type), $member)); 125 | } catch (\ReflectionException $e) { 126 | try { 127 | $inherited = self::getAll($r->getPrototype()); 128 | } catch (\ReflectionException $e) { 129 | $inherited = []; 130 | } 131 | } 132 | $annotations += array_intersect_key($inherited, array_flip(self::$inherited)); 133 | } 134 | 135 | return self::$cache[$type][$member] = $annotations; 136 | } 137 | 138 | 139 | /** 140 | * Expands class name into FQN. 141 | * @param string 142 | * @return string fully qualified class name 143 | * @throws Nette\InvalidArgumentException 144 | */ 145 | public static function expandClassName($name, \ReflectionClass $reflector) 146 | { 147 | if (empty($name)) { 148 | throw new Nette\InvalidArgumentException('Class name must not be empty.'); 149 | 150 | } elseif ($name === 'self') { 151 | return $reflector->getName(); 152 | 153 | } elseif ($name[0] === '\\') { // already fully qualified 154 | return ltrim($name, '\\'); 155 | } 156 | 157 | $filename = $reflector->getFileName(); 158 | $parsed = static::getCache()->load($filename, function (&$dp) use ($filename) { 159 | if (self::$autoRefresh) { 160 | $dp[Nette\Caching\Cache::FILES] = $filename; 161 | } 162 | return self::parsePhp(file_get_contents($filename)); 163 | }); 164 | $uses = array_change_key_case((array) $tmp = &$parsed[$reflector->getName()]['use']); 165 | $parts = explode('\\', $name, 2); 166 | $parts[0] = strtolower($parts[0]); 167 | if (isset($uses[$parts[0]])) { 168 | $parts[0] = $uses[$parts[0]]; 169 | return implode('\\', $parts); 170 | 171 | } elseif ($reflector->inNamespace()) { 172 | return $reflector->getNamespaceName() . '\\' . $name; 173 | 174 | } else { 175 | return $name; 176 | } 177 | } 178 | 179 | 180 | /** 181 | * Parses phpDoc comment. 182 | * @param string 183 | * @return array 184 | */ 185 | private static function parseComment($comment) 186 | { 187 | static $tokens = ['true' => true, 'false' => false, 'null' => null, '' => true]; 188 | 189 | $res = []; 190 | $comment = preg_replace('#^\s*\*\s?#ms', '', trim($comment, '/*')); 191 | $parts = preg_split('#^\s*(?=@' . self::RE_IDENTIFIER . ')#m', $comment, 2); 192 | 193 | $description = trim($parts[0]); 194 | if ($description !== '') { 195 | $res['description'] = [$description]; 196 | } 197 | 198 | $matches = Strings::matchAll( 199 | isset($parts[1]) ? $parts[1] : '', 200 | '~ 201 | (?<=\s|^)@(' . self::RE_IDENTIFIER . ')[ \t]* ## annotation 202 | ( 203 | \((?>' . self::RE_STRING . '|[^\'")@]+)+\)| ## (value) 204 | [^(@\r\n][^@\r\n]*|) ## value 205 | ~xi' 206 | ); 207 | 208 | foreach ($matches as $match) { 209 | list(, $name, $value) = $match; 210 | 211 | if (substr($value, 0, 1) === '(') { 212 | $items = []; 213 | $key = ''; 214 | $val = true; 215 | $value[0] = ','; 216 | while ($m = Strings::match( 217 | $value, 218 | '#\s*,\s*(?>(' . self::RE_IDENTIFIER . ')\s*=\s*)?(' . self::RE_STRING . '|[^\'"),\s][^\'"),]*)#A') 219 | ) { 220 | $value = substr($value, strlen($m[0])); 221 | list(, $key, $val) = $m; 222 | $val = rtrim($val); 223 | if ($val[0] === "'" || $val[0] === '"') { 224 | $val = substr($val, 1, -1); 225 | 226 | } elseif (is_numeric($val)) { 227 | $val = 1 * $val; 228 | 229 | } else { 230 | $lval = strtolower($val); 231 | $val = array_key_exists($lval, $tokens) ? $tokens[$lval] : $val; 232 | } 233 | 234 | if ($key === '') { 235 | $items[] = $val; 236 | 237 | } else { 238 | $items[$key] = $val; 239 | } 240 | } 241 | 242 | $value = count($items) < 2 && $key === '' ? $val : $items; 243 | 244 | } else { 245 | $value = trim($value); 246 | if (is_numeric($value)) { 247 | $value = 1 * $value; 248 | 249 | } else { 250 | $lval = strtolower($value); 251 | $value = array_key_exists($lval, $tokens) ? $tokens[$lval] : $value; 252 | } 253 | } 254 | 255 | $res[$name][] = is_array($value) ? Nette\Utils\ArrayHash::from($value) : $value; 256 | } 257 | 258 | return $res; 259 | } 260 | 261 | 262 | /** 263 | * Parses PHP file. 264 | * @param string 265 | * @return array [class => [prop => comment (or 'use' => [alias => class])] 266 | * @internal 267 | */ 268 | public static function parsePhp($code) 269 | { 270 | if (Strings::match($code, '#//nette' . 'loader=(\S*)#')) { 271 | return; 272 | } 273 | 274 | $tokens = @token_get_all($code); 275 | $namespace = $class = $classLevel = $level = $docComment = null; 276 | $res = $uses = []; 277 | 278 | while ($token = current($tokens)) { 279 | next($tokens); 280 | switch (is_array($token) ? $token[0] : $token) { 281 | case T_DOC_COMMENT: 282 | $docComment = $token[1]; 283 | break; 284 | 285 | case T_NAMESPACE: 286 | $namespace = ltrim(self::fetch($tokens, [T_STRING, T_NS_SEPARATOR]) . '\\', '\\'); 287 | $uses = []; 288 | break; 289 | 290 | case T_CLASS: 291 | case T_INTERFACE: 292 | case T_TRAIT: 293 | if ($name = self::fetch($tokens, T_STRING)) { 294 | $class = $namespace . $name; 295 | $classLevel = $level + 1; 296 | $res[$class] = []; 297 | if ($docComment) { 298 | $res[$class]['class'] = $docComment; 299 | } 300 | if ($uses) { 301 | $res[$class]['use'] = $uses; 302 | } 303 | } 304 | break; 305 | 306 | case T_FUNCTION: 307 | self::fetch($tokens, '&'); 308 | if ($level === $classLevel && $docComment && ($name = self::fetch($tokens, T_STRING))) { 309 | $res[$class][$name] = $docComment; 310 | } 311 | break; 312 | 313 | case T_VAR: 314 | case T_PUBLIC: 315 | case T_PROTECTED: 316 | self::fetch($tokens, T_STATIC); 317 | if ($level === $classLevel && $docComment && ($name = self::fetch($tokens, T_VARIABLE))) { 318 | $res[$class][$name] = $docComment; 319 | } 320 | break; 321 | 322 | case T_USE: 323 | while (!$class && ($name = self::fetch($tokens, [T_STRING, T_NS_SEPARATOR]))) { 324 | $name = ltrim($name, '\\'); 325 | if (self::fetch($tokens, '{')) { 326 | while ($suffix = self::fetch($tokens, [T_STRING, T_NS_SEPARATOR])) { 327 | if (self::fetch($tokens, T_AS)) { 328 | $uses[self::fetch($tokens, T_STRING)] = $name . $suffix; 329 | } else { 330 | $tmp = explode('\\', $suffix); 331 | $uses[end($tmp)] = $name . $suffix; 332 | } 333 | if (!self::fetch($tokens, ',')) { 334 | break; 335 | } 336 | } 337 | 338 | } elseif (self::fetch($tokens, T_AS)) { 339 | $uses[self::fetch($tokens, T_STRING)] = $name; 340 | 341 | } else { 342 | $tmp = explode('\\', $name); 343 | $uses[end($tmp)] = $name; 344 | } 345 | if (!self::fetch($tokens, ',')) { 346 | break; 347 | } 348 | } 349 | break; 350 | 351 | case T_CURLY_OPEN: 352 | case T_DOLLAR_OPEN_CURLY_BRACES: 353 | case '{': 354 | $level++; 355 | break; 356 | 357 | case '}': 358 | if ($level === $classLevel) { 359 | $class = $classLevel = null; 360 | } 361 | $level--; 362 | // break omitted 363 | case ';': 364 | $docComment = null; 365 | } 366 | } 367 | 368 | return $res; 369 | } 370 | 371 | 372 | private static function fetch(&$tokens, $take) 373 | { 374 | $res = null; 375 | while ($token = current($tokens)) { 376 | list($token, $s) = is_array($token) ? $token : [$token, $token]; 377 | if (in_array($token, (array) $take, true)) { 378 | $res .= $s; 379 | } elseif (!in_array($token, [T_DOC_COMMENT, T_WHITESPACE, T_COMMENT], true)) { 380 | break; 381 | } 382 | next($tokens); 383 | } 384 | return $res; 385 | } 386 | 387 | 388 | /********************* backend ****************d*g**/ 389 | 390 | 391 | /** 392 | * @return void 393 | */ 394 | public static function setCacheStorage(Nette\Caching\IStorage $storage) 395 | { 396 | self::$cacheStorage = $storage; 397 | } 398 | 399 | 400 | /** 401 | * @return Nette\Caching\IStorage 402 | */ 403 | public static function getCacheStorage() 404 | { 405 | if (!self::$cacheStorage) { 406 | self::$cacheStorage = new Nette\Caching\Storages\MemoryStorage(); 407 | } 408 | return self::$cacheStorage; 409 | } 410 | 411 | 412 | /** 413 | * @return Nette\Caching\Cache 414 | */ 415 | private static function getCache() 416 | { 417 | return new Nette\Caching\Cache(static::getCacheStorage(), 'Nette.Reflection.Annotations'); 418 | } 419 | } 420 | -------------------------------------------------------------------------------- /src/Reflection/ClassType.php: -------------------------------------------------------------------------------- 1 | getName(); 61 | } 62 | 63 | 64 | /** 65 | * @param string 66 | * @return bool 67 | */ 68 | public function is($type) 69 | { 70 | return is_a($this->getName(), $type, true); 71 | } 72 | 73 | 74 | /********************* Reflection layer ****************d*g**/ 75 | 76 | 77 | /** 78 | * @return Method|null 79 | */ 80 | public function getConstructor() 81 | { 82 | return ($ref = parent::getConstructor()) ? Method::from($this->getName(), $ref->getName()) : null; 83 | } 84 | 85 | 86 | /** 87 | * @return Extension|null 88 | */ 89 | public function getExtension() 90 | { 91 | return ($name = $this->getExtensionName()) ? new Extension($name) : null; 92 | } 93 | 94 | 95 | /** 96 | * @return static[] 97 | */ 98 | public function getInterfaces() 99 | { 100 | $res = []; 101 | foreach (parent::getInterfaceNames() as $val) { 102 | $res[$val] = new static($val); 103 | } 104 | return $res; 105 | } 106 | 107 | 108 | /** 109 | * @return Method 110 | */ 111 | public function getMethod($name) 112 | { 113 | return new Method($this->getName(), $name); 114 | } 115 | 116 | 117 | /** 118 | * @return Method[] 119 | */ 120 | public function getMethods($filter = -1) 121 | { 122 | foreach ($res = parent::getMethods($filter) as $key => $val) { 123 | $res[$key] = new Method($this->getName(), $val->getName()); 124 | } 125 | return $res; 126 | } 127 | 128 | 129 | /** 130 | * @return static|null 131 | */ 132 | public function getParentClass() 133 | { 134 | return ($ref = parent::getParentClass()) ? new static($ref->getName()) : null; 135 | } 136 | 137 | 138 | /** 139 | * @return Property[] 140 | */ 141 | public function getProperties($filter = -1) 142 | { 143 | foreach ($res = parent::getProperties($filter) as $key => $val) { 144 | $res[$key] = new Property($this->getName(), $val->getName()); 145 | } 146 | return $res; 147 | } 148 | 149 | 150 | /** 151 | * @return Property 152 | */ 153 | public function getProperty($name) 154 | { 155 | return new Property($this->getName(), $name); 156 | } 157 | 158 | 159 | /********************* Nette\Annotations support ****************d*g**/ 160 | 161 | 162 | /** 163 | * Has class specified annotation? 164 | * @param string 165 | * @return bool 166 | */ 167 | public function hasAnnotation($name) 168 | { 169 | $res = AnnotationsParser::getAll($this); 170 | return !empty($res[$name]); 171 | } 172 | 173 | 174 | /** 175 | * Returns an annotation value. 176 | * @param string 177 | * @return IAnnotation|null 178 | */ 179 | public function getAnnotation($name) 180 | { 181 | $res = AnnotationsParser::getAll($this); 182 | return isset($res[$name]) ? end($res[$name]) : null; 183 | } 184 | 185 | 186 | /** 187 | * Returns all annotations. 188 | * @return IAnnotation[][] 189 | */ 190 | public function getAnnotations() 191 | { 192 | return AnnotationsParser::getAll($this); 193 | } 194 | 195 | 196 | /** 197 | * Returns value of annotation 'description'. 198 | * @return string 199 | */ 200 | public function getDescription() 201 | { 202 | return $this->getAnnotation('description'); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/Reflection/Extension.php: -------------------------------------------------------------------------------- 1 | getName(); 23 | } 24 | 25 | 26 | /********************* Reflection layer ****************d*g**/ 27 | 28 | 29 | public function getClasses() 30 | { 31 | $res = []; 32 | foreach (parent::getClassNames() as $val) { 33 | $res[$val] = new ClassType($val); 34 | } 35 | return $res; 36 | } 37 | 38 | 39 | public function getFunctions() 40 | { 41 | foreach ($res = parent::getFunctions() as $key => $val) { 42 | $res[$key] = new GlobalFunction($key); 43 | } 44 | return $res; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Reflection/GlobalFunction.php: -------------------------------------------------------------------------------- 1 | value = $name); 46 | } 47 | 48 | 49 | /** 50 | * @deprecated 51 | */ 52 | public function toCallback() 53 | { 54 | return new Nette\Callback($this->value); 55 | } 56 | 57 | 58 | public function __toString() 59 | { 60 | return $this->getName() . '()'; 61 | } 62 | 63 | 64 | /********************* Reflection layer ****************d*g**/ 65 | 66 | 67 | /** 68 | * @return Extension 69 | */ 70 | public function getExtension() 71 | { 72 | return ($name = $this->getExtensionName()) ? new Extension($name) : null; 73 | } 74 | 75 | 76 | /** 77 | * @return Parameter[] 78 | */ 79 | public function getParameters() 80 | { 81 | foreach ($res = parent::getParameters() as $key => $val) { 82 | $res[$key] = new Parameter($this->value, $val->getName()); 83 | } 84 | return $res; 85 | } 86 | 87 | 88 | /********************* Nette\Annotations support ****************d*g**/ 89 | 90 | 91 | /** 92 | * Has method specified annotation? 93 | * @param string 94 | * @return bool 95 | */ 96 | public function hasAnnotation($name) 97 | { 98 | $res = AnnotationsParser::getAll($this); 99 | return !empty($res[$name]); 100 | } 101 | 102 | 103 | /** 104 | * Returns an annotation value. 105 | * @param string 106 | * @return IAnnotation 107 | */ 108 | public function getAnnotation($name) 109 | { 110 | $res = AnnotationsParser::getAll($this); 111 | return isset($res[$name]) ? end($res[$name]) : null; 112 | } 113 | 114 | 115 | /** 116 | * Returns all annotations. 117 | * @return IAnnotation[][] 118 | */ 119 | public function getAnnotations() 120 | { 121 | return AnnotationsParser::getAll($this); 122 | } 123 | 124 | 125 | /** 126 | * Returns value of annotation 'description'. 127 | * @return string 128 | */ 129 | public function getDescription() 130 | { 131 | return $this->getAnnotation('description'); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/Reflection/Helpers.php: -------------------------------------------------------------------------------- 1 | getDeclaringClass()->getTraits() as $trait) { 25 | if ($trait->hasProperty($prop->getName())) { 26 | return self::getDeclaringClass($trait->getProperty($prop->getName())); 27 | } 28 | } 29 | return $prop->getDeclaringClass(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Reflection/IAnnotation.php: -------------------------------------------------------------------------------- 1 | getName(), $this->getName()); 69 | } 70 | 71 | 72 | public function __toString() 73 | { 74 | return parent::getDeclaringClass()->getName() . '::' . $this->getName() . '()'; 75 | } 76 | 77 | 78 | /********************* Reflection layer ****************d*g**/ 79 | 80 | 81 | /** 82 | * @return ClassType 83 | */ 84 | public function getDeclaringClass() 85 | { 86 | return new ClassType(parent::getDeclaringClass()->getName()); 87 | } 88 | 89 | 90 | /** 91 | * @return static 92 | */ 93 | public function getPrototype() 94 | { 95 | $prototype = parent::getPrototype(); 96 | return new static($prototype->getDeclaringClass()->getName(), $prototype->getName()); 97 | } 98 | 99 | 100 | /** 101 | * @return Extension 102 | */ 103 | public function getExtension() 104 | { 105 | return ($name = $this->getExtensionName()) ? new Extension($name) : null; 106 | } 107 | 108 | 109 | /** 110 | * @return Parameter[] 111 | */ 112 | public function getParameters() 113 | { 114 | $me = [parent::getDeclaringClass()->getName(), $this->getName()]; 115 | foreach ($res = parent::getParameters() as $key => $val) { 116 | $res[$key] = new Parameter($me, $val->getName()); 117 | } 118 | return $res; 119 | } 120 | 121 | 122 | /********************* Nette\Annotations support ****************d*g**/ 123 | 124 | 125 | /** 126 | * Has method specified annotation? 127 | * @param string 128 | * @return bool 129 | */ 130 | public function hasAnnotation($name) 131 | { 132 | $res = AnnotationsParser::getAll($this); 133 | return !empty($res[$name]); 134 | } 135 | 136 | 137 | /** 138 | * Returns an annotation value. 139 | * @param string 140 | * @return IAnnotation|null 141 | */ 142 | public function getAnnotation($name) 143 | { 144 | $res = AnnotationsParser::getAll($this); 145 | return isset($res[$name]) ? end($res[$name]) : null; 146 | } 147 | 148 | 149 | /** 150 | * Returns all annotations. 151 | * @return IAnnotation[][] 152 | */ 153 | public function getAnnotations() 154 | { 155 | return AnnotationsParser::getAll($this); 156 | } 157 | 158 | 159 | /** 160 | * Returns value of annotation 'description'. 161 | * @return string 162 | */ 163 | public function getDescription() 164 | { 165 | return $this->getAnnotation('description'); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/Reflection/Parameter.php: -------------------------------------------------------------------------------- 1 | function = $function, $parameter); 38 | } 39 | 40 | 41 | /** 42 | * @return ClassType 43 | */ 44 | public function getClass() 45 | { 46 | return ($ref = parent::getClass()) ? new ClassType($ref->getName()) : null; 47 | } 48 | 49 | 50 | /** 51 | * @return string 52 | */ 53 | public function getClassName() 54 | { 55 | try { 56 | return ($ref = parent::getClass()) ? $ref->getName() : null; 57 | } catch (\ReflectionException $e) { 58 | if (preg_match('#Class (.+) does not exist#', $e->getMessage(), $m)) { 59 | return $m[1]; 60 | } 61 | throw $e; 62 | } 63 | } 64 | 65 | 66 | /** 67 | * @return ClassType 68 | */ 69 | public function getDeclaringClass() 70 | { 71 | return ($ref = parent::getDeclaringClass()) ? new ClassType($ref->getName()) : null; 72 | } 73 | 74 | 75 | /** 76 | * @return Method|GlobalFunction 77 | */ 78 | public function getDeclaringFunction() 79 | { 80 | return is_array($this->function) 81 | ? new Method($this->function[0], $this->function[1]) 82 | : new GlobalFunction($this->function); 83 | } 84 | 85 | 86 | public function __toString() 87 | { 88 | return '$' . parent::getName() . ' in ' . $this->getDeclaringFunction(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Reflection/Property.php: -------------------------------------------------------------------------------- 1 | getName() . '::$' . $this->getName(); 36 | } 37 | 38 | 39 | /********************* Reflection layer ****************d*g**/ 40 | 41 | 42 | /** 43 | * @return ClassType 44 | */ 45 | public function getDeclaringClass() 46 | { 47 | return new ClassType(parent::getDeclaringClass()->getName()); 48 | } 49 | 50 | 51 | /********************* Nette\Annotations support ****************d*g**/ 52 | 53 | 54 | /** 55 | * Has property specified annotation? 56 | * @param string 57 | * @return bool 58 | */ 59 | public function hasAnnotation($name) 60 | { 61 | $res = AnnotationsParser::getAll($this); 62 | return !empty($res[$name]); 63 | } 64 | 65 | 66 | /** 67 | * Returns an annotation value. 68 | * @param string 69 | * @return IAnnotation 70 | */ 71 | public function getAnnotation($name) 72 | { 73 | $res = AnnotationsParser::getAll($this); 74 | return isset($res[$name]) ? end($res[$name]) : null; 75 | } 76 | 77 | 78 | /** 79 | * Returns all annotations. 80 | * @return IAnnotation[][] 81 | */ 82 | public function getAnnotations() 83 | { 84 | return AnnotationsParser::getAll($this); 85 | } 86 | 87 | 88 | /** 89 | * Returns value of annotation 'description'. 90 | * @return string 91 | */ 92 | public function getDescription() 93 | { 94 | return $this->getAnnotation('description'); 95 | } 96 | } 97 | --------------------------------------------------------------------------------