代码初始化
This commit is contained in:
26
addons/epay/library/hyperf/utils/src/CodeGen/Package.php
Normal file
26
addons/epay/library/hyperf/utils/src/CodeGen/Package.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\Utils\CodeGen;
|
||||
|
||||
use Jean85\PrettyVersions;
|
||||
|
||||
class Package
|
||||
{
|
||||
public static function getPrettyVersion(string $package): string
|
||||
{
|
||||
try {
|
||||
return (string) PrettyVersions::getVersion($package);
|
||||
} catch (\Throwable $exception) {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
247
addons/epay/library/hyperf/utils/src/CodeGen/PhpDocReader.php
Normal file
247
addons/epay/library/hyperf/utils/src/CodeGen/PhpDocReader.php
Normal file
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\Utils\CodeGen;
|
||||
|
||||
use PhpDocReader\AnnotationException;
|
||||
use PhpDocReader\PhpParser\UseStatementParser;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionParameter;
|
||||
use ReflectionProperty;
|
||||
use Reflector;
|
||||
|
||||
/**
|
||||
* @see https://github.com/PHP-DI/PhpDocReader
|
||||
*/
|
||||
class PhpDocReader
|
||||
{
|
||||
private const PRIMITIVE_TYPES = [
|
||||
'bool' => 'bool',
|
||||
'boolean' => 'bool',
|
||||
'string' => 'string',
|
||||
'int' => 'int',
|
||||
'integer' => 'int',
|
||||
'float' => 'float',
|
||||
'double' => 'float',
|
||||
'array' => 'array',
|
||||
'object' => 'object',
|
||||
'callable' => 'callable',
|
||||
'resource' => 'resource',
|
||||
'mixed' => 'mixed',
|
||||
'iterable' => 'iterable',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var null|PhpDocReader
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/** @var UseStatementParser */
|
||||
private $parser;
|
||||
|
||||
/** @var bool */
|
||||
private $ignorePhpDocErrors;
|
||||
|
||||
/**
|
||||
* @param bool $ignorePhpDocErrors enable or disable throwing errors when PhpDoc errors occur (when parsing annotations)
|
||||
*/
|
||||
public function __construct(bool $ignorePhpDocErrors = false)
|
||||
{
|
||||
$this->parser = new UseStatementParser();
|
||||
$this->ignorePhpDocErrors = $ignorePhpDocErrors;
|
||||
}
|
||||
|
||||
public static function getInstance(): PhpDocReader
|
||||
{
|
||||
if (static::$instance) {
|
||||
return static::$instance;
|
||||
}
|
||||
return static::$instance = new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the docblock of the property to get the type (class or primitive type) of the param annotation.
|
||||
*
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function getReturnType(ReflectionMethod $method, bool $withoutNamespace = false): array
|
||||
{
|
||||
return $this->readReturnClass($method, true, $withoutNamespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the docblock of the property to get the class of the param annotation.
|
||||
*
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function getReturnClass(ReflectionMethod $method, bool $withoutNamespace = false): array
|
||||
{
|
||||
return $this->readReturnClass($method, false, $withoutNamespace);
|
||||
}
|
||||
|
||||
protected function readReturnClass(ReflectionMethod $method, bool $allowPrimitiveTypes, bool $withoutNamespace = false): array
|
||||
{
|
||||
// Use reflection
|
||||
$returnType = $method->getReturnType();
|
||||
if ($returnType instanceof \ReflectionNamedType) {
|
||||
if (! $returnType->isBuiltin() || $allowPrimitiveTypes) {
|
||||
return [($returnType->allowsNull() ? '?' : '') . $returnType->getName()];
|
||||
}
|
||||
}
|
||||
|
||||
$docComment = $method->getDocComment();
|
||||
if (! $docComment) {
|
||||
return ['mixed'];
|
||||
}
|
||||
if (preg_match('/@return\s+([^\s]+)\s+/', $docComment, $matches)) {
|
||||
[, $type] = $matches;
|
||||
} else {
|
||||
return ['mixed'];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
$class = $method->getDeclaringClass();
|
||||
$types = explode('|', $type);
|
||||
foreach ($types as $type) {
|
||||
// Ignore primitive types
|
||||
if (isset(self::PRIMITIVE_TYPES[$type])) {
|
||||
if ($allowPrimitiveTypes) {
|
||||
$result[] = self::PRIMITIVE_TYPES[$type];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore types containing special characters ([], <> ...)
|
||||
if (! preg_match('/^[a-zA-Z0-9\\\\_]+$/', $type)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the class name is not fully qualified (i.e. doesn't start with a \)
|
||||
if ($type[0] !== '\\' && ! $withoutNamespace) {
|
||||
// Try to resolve the FQN using the class context
|
||||
$resolvedType = $this->tryResolveFqn($type, $class, $method);
|
||||
|
||||
if (! $resolvedType && ! $this->ignorePhpDocErrors) {
|
||||
throw new AnnotationException(sprintf(
|
||||
'The @return annotation for parameter "%s" of %s::%s contains a non existent class "%s". '
|
||||
. 'Did you maybe forget to add a "use" statement for this annotation?',
|
||||
$method,
|
||||
$class->name,
|
||||
$method->name,
|
||||
$type
|
||||
));
|
||||
}
|
||||
|
||||
$type = $resolvedType;
|
||||
}
|
||||
|
||||
if (! $this->ignorePhpDocErrors && ! $withoutNamespace && ! $this->classExists($type)) {
|
||||
throw new AnnotationException(sprintf(
|
||||
'The @return annotation for parameter "%s" of %s::%s contains a non existent class "%s"',
|
||||
$method,
|
||||
$class->name,
|
||||
$method->name,
|
||||
$type
|
||||
));
|
||||
}
|
||||
|
||||
// Remove the leading \ (FQN shouldn't contain it)
|
||||
$result[] = is_string($type) ? ltrim($type, '\\') : null;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to resolve the FQN of the provided $type based on the $class and $member context.
|
||||
*
|
||||
* @return null|string Fully qualified name of the type, or null if it could not be resolved
|
||||
*/
|
||||
protected function tryResolveFqn(string $type, ReflectionClass $class, Reflector $member): ?string
|
||||
{
|
||||
$alias = ($pos = strpos($type, '\\')) === false ? $type : substr($type, 0, $pos);
|
||||
$loweredAlias = strtolower($alias);
|
||||
|
||||
// Retrieve "use" statements
|
||||
$uses = $this->parser->parseUseStatements($class);
|
||||
|
||||
if (isset($uses[$loweredAlias])) {
|
||||
// Imported classes
|
||||
if ($pos !== false) {
|
||||
return $uses[$loweredAlias] . substr($type, $pos);
|
||||
}
|
||||
return $uses[$loweredAlias];
|
||||
}
|
||||
|
||||
if ($this->classExists($class->getNamespaceName() . '\\' . $type)) {
|
||||
return $class->getNamespaceName() . '\\' . $type;
|
||||
}
|
||||
|
||||
if (isset($uses['__NAMESPACE__']) && $this->classExists($uses['__NAMESPACE__'] . '\\' . $type)) {
|
||||
// Class namespace
|
||||
return $uses['__NAMESPACE__'] . '\\' . $type;
|
||||
}
|
||||
|
||||
if ($this->classExists($type)) {
|
||||
// No namespace
|
||||
return $type;
|
||||
}
|
||||
|
||||
// If all fail, try resolving through related traits
|
||||
return $this->tryResolveFqnInTraits($type, $class, $member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to resolve the FQN of the provided $type based on the $class and $member context, specifically searching
|
||||
* through the traits that are used by the provided $class.
|
||||
*
|
||||
* @return null|string Fully qualified name of the type, or null if it could not be resolved
|
||||
*/
|
||||
protected function tryResolveFqnInTraits(string $type, ReflectionClass $class, Reflector $member): ?string
|
||||
{
|
||||
/** @var ReflectionClass[] $traits */
|
||||
$traits = [];
|
||||
|
||||
// Get traits for the class and its parents
|
||||
while ($class) {
|
||||
$traits = array_merge($traits, $class->getTraits());
|
||||
$class = $class->getParentClass();
|
||||
}
|
||||
|
||||
foreach ($traits as $trait) {
|
||||
// Eliminate traits that don't have the property/method/parameter
|
||||
if ($member instanceof ReflectionProperty && ! $trait->hasProperty($member->name)) {
|
||||
continue;
|
||||
}
|
||||
if ($member instanceof ReflectionMethod && ! $trait->hasMethod($member->name)) {
|
||||
continue;
|
||||
}
|
||||
if ($member instanceof ReflectionParameter && ! $trait->hasMethod($member->getDeclaringFunction()->name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Run the resolver again with the ReflectionClass instance for the trait
|
||||
$resolvedType = $this->tryResolveFqn($type, $trait, $member);
|
||||
|
||||
if ($resolvedType) {
|
||||
return $resolvedType;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function classExists(string $class): bool
|
||||
{
|
||||
return class_exists($class) || interface_exists($class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\Utils\CodeGen;
|
||||
|
||||
use PhpDocReader\PhpDocReader;
|
||||
|
||||
class PhpDocReaderManager
|
||||
{
|
||||
/**
|
||||
* @var null|PhpDocReader
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
public static function getInstance(): PhpDocReader
|
||||
{
|
||||
if (static::$instance) {
|
||||
return static::$instance;
|
||||
}
|
||||
return static::$instance = new PhpDocReader();
|
||||
}
|
||||
}
|
||||
156
addons/epay/library/hyperf/utils/src/CodeGen/PhpParser.php
Normal file
156
addons/epay/library/hyperf/utils/src/CodeGen/PhpParser.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\Utils\CodeGen;
|
||||
|
||||
use Hyperf\Utils\Exception\InvalidArgumentException;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Parser;
|
||||
use PhpParser\ParserFactory;
|
||||
use ReflectionClass;
|
||||
use ReflectionParameter;
|
||||
|
||||
class PhpParser
|
||||
{
|
||||
public const TYPES = [
|
||||
'int',
|
||||
'float',
|
||||
'string',
|
||||
'bool',
|
||||
'array',
|
||||
'object',
|
||||
'resource',
|
||||
'mixed',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var null|PhpParser
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* @var Parser
|
||||
*/
|
||||
protected $parser;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$parserFactory = new ParserFactory();
|
||||
$this->parser = $parserFactory->create(ParserFactory::ONLY_PHP7);
|
||||
}
|
||||
|
||||
public static function getInstance(): PhpParser
|
||||
{
|
||||
if (static::$instance) {
|
||||
return static::$instance;
|
||||
}
|
||||
return static::$instance = new static();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|Node\Stmt[]
|
||||
*/
|
||||
public function getNodesFromReflectionClass(ReflectionClass $reflectionClass): ?array
|
||||
{
|
||||
$code = file_get_contents($reflectionClass->getFileName());
|
||||
return $this->parser->parse($code);
|
||||
}
|
||||
|
||||
public function getNodeFromReflectionParameter(ReflectionParameter $parameter): Node\Param
|
||||
{
|
||||
$result = new Node\Param(
|
||||
new Node\Expr\Variable($parameter->getName())
|
||||
);
|
||||
|
||||
if ($parameter->isDefaultValueAvailable()) {
|
||||
$result->default = $this->getExprFromValue($parameter->getDefaultValue());
|
||||
}
|
||||
|
||||
if ($parameter->hasType()) {
|
||||
/** @var \ReflectionNamedType|\ReflectionUnionType $reflection */
|
||||
$reflection = $parameter->getType();
|
||||
if ($reflection instanceof \ReflectionUnionType) {
|
||||
$unionType = [];
|
||||
foreach ($reflection->getTypes() as $objType) {
|
||||
$type = $objType->getName();
|
||||
if (! in_array($type, static::TYPES)) {
|
||||
$unionType[] = new Node\Name('\\' . $type);
|
||||
} else {
|
||||
$unionType[] = new Node\Identifier($type);
|
||||
}
|
||||
}
|
||||
$result->type = new Node\UnionType($unionType);
|
||||
} else {
|
||||
$type = $reflection->getName();
|
||||
if (! in_array($type, static::TYPES)) {
|
||||
$result->type = new Node\Name('\\' . $type);
|
||||
} else {
|
||||
$result->type = new Node\Identifier($type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($parameter->isPassedByReference()) {
|
||||
$result->byRef = true;
|
||||
}
|
||||
|
||||
if ($parameter->isVariadic()) {
|
||||
$result->variadic = true;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getExprFromValue($value): Node\Expr
|
||||
{
|
||||
switch (gettype($value)) {
|
||||
case 'array':
|
||||
return new Node\Expr\Array_($value);
|
||||
case 'string':
|
||||
return new Node\Scalar\String_($value);
|
||||
case 'integer':
|
||||
return new Node\Scalar\LNumber($value);
|
||||
case 'double':
|
||||
return new Node\Scalar\DNumber($value);
|
||||
case 'NULL':
|
||||
return new Node\Expr\ConstFetch(new Node\Name('null'));
|
||||
case 'boolean':
|
||||
return new Node\Expr\ConstFetch(new Node\Name($value ? 'true' : 'false'));
|
||||
default:
|
||||
throw new InvalidArgumentException($value . ' is invalid');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Node\Stmt\ClassMethod[]
|
||||
*/
|
||||
public function getAllMethodsFromStmts(array $stmts): array
|
||||
{
|
||||
$methods = [];
|
||||
foreach ($stmts as $namespace) {
|
||||
if (! $namespace instanceof Node\Stmt\Namespace_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($namespace->stmts as $class) {
|
||||
if (! $class instanceof Node\Stmt\Class_ && ! $class instanceof Node\Stmt\Interface_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($class->getMethods() as $method) {
|
||||
$methods[] = $method;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $methods;
|
||||
}
|
||||
}
|
||||
69
addons/epay/library/hyperf/utils/src/CodeGen/Project.php
Normal file
69
addons/epay/library/hyperf/utils/src/CodeGen/Project.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* This file is part of Hyperf.
|
||||
*
|
||||
* @link https://www.hyperf.io
|
||||
* @document https://hyperf.wiki
|
||||
* @contact group@hyperf.io
|
||||
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
|
||||
*/
|
||||
namespace Hyperf\Utils\CodeGen;
|
||||
|
||||
use Hyperf\Utils\Composer;
|
||||
use Hyperf\Utils\Str;
|
||||
|
||||
/**
|
||||
* Read composer.json autoload psr-4 rules to figure out the namespace or path.
|
||||
*/
|
||||
class Project
|
||||
{
|
||||
public function namespace(string $path): string
|
||||
{
|
||||
$ext = pathinfo($path, PATHINFO_EXTENSION);
|
||||
|
||||
if ($ext !== '') {
|
||||
$path = substr($path, 0, -(strlen($ext) + 1));
|
||||
} else {
|
||||
$path = trim($path, '/') . '/';
|
||||
}
|
||||
|
||||
foreach ($this->getAutoloadRules() as $prefix => $prefixPath) {
|
||||
if ($this->isRootNamespace($prefix) || strpos($path, $prefixPath) === 0) {
|
||||
return $prefix . str_replace('/', '\\', substr($path, strlen($prefixPath)));
|
||||
}
|
||||
}
|
||||
throw new \RuntimeException("Invalid project path: {$path}");
|
||||
}
|
||||
|
||||
public function className(string $path): string
|
||||
{
|
||||
return $this->namespace($path);
|
||||
}
|
||||
|
||||
public function path(string $name, $extension = '.php'): string
|
||||
{
|
||||
if (Str::endsWith($name, '\\')) {
|
||||
$extension = '';
|
||||
}
|
||||
|
||||
foreach ($this->getAutoloadRules() as $prefix => $prefixPath) {
|
||||
if ($this->isRootNamespace($prefix) || strpos($name, $prefix) === 0) {
|
||||
return $prefixPath . str_replace('\\', '/', substr($name, strlen($prefix))) . $extension;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException("Invalid class name: {$name}");
|
||||
}
|
||||
|
||||
protected function isRootNamespace(string $namespace): bool
|
||||
{
|
||||
return $namespace === '';
|
||||
}
|
||||
|
||||
protected function getAutoloadRules(): array
|
||||
{
|
||||
return data_get(Composer::getJsonContent(), 'autoload.psr-4', []);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user