mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-18 05:48:21 +01:00
Updated Rector to commit ec61753f20dbabd4b4b350f9d7a994ac61315e82
ec61753f20
[PHP 8.0] Deprecate old UnionTypesRector that is dangerously moving docblocks, use type declaration rules instead (#3846)
This commit is contained in:
parent
535f180737
commit
98e78b2b7c
@ -21,7 +21,6 @@ use Rector\Php80\Rector\FuncCall\ClassOnObjectRector;
|
||||
use Rector\Php80\Rector\FuncCall\Php8ResourceReturnToObjectRector;
|
||||
use Rector\Php80\Rector\FuncCall\TokenGetAllToObjectRector;
|
||||
use Rector\Php80\Rector\FunctionLike\MixedTypeRector;
|
||||
use Rector\Php80\Rector\FunctionLike\UnionTypesRector;
|
||||
use Rector\Php80\Rector\Identical\StrEndsWithRector;
|
||||
use Rector\Php80\Rector\Identical\StrStartsWithRector;
|
||||
use Rector\Php80\Rector\NotIdentical\StrContainsRector;
|
||||
@ -31,7 +30,6 @@ use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
|
||||
use Rector\Transform\Rector\StaticCall\StaticCallToFuncCallRector;
|
||||
use Rector\Transform\ValueObject\StaticCallToFuncCall;
|
||||
return static function (RectorConfig $rectorConfig) : void {
|
||||
$rectorConfig->rule(UnionTypesRector::class);
|
||||
$rectorConfig->rule(StrContainsRector::class);
|
||||
$rectorConfig->rule(StrStartsWithRector::class);
|
||||
$rectorConfig->rule(StrEndsWithRector::class);
|
||||
|
@ -38,22 +38,6 @@ final class ClassMethodParamVendorLockResolver
|
||||
$this->reflectionResolver = $reflectionResolver;
|
||||
$this->filePathHelper = $filePathHelper;
|
||||
}
|
||||
/**
|
||||
* Includes non-vendor classes
|
||||
*/
|
||||
public function isSoftLocked(ClassMethod $classMethod) : bool
|
||||
{
|
||||
if ($this->isVendorLocked($classMethod)) {
|
||||
return \true;
|
||||
}
|
||||
$classReflection = $this->reflectionResolver->resolveClassReflection($classMethod);
|
||||
if (!$classReflection instanceof ClassReflection) {
|
||||
return \false;
|
||||
}
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
return $this->hasClassMethodLockMatchingFileName($classReflection, $methodName, '');
|
||||
}
|
||||
public function isVendorLocked(ClassMethod $classMethod) : bool
|
||||
{
|
||||
if ($classMethod->isMagic()) {
|
||||
|
@ -3,8 +3,8 @@
|
||||
declare (strict_types=1);
|
||||
namespace Rector\DeadCode\PhpDoc;
|
||||
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\Trait_;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
|
||||
@ -58,17 +58,17 @@ final class DeadReturnTagValueNodeAnalyzer
|
||||
$this->standaloneTypeRemovalGuard = $standaloneTypeRemovalGuard;
|
||||
$this->phpDocTypeChanger = $phpDocTypeChanger;
|
||||
}
|
||||
public function isDead(ReturnTagValueNode $returnTagValueNode, FunctionLike $functionLike) : bool
|
||||
public function isDead(ReturnTagValueNode $returnTagValueNode, ClassMethod $classMethod) : bool
|
||||
{
|
||||
$returnType = $functionLike->getReturnType();
|
||||
$returnType = $classMethod->getReturnType();
|
||||
if ($returnType === null) {
|
||||
return \false;
|
||||
}
|
||||
$classLike = $this->betterNodeFinder->findParentType($functionLike, ClassLike::class);
|
||||
$classLike = $this->betterNodeFinder->findParentType($classMethod, ClassLike::class);
|
||||
if ($classLike instanceof Trait_ && $returnTagValueNode->type instanceof ThisTypeNode) {
|
||||
return \false;
|
||||
}
|
||||
if (!$this->typeComparator->arePhpParserAndPhpStanPhpDocTypesEqual($returnType, $returnTagValueNode->type, $functionLike)) {
|
||||
if (!$this->typeComparator->arePhpParserAndPhpStanPhpDocTypesEqual($returnType, $returnTagValueNode->type, $classMethod)) {
|
||||
return \false;
|
||||
}
|
||||
if ($this->phpDocTypeChanger->isAllowed($returnTagValueNode->type)) {
|
||||
|
@ -3,7 +3,7 @@
|
||||
declare (strict_types=1);
|
||||
namespace Rector\DeadCode\PhpDoc\TagRemover;
|
||||
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\DeadCode\PhpDoc\DeadReturnTagValueNodeAnalyzer;
|
||||
@ -18,14 +18,14 @@ final class ReturnTagRemover
|
||||
{
|
||||
$this->deadReturnTagValueNodeAnalyzer = $deadReturnTagValueNodeAnalyzer;
|
||||
}
|
||||
public function removeReturnTagIfUseless(PhpDocInfo $phpDocInfo, FunctionLike $functionLike) : bool
|
||||
public function removeReturnTagIfUseless(PhpDocInfo $phpDocInfo, ClassMethod $classMethod) : bool
|
||||
{
|
||||
// remove existing type
|
||||
$returnTagValueNode = $phpDocInfo->getReturnTagValue();
|
||||
if (!$returnTagValueNode instanceof ReturnTagValueNode) {
|
||||
return \false;
|
||||
}
|
||||
$isReturnTagValueDead = $this->deadReturnTagValueNodeAnalyzer->isDead($returnTagValueNode, $functionLike);
|
||||
$isReturnTagValueDead = $this->deadReturnTagValueNodeAnalyzer->isDead($returnTagValueNode, $classMethod);
|
||||
if (!$isReturnTagValueDead) {
|
||||
return \false;
|
||||
}
|
||||
|
@ -4,80 +4,20 @@ declare (strict_types=1);
|
||||
namespace Rector\Php80\Rector\FunctionLike;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\ComplexType;
|
||||
use PhpParser\Node\Expr\ArrowFunction;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\UnionType as PhpParserUnionType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Rector\DeadCode\PhpDoc\TagRemover\ParamTagRemover;
|
||||
use Rector\DeadCode\PhpDoc\TagRemover\ReturnTagRemover;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
|
||||
use Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeAnalyzer;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodParamVendorLockResolver;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard;
|
||||
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
/**
|
||||
* @see \Rector\Tests\Php80\Rector\FunctionLike\UnionTypesRector\UnionTypesRectorTest
|
||||
* @deprecated This split is deprecated as dangerous to rely on doblock strings. Instead, use strict type declaration rules.
|
||||
*/
|
||||
final class UnionTypesRector extends AbstractRector implements MinPhpVersionInterface
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $hasChanged = \false;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\DeadCode\PhpDoc\TagRemover\ReturnTagRemover
|
||||
*/
|
||||
private $returnTagRemover;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\DeadCode\PhpDoc\TagRemover\ParamTagRemover
|
||||
*/
|
||||
private $paramTagRemover;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\VendorLocker\NodeVendorLocker\ClassMethodParamVendorLockResolver
|
||||
*/
|
||||
private $classMethodParamVendorLockResolver;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard
|
||||
*/
|
||||
private $classMethodReturnTypeOverrideGuard;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeAnalyzer
|
||||
*/
|
||||
private $unionTypeAnalyzer;
|
||||
/**
|
||||
* @readonly
|
||||
* @var \Rector\NodeTypeResolver\PHPStan\Type\TypeFactory
|
||||
*/
|
||||
private $typeFactory;
|
||||
public function __construct(ReturnTagRemover $returnTagRemover, ParamTagRemover $paramTagRemover, ClassMethodParamVendorLockResolver $classMethodParamVendorLockResolver, ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, UnionTypeAnalyzer $unionTypeAnalyzer, TypeFactory $typeFactory)
|
||||
{
|
||||
$this->returnTagRemover = $returnTagRemover;
|
||||
$this->paramTagRemover = $paramTagRemover;
|
||||
$this->classMethodParamVendorLockResolver = $classMethodParamVendorLockResolver;
|
||||
$this->classMethodReturnTypeOverrideGuard = $classMethodReturnTypeOverrideGuard;
|
||||
$this->unionTypeAnalyzer = $unionTypeAnalyzer;
|
||||
$this->typeFactory = $typeFactory;
|
||||
}
|
||||
public function getRuleDefinition() : RuleDefinition
|
||||
{
|
||||
return new RuleDefinition('Change docs types to union types, where possible (properties are covered by TypedPropertiesRector)', [new CodeSample(<<<'CODE_SAMPLE'
|
||||
@ -114,148 +54,13 @@ CODE_SAMPLE
|
||||
*/
|
||||
public function refactor(Node $node) : ?Node
|
||||
{
|
||||
$this->hasChanged = \false;
|
||||
if ($node instanceof ClassMethod && $this->classMethodReturnTypeOverrideGuard->shouldSkipClassMethod($node)) {
|
||||
return null;
|
||||
}
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
|
||||
$this->refactorParamTypes($node, $phpDocInfo);
|
||||
$this->refactorReturnType($node, $phpDocInfo);
|
||||
$hasChanged = $this->paramTagRemover->removeParamTagsIfUseless($phpDocInfo, $node);
|
||||
if ($hasChanged) {
|
||||
$this->hasChanged = \true;
|
||||
}
|
||||
$hasReturnChanged = $this->returnTagRemover->removeReturnTagIfUseless($phpDocInfo, $node);
|
||||
if ($hasReturnChanged) {
|
||||
$this->hasChanged = \true;
|
||||
}
|
||||
if ($this->hasChanged) {
|
||||
return $node;
|
||||
}
|
||||
$errorMessage = \sprintf('Rule "%s" is deprecated, as dangerous to move docblocks for type declarations. Use strict type rules instead', self::class);
|
||||
\trigger_error($errorMessage, \E_USER_WARNING);
|
||||
\sleep(3);
|
||||
return null;
|
||||
}
|
||||
public function provideMinPhpVersion() : int
|
||||
{
|
||||
return PhpVersionFeature::UNION_TYPES;
|
||||
}
|
||||
/**
|
||||
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\Closure|\PhpParser\Node\Expr\ArrowFunction $functionLike
|
||||
*/
|
||||
private function refactorParamTypes($functionLike, PhpDocInfo $phpDocInfo) : void
|
||||
{
|
||||
// skip parent class lock too, just to be safe in case of different parent docs
|
||||
if ($functionLike instanceof ClassMethod && $this->classMethodParamVendorLockResolver->isSoftLocked($functionLike)) {
|
||||
return;
|
||||
}
|
||||
foreach ($functionLike->getParams() as $param) {
|
||||
/** @var string $paramName */
|
||||
$paramName = $this->getName($param->var);
|
||||
$paramType = $phpDocInfo->getParamType($paramName);
|
||||
if (!$paramType instanceof UnionType) {
|
||||
continue;
|
||||
}
|
||||
if ($this->unionTypeAnalyzer->hasObjectWithoutClassType($paramType)) {
|
||||
$this->changeObjectWithoutClassType($param, $paramType);
|
||||
continue;
|
||||
}
|
||||
$uniqueatedParamType = $this->filterOutDuplicatedArrayTypes($paramType);
|
||||
if (!$uniqueatedParamType instanceof UnionType) {
|
||||
continue;
|
||||
}
|
||||
// mixed has to be standalone type, cannot be part of union type declaration
|
||||
if ($paramType->isSuperTypeOf(new MixedType())->yes()) {
|
||||
continue;
|
||||
}
|
||||
$phpParserUnionType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($uniqueatedParamType, TypeKind::PARAM);
|
||||
if ($this->shouldSkipParamTypeRefactor($param->type, $phpParserUnionType)) {
|
||||
continue;
|
||||
}
|
||||
$param->type = $phpParserUnionType;
|
||||
$this->hasChanged = \true;
|
||||
}
|
||||
}
|
||||
private function changeObjectWithoutClassType(Param $param, UnionType $unionType) : void
|
||||
{
|
||||
if (!$this->unionTypeAnalyzer->hasObjectWithoutClassTypeWithOnlyFullyQualifiedObjectType($unionType)) {
|
||||
return;
|
||||
}
|
||||
$param->type = new Identifier('object');
|
||||
$this->hasChanged = \true;
|
||||
}
|
||||
/**
|
||||
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Expr\Closure|\PhpParser\Node\Expr\ArrowFunction $functionLike
|
||||
*/
|
||||
private function refactorReturnType($functionLike, PhpDocInfo $phpDocInfo) : void
|
||||
{
|
||||
// do not override existing return type
|
||||
if ($functionLike->getReturnType() !== null) {
|
||||
return;
|
||||
}
|
||||
$returnType = $phpDocInfo->getReturnType();
|
||||
if (!$returnType instanceof UnionType) {
|
||||
return;
|
||||
}
|
||||
$uniqueatedReturnType = $this->filterOutDuplicatedArrayTypes($returnType);
|
||||
if (!$uniqueatedReturnType instanceof UnionType) {
|
||||
return;
|
||||
}
|
||||
// mixed has to be standalone type, cannot be part of union type declaration
|
||||
if ($uniqueatedReturnType->isSuperTypeOf(new MixedType())->yes()) {
|
||||
return;
|
||||
}
|
||||
$phpParserUnionType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($uniqueatedReturnType, TypeKind::RETURN);
|
||||
if (!$phpParserUnionType instanceof Node) {
|
||||
return;
|
||||
}
|
||||
$functionLike->returnType = $phpParserUnionType;
|
||||
$this->hasChanged = \true;
|
||||
}
|
||||
/**
|
||||
* @return \PHPStan\Type\UnionType|\PHPStan\Type\Type
|
||||
*/
|
||||
private function filterOutDuplicatedArrayTypes(UnionType $unionType)
|
||||
{
|
||||
$hasArrayType = \false;
|
||||
$singleArrayTypes = [];
|
||||
$originalTypeCount = \count($unionType->getTypes());
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
if ($unionedType->isArray()->yes()) {
|
||||
if ($hasArrayType) {
|
||||
continue;
|
||||
}
|
||||
$singleArrayTypes[] = $unionedType;
|
||||
$hasArrayType = \true;
|
||||
continue;
|
||||
}
|
||||
$singleArrayTypes[] = $unionedType;
|
||||
}
|
||||
if ($originalTypeCount === \count($singleArrayTypes)) {
|
||||
return $unionType;
|
||||
}
|
||||
return $this->typeFactory->createMixedPassedOrUnionType($singleArrayTypes);
|
||||
}
|
||||
/**
|
||||
* @param \PhpParser\Node\Name|\PhpParser\Node\Identifier|\PhpParser\Node\ComplexType|null $type
|
||||
* @param \PhpParser\Node\Name|\PhpParser\Node\Identifier|\PhpParser\Node\ComplexType|null $phpParserUnionType
|
||||
*/
|
||||
private function shouldSkipParamTypeRefactor($type, $phpParserUnionType) : bool
|
||||
{
|
||||
if (!$phpParserUnionType instanceof PhpParserUnionType) {
|
||||
return $type instanceof Node;
|
||||
}
|
||||
if ($type instanceof PhpParserUnionType) {
|
||||
return \true;
|
||||
}
|
||||
if (\count($phpParserUnionType->types) > 1) {
|
||||
return \false;
|
||||
}
|
||||
$firstType = $phpParserUnionType->types[0];
|
||||
if (!$firstType instanceof FullyQualified) {
|
||||
return \false;
|
||||
}
|
||||
if (!$type instanceof FullyQualified) {
|
||||
return \false;
|
||||
}
|
||||
return $type->toString() === $firstType->toString();
|
||||
}
|
||||
}
|
||||
|
@ -19,12 +19,12 @@ final class VersionResolver
|
||||
* @api
|
||||
* @var string
|
||||
*/
|
||||
public const PACKAGE_VERSION = '37f0942c0976630ce9479c7499099a67d1f07bc4';
|
||||
public const PACKAGE_VERSION = 'ec61753f20dbabd4b4b350f9d7a994ac61315e82';
|
||||
/**
|
||||
* @api
|
||||
* @var string
|
||||
*/
|
||||
public const RELEASE_DATE = '2023-05-14 17:23:44';
|
||||
public const RELEASE_DATE = '2023-05-14 15:38:09';
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
|
2
vendor/autoload.php
vendored
2
vendor/autoload.php
vendored
@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit46def7a8c935d82e0f28baded9e426d7::getLoader();
|
||||
return ComposerAutoloaderInitef8919fd4ad7d9ffc328fd9d160bb2ee::getLoader();
|
||||
|
10
vendor/composer/autoload_real.php
vendored
10
vendor/composer/autoload_real.php
vendored
@ -2,7 +2,7 @@
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit46def7a8c935d82e0f28baded9e426d7
|
||||
class ComposerAutoloaderInitef8919fd4ad7d9ffc328fd9d160bb2ee
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
@ -22,17 +22,17 @@ class ComposerAutoloaderInit46def7a8c935d82e0f28baded9e426d7
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit46def7a8c935d82e0f28baded9e426d7', 'loadClassLoader'), true, true);
|
||||
spl_autoload_register(array('ComposerAutoloaderInitef8919fd4ad7d9ffc328fd9d160bb2ee', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit46def7a8c935d82e0f28baded9e426d7', 'loadClassLoader'));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitef8919fd4ad7d9ffc328fd9d160bb2ee', 'loadClassLoader'));
|
||||
|
||||
require __DIR__ . '/autoload_static.php';
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit46def7a8c935d82e0f28baded9e426d7::getInitializer($loader));
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitef8919fd4ad7d9ffc328fd9d160bb2ee::getInitializer($loader));
|
||||
|
||||
$loader->setClassMapAuthoritative(true);
|
||||
$loader->register(true);
|
||||
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInit46def7a8c935d82e0f28baded9e426d7::$files;
|
||||
$filesToLoad = \Composer\Autoload\ComposerStaticInitef8919fd4ad7d9ffc328fd9d160bb2ee::$files;
|
||||
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
|
8
vendor/composer/autoload_static.php
vendored
8
vendor/composer/autoload_static.php
vendored
@ -4,7 +4,7 @@
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit46def7a8c935d82e0f28baded9e426d7
|
||||
class ComposerStaticInitef8919fd4ad7d9ffc328fd9d160bb2ee
|
||||
{
|
||||
public static $files = array (
|
||||
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
|
||||
@ -3110,9 +3110,9 @@ class ComposerStaticInit46def7a8c935d82e0f28baded9e426d7
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit46def7a8c935d82e0f28baded9e426d7::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit46def7a8c935d82e0f28baded9e426d7::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInit46def7a8c935d82e0f28baded9e426d7::$classMap;
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitef8919fd4ad7d9ffc328fd9d160bb2ee::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitef8919fd4ad7d9ffc328fd9d160bb2ee::$prefixDirsPsr4;
|
||||
$loader->classMap = ComposerStaticInitef8919fd4ad7d9ffc328fd9d160bb2ee::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user