Last active
November 30, 2021 01:03
-
-
Save WinterSilence/ce586345d1ed4b147cc9385aa923ae83 to your computer and use it in GitHub Desktop.
The reflection class reports information about a PHP7 file. Collects functions, interfaces, classes, traits with his constants, properties, methods.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace EnsoStudio; | |
use Reflector; | |
use Reflection; | |
use ReflectionClass; | |
use ReflectionType; | |
use ReflectionNamedType; | |
use ReflectionFunctionAbstract; | |
/** | |
* The reflection class reports information about a PHP >= 7 file. Collects functions/interfaces/classes/traits with | |
* his constants/properties/methods. | |
* | |
* @author [email protected] | |
* @license MIT | |
*/ | |
class ReflectionFile | |
{ | |
/** | |
* Types to fix | |
*/ | |
const FIX_TYPES = ['boolean' => 'bool', 'integer' => 'int']; | |
/** | |
* @var array | |
*/ | |
protected $declared = [ | |
'constants' => [], | |
'functions' => [], | |
'interfaces' => [], | |
'traits' => [], | |
'classes' => [], | |
]; | |
/** | |
* @var array | |
*/ | |
protected $comments = []; | |
/** | |
* @var array | |
*/ | |
protected $constants = []; | |
/** | |
* @var array | |
*/ | |
protected $functions = []; | |
/** | |
* @var array | |
*/ | |
protected $interfaces = []; | |
/** | |
* @var array | |
*/ | |
protected $traits = []; | |
/** | |
* @var array | |
*/ | |
protected $classes = []; | |
/** | |
* @param string $file the path to PHP file | |
*/ | |
public function __construct($file) | |
{ | |
foreach (array_keys($this->declared) as $type) { | |
$this->declared[$type] = $this->getDeclared($type); | |
} | |
require_once realpath($file); | |
} | |
/** | |
* @param string $type | |
* @return array | |
*/ | |
protected function getDeclared($type) | |
{ | |
if (in_array($type, ['constants', 'functions'])) { | |
$result = "get_defined_{$type}"(true); | |
return $result['user'] ?? []; | |
} | |
return "get_declared_{$type}"(); | |
} | |
/** | |
* @param string $comment DocComment | |
* @return array [description: string, tags: array] | |
*/ | |
public function parseComment($comment) | |
{ | |
$description = ''; | |
$tags = []; | |
$matches = []; | |
$comment = trim($comment); | |
if ($comment) { | |
// $comment = strtr(substr($comment, 2, -2), ["\r" => '']); | |
// Remove '/*', '*/' | |
$comment = substr($comment, 2, -2); | |
if (preg_match_all('/^\s*\*\s+?(.*)$/m', $comment, $matches)) { | |
$comment = []; | |
foreach ($matches[1] as $line) { | |
if (substr($line, 0, 1) == '@') { | |
$comment[] = trim($line); | |
} else { | |
$comment[count($comment) - 1] .= ' ' . trim($line); | |
} | |
} | |
foreach ($comment as $line) { | |
if (preg_match('/^@([-a-z]+)\s*(.*)$/mi', $line, $matches)) { | |
list(, $tag, $line) = $matches; | |
if (!isset($tags[$tag])) { | |
$tags[$tag] = []; | |
} | |
if ($tag == 'param') { | |
$line = preg_split('/\s+/', $line, 3); | |
if (count($line) > 1) { | |
$tags[$tag][substr($line[1], 1)] = [ | |
'type' => strtr($line[0], self::FIX_TYPES), | |
'description' => $line[2] ?? '', | |
]; | |
} | |
} elseif (in_array($tag, ['var', 'return', 'throws'])) { | |
$line = preg_split('/\s+/', $line, 2); | |
if (count($line) > 1) { | |
$tags[$tag][] = [ | |
'type' => strtr($line[1], self::FIX_TYPES), | |
'description' => $line[1] ?? '', | |
]; | |
} | |
} else { | |
$tags[$tag][] = $line; | |
} | |
} else { | |
$description = $line; | |
} | |
} | |
} | |
} | |
return compact('description', 'tags'); | |
} | |
/** | |
* Parse comment. | |
* | |
* @param Reflector $r | |
* @param string|null $returnTag | |
* @return mixed | |
*/ | |
protected function getComment(Reflector $r, $returnTag = null) | |
{ | |
if (property_exists($r, 'class')) { | |
$key = $r->class . '::' . $r->name; | |
} else { | |
$key = $r->name; | |
} | |
if (!isset($this->comments[$key])) { | |
$comment = $r->getDocComment(); | |
$this->comments[$key] = $this->parseComment($comment); | |
} | |
if ($returnTag) { | |
return $this->comments[$key]['tags'][$returnTag] ?? null; | |
} | |
return $this->comments[$key]; | |
} | |
/** | |
* Returns modifier names. | |
* | |
* @param Reflector $r | |
* @return array | |
*/ | |
public function getModifierNames(Reflector $r) | |
{ | |
return Reflection::getModifierNames($r->getModifiers()); | |
} | |
/** | |
* Returns type name. | |
* | |
* @param ReflectionType $type | |
* @return string | |
*/ | |
public function getTypeName(ReflectionType $type) | |
{ | |
if ($type instanceof ReflectionNamedType) { | |
return $type->getName(); | |
} | |
return (string) $type; | |
} | |
/** | |
* Returns declaring class. | |
* | |
* @param Reflector $reflection the class to reflect | |
* @return string | |
*/ | |
public function getDeclaringClass(Reflector $reflection) | |
{ | |
$declaringClass = $reflection->getDeclaringClass(); | |
return $declaringClass === $reflection->class ? 'self' : $declaringClass; | |
} | |
/** | |
* Returns class constants. | |
* | |
* @param Reflector $class the class to reflect | |
* @return array | |
*/ | |
public function getClassConstants(Reflector $class) | |
{ | |
$constants = []; | |
foreach ($class->getConstants() as $name => $value) { | |
$constant = $class->getReflectionConstant($name); | |
$constants[$name] = [ | |
'value' => $value, | |
'modifiers' => $this->getModifierNames($constant), | |
'description' => $this->getComment($constant), | |
'inherited' => $this->getDeclaringClass($constant), | |
]; | |
} | |
return $constants; | |
} | |
/** | |
* Returns class properties. | |
* | |
* @param Reflector $class the class to reflect | |
* @return array | |
*/ | |
public function getProperties(Reflector $class) | |
{ | |
$properties = []; | |
foreach ($class->getDefaultProperties() as $name => $value) { | |
$property = $class->getProperty($name); | |
$comment = $this->getComment($property); | |
if (preg_match('/@var\s+([^\s]+)(.*)/i', $comment, $matches)) { | |
$type = $matches[1]; | |
$comment = trim($matches[2]); | |
} else { | |
$type = 'null'; | |
} | |
$properties[$name] = [ | |
'type' => $type, | |
'value' => $value, | |
'modifiers' => $this->getModifierNames($property), | |
'description' => $comment, | |
'inherited' => $this->getDeclaringClass($property), | |
]; | |
} | |
return $properties; | |
} | |
/** | |
* Returns result of function/method. | |
* | |
* @param ReflectionFunction|ReflectionMethod $function function/method | |
* @return string | |
*/ | |
public function getFunctionReturn(ReflectionFunctionAbstract $function) | |
{ | |
if ($function->hasReturnType()) { | |
return $this->getTypeName($function->getReturnType()); | |
} | |
$comment = $this->getComment($function); | |
if (!empty($comment) && preg_match('/@return\s+([^\s\[]+)/ui', $comment, $matches) === 1) { | |
return $matches[1]; | |
} | |
return 'void'; | |
} | |
/** | |
* Returns parameters of function/method. | |
* | |
* @param ReflectionFunction|ReflectionMethod $function function/method | |
* @return array | |
*/ | |
public function getFunctionParameters(ReflectionFunctionAbstract $function) | |
{ | |
$params = []; | |
$tags = $this->getComment($function, 'param'); | |
foreach ($function->getParameters() as $param) { | |
if ($param->isDefaultValueAvailable()) { | |
if ($param->isDefaultValueConstant()) { | |
$value = 'const ' . $param->getDefaultValueConstantName(); | |
} else { | |
$value = $param->getDefaultValue(); | |
} | |
} else { | |
$value = null; | |
} | |
$name = $param->getName(); | |
$type = $comment = ''; | |
if ($param->hasType()) { | |
$type = $this->getTypeName($param->getType()); | |
} elseif (isset($tags[$name])) { | |
$type = $tags[$name][0]; | |
if (!empty($tags[$name][1])) { | |
$comment = $tags[$name][1]; | |
} | |
} | |
$params[$name] = [ | |
'type' => $type, | |
'value' => $value, | |
'description' => $comment, | |
'optional' => $param->isOptional(), | |
]; | |
} | |
return $params; | |
} | |
/** | |
* @param string $type | |
* @return array | |
*/ | |
public function get($type) | |
{ | |
if (!is_array($this->{$type})) { | |
$this->{$type} = array_diff($this->getDeclared($type), $this->declared[$type]); | |
unset($this->declared[$type]); | |
foreach ($this->{$type} as $key => $name) { | |
$class = new ReflectionClass($name); | |
$parentClass = $class->getParentClass(); | |
$this->{$type}[$key] = [ | |
'parent' => $parentClass ? $parentClass->getName() : '', | |
'interfaces' => $class->getInterfaceNames(), | |
'traits' => $class->isInterface() ? [] : $class->getTraitNames(), | |
'modifiers' => $this->getModifierNames($class), | |
'description' => $this->getComment($class), | |
'constants' => $this->getClassConstants($class), | |
'properties' => $class->isInterface() ? [] : $this->getProperties($class), | |
'methods' => $this->getMethods($class), | |
]; | |
} | |
} | |
return $this->{$type}; | |
} | |
/** | |
* Returns class methods. | |
* | |
* @param Reflector $class the class to reflect | |
* @return array | |
*/ | |
public function getMethods(Reflector $class) | |
{ | |
$methods = []; | |
foreach ($class->getMethods() as $name) { | |
$method = $class->getMethod($name); | |
$methods[$name] = [ | |
'parameters' => $this->getFunctionParameters(), | |
'return' => $this->getFunctionReturn($method), | |
'modifiers' => $this->getModifierNames($method), | |
'description' => $this->getComment($method), | |
'inherited' => $this->getDeclaringClass($method), | |
]; | |
} | |
return $methods; | |
} | |
/** | |
* @return array | |
*/ | |
public function getConstants() | |
{ | |
return $this->get('constants'); | |
} | |
/** | |
* @return array | |
*/ | |
public function getFunctions() | |
{ | |
return $this->get('functions'); | |
} | |
/** | |
* @return array | |
*/ | |
public function getInterfaces() | |
{ | |
return $this->get('interfaces'); | |
} | |
/** | |
* @return array | |
*/ | |
public function getTraits() | |
{ | |
return $this->get('traits'); | |
} | |
/** | |
* @return array | |
*/ | |
public function getClasses() | |
{ | |
return $this->get('classes'); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// Explore php file | |
$reflection = new Enso\ReflectionFile('path/to/class.php'); | |
var_dump( | |
$reflection->getConstants(), | |
$reflection->getFunctions(), | |
$reflection->getInterfaces(), | |
$reflection->getTraits(), | |
$reflection->getClasses() | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment