Skip to content

Commit

Permalink
Added support for ReflectionClassConstant (#97)
Browse files Browse the repository at this point in the history
Added support for ReflectionClassConstant #97, resolves #93
  • Loading branch information
MaximilianKresse authored and lisachenko committed Dec 4, 2019
1 parent 793727d commit 6af734c
Show file tree
Hide file tree
Showing 12 changed files with 423 additions and 46 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
language: php

php:
- 7.0
- 7.1
- 7.2
- hhvm
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ Parser Reflection API Library

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.

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.
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.

[![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)
[![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)
[![Total Downloads](https://img.shields.io/packagist/dt/goaop/parser-reflection.svg)](https://packagist.org/packages/goaop/parser-reflection)
[![Daily Downloads](https://img.shields.io/packagist/dd/goaop/parser-reflection.svg)](https://packagist.org/packages/goaop/parser-reflection)
[![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)
[![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/)
[![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/)
[![License](https://img.shields.io/packagist/l/goaop/parser-reflection.svg)](https://packagist.org/packages/goaop/parser-reflection)

Installation
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
}
},
"require": {
"php": ">=7.0",
"php": ">=7.1",
"nikic/php-parser": "^4.0"
},
"require-dev": {
Expand Down
189 changes: 189 additions & 0 deletions src/ReflectionClassConstant.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php
/**
* Parser Reflection API
*
* @copyright Copyright 2019, Lisachenko Alexander <[email protected]>
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/
declare(strict_types=1);

namespace Go\ParserReflection;

use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
use PhpParser\Node\Const_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassLike;
use \ReflectionClassConstant as BaseReflectionClassConstant;

class ReflectionClassConstant extends BaseReflectionClassConstant
{
/**
* Concrete class constant node
*
* @var ClassConst
*/
private $classConstantNode;

/**
* @var Const_
*/
private $constNode;

/**
* Name of the class
*
* @var string
*/
private $className;

/**
* Parses class constants from the concrete class node
*
* @param ClassLike $classLikeNode Class-like node
* @param string $reflectionClassName FQN of the class
*
* @return array|ReflectionClassConstant[]
*/
public static function collectFromClassNode(ClassLike $classLikeNode, string $reflectionClassName): array
{
$classConstants = [];

foreach ($classLikeNode->stmts as $classLevelNode) {
if ($classLevelNode instanceof ClassConst) {
foreach ($classLevelNode->consts as $const) {
$classConstName = $const->name->toString();
$classConstants[$classConstName] = new ReflectionClassConstant(
$reflectionClassName,
$classConstName,
$classLevelNode,
$const
);
}
}
}

return $classConstants;
}

/**
* Initializes a reflection for the class constant
*
* @param string $className Name of the class
* @param string $classConstantName Name of the class constant to reflect
* @param ClassConst $classConstNode ClassConstant definition node
* @param Const_|null $constNode Concrete const definition node
*/
public function __construct(
string $className,
string $classConstantName,
ClassConst $classConstNode = null,
Const_ $constNode = null
) {
$this->className = ltrim($className, '\\');

if (!$classConstNode || !$constNode) {
list($classConstNode, $constNode) = ReflectionEngine::parseClassConstant($className, $classConstantName);
}

$this->classConstantNode = $classConstNode;
$this->constNode = $constNode;
}

/**
* @inheritDoc
*/
public function getDeclaringClass()
{
return new ReflectionClass($this->className);
}

/**
* @inheritDoc
*/
public function getDocComment()
{
$docBlock = $this->classConstantNode->getDocComment();

return $docBlock ? $docBlock->getText() : false;
}

/**
* @inheritDoc
*/
public function getModifiers()
{
$modifiers = 0;
if ($this->isPublic()) {
$modifiers += ReflectionMethod::IS_PUBLIC;
}
if ($this->isProtected()) {
$modifiers += ReflectionMethod::IS_PROTECTED;
}
if ($this->isPrivate()) {
$modifiers += ReflectionMethod::IS_PRIVATE;
}

return $modifiers;
}

/**
* @inheritDoc
*/
public function getName()
{
return $this->constNode->name->toString();
}

/**
* @inheritDoc
*/
public function getValue()
{
$solver = new NodeExpressionResolver($this->getDeclaringClass());
$solver->process($this->constNode->value);
return $solver->getValue();
}

/**
* @inheritDoc
*/
public function isPrivate()
{
return $this->classConstantNode->isPrivate();
}

/**
* @inheritDoc
*/
public function isProtected()
{
return $this->classConstantNode->isProtected();
}

/**
* @inheritDoc
*/
public function isPublic()
{
return $this->classConstantNode->isPublic();
}

/**
* @inheritDoc
*/
public function __toString()
{
$value = $this->getValue();
$valueType = new ReflectionType(gettype($value), null, true);

return sprintf(
"Constant [ %s %s %s ] { %s }\n",
implode(' ', \Reflection::getModifierNames($this->getModifiers())),
strtolower((string) ReflectionType::convertToDisplayType($valueType)),
$this->getName(),
(string) $value
);
}
}
26 changes: 26 additions & 0 deletions src/ReflectionEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Go\ParserReflection\NodeVisitor\RootNamespaceNormalizer;
use PhpParser\Lexer;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Namespace_;
Expand Down Expand Up @@ -199,6 +200,31 @@ public static function parseClassProperty($fullClassName, $propertyName)
throw new \InvalidArgumentException("Property $propertyName was not found in the $fullClassName");
}

/**
* Parses class constants
*
* @param string $fullClassName
* @param string $constantName
* @return array Pair of [ClassConst and Const_] nodes
*/
public static function parseClassConstant(string $fullClassName, string $constantName): array
{
$class = self::parseClass($fullClassName);
$classNodes = $class->stmts;

foreach ($classNodes as $classLevelNode) {
if ($classLevelNode instanceof ClassConst) {
foreach ($classLevelNode->consts as $classConst) {
if ($classConst->name->toString() === $constantName) {
return [$classLevelNode, $classConst];
}
}
}
}

throw new \InvalidArgumentException("ClassConstant $constantName was not found in the $fullClassName");
}

/**
* Parses a file and returns an AST for it
*
Expand Down
45 changes: 45 additions & 0 deletions src/Traits/ReflectionClassLikeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
namespace Go\ParserReflection\Traits;

use Go\ParserReflection\ReflectionClass;
use Go\ParserReflection\ReflectionClassConstant;
use Go\ParserReflection\ReflectionException;
use Go\ParserReflection\ReflectionMethod;
use Go\ParserReflection\ReflectionProperty;
Expand Down Expand Up @@ -94,6 +95,11 @@ trait ReflectionClassLikeTrait
*/
protected $properties;

/**
* @var array|ReflectionClassConstant[]
*/
protected $classConstants;

/**
* Returns the string representation of the ReflectionClass object.
*
Expand Down Expand Up @@ -508,6 +514,45 @@ public function getProperty($name)
return false;
}

/**
* @inheritDoc
*/
public function getReflectionConstant($name)
{
$classConstants = $this->getReflectionConstants();
foreach ($classConstants as $classConstant) {
if ($classConstant->getName() == $name) {
return $classConstant;
}
}

return false;
}

/**
* @inheritDoc
*/
public function getReflectionConstants()
{
if (!isset($this->classConstants)) {
$directClassConstants = ReflectionClassConstant::collectFromClassNode($this->classLikeNode, $this->getName());
$parentClassConstants = $this->recursiveCollect(function (array &$result, \ReflectionClass $instance, $isParent) {
$reflectionClassConstants = [];
foreach ($instance->getReflectionConstants() as $reflectionClassConstant) {
if (!$isParent || !$reflectionClassConstant->isPrivate()) {
$reflectionClassConstants[$reflectionClassConstant->name] = $reflectionClassConstant;
}
}
$result += $reflectionClassConstants;
});
$classConstants = $directClassConstants + $parentClassConstants;

$this->classConstants = $classConstants;
}

return array_values($this->classConstants);
}

/**
* {@inheritDoc}
*/
Expand Down
2 changes: 0 additions & 2 deletions src/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,3 @@
* Go\ParserReflection\ReflectionEngine class
*/
ReflectionEngine::init(new ComposerLocator());

require(__DIR__ . '/polyfill.php');
34 changes: 0 additions & 34 deletions src/polyfill.php

This file was deleted.

Loading

0 comments on commit 6af734c

Please sign in to comment.