Skip to content

Commit 6af734c

Browse files
MaximilianKresselisachenko
authored andcommitted
Added support for ReflectionClassConstant (#97)
Added support for ReflectionClassConstant #97, resolves #93
1 parent 793727d commit 6af734c

12 files changed

+423
-46
lines changed

.travis.yml

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
language: php
22

33
php:
4-
- 7.0
54
- 7.1
65
- 7.2
76
- hhvm

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ Parser Reflection API Library
33

44
Parser Reflection API library provides a set of classes that extend original internal Reflection classes, but powered by [PHP-Parser](https://github.com/nikic/PHP-Parser) library thus allowing to create a reflection instance without loading classes into the memory.
55

6-
This library can be used for analysing the source code for PHP versions 5.5, 5.6, 7.0; for automatic proxy creation and much more.
6+
This library can be used for analysing the source code for PHP versions 7.1, 7.2, 7.3; for automatic proxy creation and much more.
77

88
[![Build Status](https://scrutinizer-ci.com/g/goaop/parser-reflection/badges/build.png?b=master)](https://scrutinizer-ci.com/g/goaop/parser-reflection/build-status/master)
99
[![Code Coverage](https://scrutinizer-ci.com/g/goaop/parser-reflection/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/goaop/parser-reflection/?branch=master)
1010
[![Total Downloads](https://img.shields.io/packagist/dt/goaop/parser-reflection.svg)](https://packagist.org/packages/goaop/parser-reflection)
1111
[![Daily Downloads](https://img.shields.io/packagist/dd/goaop/parser-reflection.svg)](https://packagist.org/packages/goaop/parser-reflection)
1212
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/goaop/parser-reflection/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/goaop/parser-reflection/?branch=master)
13-
[![SensioLabs Insight](https://img.shields.io/sensiolabs/i/1fdfee9c-839a-4209-a2f2-42dadc859621.svg)](https://insight.sensiolabs.com/projects/1fdfee9c-839a-4209-a2f2-42dadc859621)[![Minimum PHP Version](http://img.shields.io/badge/php-%3E%3D%205.5-8892BF.svg)](https://php.net/)
13+
[![SensioLabs Insight](https://img.shields.io/sensiolabs/i/1fdfee9c-839a-4209-a2f2-42dadc859621.svg)](https://insight.sensiolabs.com/projects/1fdfee9c-839a-4209-a2f2-42dadc859621)[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.1-8892BF.svg)](https://php.net/)
1414
[![License](https://img.shields.io/packagist/l/goaop/parser-reflection.svg)](https://packagist.org/packages/goaop/parser-reflection)
1515

1616
Installation

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
}
2323
},
2424
"require": {
25-
"php": ">=7.0",
25+
"php": ">=7.1",
2626
"nikic/php-parser": "^4.0"
2727
},
2828
"require-dev": {

src/ReflectionClassConstant.php

+189
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
<?php
2+
/**
3+
* Parser Reflection API
4+
*
5+
* @copyright Copyright 2019, Lisachenko Alexander <[email protected]>
6+
*
7+
* This source file is subject to the license that is bundled
8+
* with this source code in the file LICENSE.
9+
*/
10+
declare(strict_types=1);
11+
12+
namespace Go\ParserReflection;
13+
14+
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
15+
use PhpParser\Node\Const_;
16+
use PhpParser\Node\Stmt\ClassConst;
17+
use PhpParser\Node\Stmt\ClassLike;
18+
use \ReflectionClassConstant as BaseReflectionClassConstant;
19+
20+
class ReflectionClassConstant extends BaseReflectionClassConstant
21+
{
22+
/**
23+
* Concrete class constant node
24+
*
25+
* @var ClassConst
26+
*/
27+
private $classConstantNode;
28+
29+
/**
30+
* @var Const_
31+
*/
32+
private $constNode;
33+
34+
/**
35+
* Name of the class
36+
*
37+
* @var string
38+
*/
39+
private $className;
40+
41+
/**
42+
* Parses class constants from the concrete class node
43+
*
44+
* @param ClassLike $classLikeNode Class-like node
45+
* @param string $reflectionClassName FQN of the class
46+
*
47+
* @return array|ReflectionClassConstant[]
48+
*/
49+
public static function collectFromClassNode(ClassLike $classLikeNode, string $reflectionClassName): array
50+
{
51+
$classConstants = [];
52+
53+
foreach ($classLikeNode->stmts as $classLevelNode) {
54+
if ($classLevelNode instanceof ClassConst) {
55+
foreach ($classLevelNode->consts as $const) {
56+
$classConstName = $const->name->toString();
57+
$classConstants[$classConstName] = new ReflectionClassConstant(
58+
$reflectionClassName,
59+
$classConstName,
60+
$classLevelNode,
61+
$const
62+
);
63+
}
64+
}
65+
}
66+
67+
return $classConstants;
68+
}
69+
70+
/**
71+
* Initializes a reflection for the class constant
72+
*
73+
* @param string $className Name of the class
74+
* @param string $classConstantName Name of the class constant to reflect
75+
* @param ClassConst $classConstNode ClassConstant definition node
76+
* @param Const_|null $constNode Concrete const definition node
77+
*/
78+
public function __construct(
79+
string $className,
80+
string $classConstantName,
81+
ClassConst $classConstNode = null,
82+
Const_ $constNode = null
83+
) {
84+
$this->className = ltrim($className, '\\');
85+
86+
if (!$classConstNode || !$constNode) {
87+
list($classConstNode, $constNode) = ReflectionEngine::parseClassConstant($className, $classConstantName);
88+
}
89+
90+
$this->classConstantNode = $classConstNode;
91+
$this->constNode = $constNode;
92+
}
93+
94+
/**
95+
* @inheritDoc
96+
*/
97+
public function getDeclaringClass()
98+
{
99+
return new ReflectionClass($this->className);
100+
}
101+
102+
/**
103+
* @inheritDoc
104+
*/
105+
public function getDocComment()
106+
{
107+
$docBlock = $this->classConstantNode->getDocComment();
108+
109+
return $docBlock ? $docBlock->getText() : false;
110+
}
111+
112+
/**
113+
* @inheritDoc
114+
*/
115+
public function getModifiers()
116+
{
117+
$modifiers = 0;
118+
if ($this->isPublic()) {
119+
$modifiers += ReflectionMethod::IS_PUBLIC;
120+
}
121+
if ($this->isProtected()) {
122+
$modifiers += ReflectionMethod::IS_PROTECTED;
123+
}
124+
if ($this->isPrivate()) {
125+
$modifiers += ReflectionMethod::IS_PRIVATE;
126+
}
127+
128+
return $modifiers;
129+
}
130+
131+
/**
132+
* @inheritDoc
133+
*/
134+
public function getName()
135+
{
136+
return $this->constNode->name->toString();
137+
}
138+
139+
/**
140+
* @inheritDoc
141+
*/
142+
public function getValue()
143+
{
144+
$solver = new NodeExpressionResolver($this->getDeclaringClass());
145+
$solver->process($this->constNode->value);
146+
return $solver->getValue();
147+
}
148+
149+
/**
150+
* @inheritDoc
151+
*/
152+
public function isPrivate()
153+
{
154+
return $this->classConstantNode->isPrivate();
155+
}
156+
157+
/**
158+
* @inheritDoc
159+
*/
160+
public function isProtected()
161+
{
162+
return $this->classConstantNode->isProtected();
163+
}
164+
165+
/**
166+
* @inheritDoc
167+
*/
168+
public function isPublic()
169+
{
170+
return $this->classConstantNode->isPublic();
171+
}
172+
173+
/**
174+
* @inheritDoc
175+
*/
176+
public function __toString()
177+
{
178+
$value = $this->getValue();
179+
$valueType = new ReflectionType(gettype($value), null, true);
180+
181+
return sprintf(
182+
"Constant [ %s %s %s ] { %s }\n",
183+
implode(' ', \Reflection::getModifierNames($this->getModifiers())),
184+
strtolower((string) ReflectionType::convertToDisplayType($valueType)),
185+
$this->getName(),
186+
(string) $value
187+
);
188+
}
189+
}

src/ReflectionEngine.php

+26
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Go\ParserReflection\NodeVisitor\RootNamespaceNormalizer;
1515
use PhpParser\Lexer;
1616
use PhpParser\Node;
17+
use PhpParser\Node\Stmt\ClassConst;
1718
use PhpParser\Node\Stmt\ClassLike;
1819
use PhpParser\Node\Stmt\ClassMethod;
1920
use PhpParser\Node\Stmt\Namespace_;
@@ -199,6 +200,31 @@ public static function parseClassProperty($fullClassName, $propertyName)
199200
throw new \InvalidArgumentException("Property $propertyName was not found in the $fullClassName");
200201
}
201202

203+
/**
204+
* Parses class constants
205+
*
206+
* @param string $fullClassName
207+
* @param string $constantName
208+
* @return array Pair of [ClassConst and Const_] nodes
209+
*/
210+
public static function parseClassConstant(string $fullClassName, string $constantName): array
211+
{
212+
$class = self::parseClass($fullClassName);
213+
$classNodes = $class->stmts;
214+
215+
foreach ($classNodes as $classLevelNode) {
216+
if ($classLevelNode instanceof ClassConst) {
217+
foreach ($classLevelNode->consts as $classConst) {
218+
if ($classConst->name->toString() === $constantName) {
219+
return [$classLevelNode, $classConst];
220+
}
221+
}
222+
}
223+
}
224+
225+
throw new \InvalidArgumentException("ClassConstant $constantName was not found in the $fullClassName");
226+
}
227+
202228
/**
203229
* Parses a file and returns an AST for it
204230
*

src/Traits/ReflectionClassLikeTrait.php

+45
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
namespace Go\ParserReflection\Traits;
1212

1313
use Go\ParserReflection\ReflectionClass;
14+
use Go\ParserReflection\ReflectionClassConstant;
1415
use Go\ParserReflection\ReflectionException;
1516
use Go\ParserReflection\ReflectionMethod;
1617
use Go\ParserReflection\ReflectionProperty;
@@ -94,6 +95,11 @@ trait ReflectionClassLikeTrait
9495
*/
9596
protected $properties;
9697

98+
/**
99+
* @var array|ReflectionClassConstant[]
100+
*/
101+
protected $classConstants;
102+
97103
/**
98104
* Returns the string representation of the ReflectionClass object.
99105
*
@@ -508,6 +514,45 @@ public function getProperty($name)
508514
return false;
509515
}
510516

517+
/**
518+
* @inheritDoc
519+
*/
520+
public function getReflectionConstant($name)
521+
{
522+
$classConstants = $this->getReflectionConstants();
523+
foreach ($classConstants as $classConstant) {
524+
if ($classConstant->getName() == $name) {
525+
return $classConstant;
526+
}
527+
}
528+
529+
return false;
530+
}
531+
532+
/**
533+
* @inheritDoc
534+
*/
535+
public function getReflectionConstants()
536+
{
537+
if (!isset($this->classConstants)) {
538+
$directClassConstants = ReflectionClassConstant::collectFromClassNode($this->classLikeNode, $this->getName());
539+
$parentClassConstants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
540+
$reflectionClassConstants = [];
541+
foreach ($instance->getReflectionConstants() as $reflectionClassConstant) {
542+
if (!$isParent || !$reflectionClassConstant->isPrivate()) {
543+
$reflectionClassConstants[$reflectionClassConstant->name] = $reflectionClassConstant;
544+
}
545+
}
546+
$result += $reflectionClassConstants;
547+
});
548+
$classConstants = $directClassConstants + $parentClassConstants;
549+
550+
$this->classConstants = $classConstants;
551+
}
552+
553+
return array_values($this->classConstants);
554+
}
555+
511556
/**
512557
* {@inheritDoc}
513558
*/

src/bootstrap.php

-2
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,3 @@
1616
* Go\ParserReflection\ReflectionEngine class
1717
*/
1818
ReflectionEngine::init(new ComposerLocator());
19-
20-
require(__DIR__ . '/polyfill.php');

src/polyfill.php

-34
This file was deleted.

0 commit comments

Comments
 (0)