?????????? ????????? - ??????????????? - /home/agenciai/public_html/cd38d8/Php70.tar
???????
Exception/InvalidEregException.php 0000644 00000000212 15127645323 0013266 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Exception; use Exception; final class InvalidEregException extends Exception { } ValueObject/ComparedExprs.php 0000644 00000001201 15127645323 0012236 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\ValueObject; use PhpParser\Node\Expr; final class ComparedExprs { /** * @readonly * @var \PhpParser\Node\Expr */ private $firstExpr; /** * @readonly * @var \PhpParser\Node\Expr */ private $secondExpr; public function __construct(Expr $firstExpr, Expr $secondExpr) { $this->firstExpr = $firstExpr; $this->secondExpr = $secondExpr; } public function getFirstExpr() : Expr { return $this->firstExpr; } public function getSecondExpr() : Expr { return $this->secondExpr; } } NodeAnalyzer/Php4ConstructorClassMethodAnalyzer.php 0000644 00000001444 15127645323 0016601 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\NodeAnalyzer; use PhpParser\Node\Stmt\ClassMethod; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; final class Php4ConstructorClassMethodAnalyzer { public function detect(ClassMethod $classMethod, Scope $scope) : bool { // catch only classes without namespace if ($scope->getNamespace() !== null) { return \false; } if ($classMethod->isAbstract()) { return \false; } if ($classMethod->isStatic()) { return \false; } $classReflection = $scope->getClassReflection(); if (!$classReflection instanceof ClassReflection) { return \false; } return !$classReflection->isAnonymous(); } } NodeAnalyzer/BattleshipTernaryAnalyzer.php 0000644 00000010632 15127645323 0015034 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\NodeAnalyzer; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Greater; use PhpParser\Node\Expr\BinaryOp\Smaller; use PhpParser\Node\Expr\Ternary; use Rector\Php70\Enum\BattleshipCompareOrder; use Rector\Php70\ValueObject\ComparedExprs; use Rector\PhpParser\Comparing\NodeComparator; use Rector\PhpParser\Node\Value\ValueResolver; final class BattleshipTernaryAnalyzer { /** * @readonly * @var \Rector\PhpParser\Comparing\NodeComparator */ private $nodeComparator; /** * @readonly * @var \Rector\PhpParser\Node\Value\ValueResolver */ private $valueResolver; public function __construct(NodeComparator $nodeComparator, ValueResolver $valueResolver) { $this->nodeComparator = $nodeComparator; $this->valueResolver = $valueResolver; } /** * @return BattleshipCompareOrder::*|null */ public function isGreaterLowerCompareReturnOneAndMinusOne(Ternary $ternary, ComparedExprs $comparedExprs) : ?string { if ($ternary->cond instanceof Greater) { return $this->evaluateGreater($ternary->cond, $ternary, $comparedExprs); } if ($ternary->cond instanceof Smaller) { return $this->evaluateSmaller($ternary->cond, $ternary, $comparedExprs); } return null; } /** * We look for: * * $firstValue > $secondValue ? 1 : -1 * * @return BattleshipCompareOrder::*|null */ private function evaluateGreater(Greater $greater, Ternary $ternary, ComparedExprs $comparedExprs) : ?string { if (!$ternary->if instanceof Expr) { return null; } if ($this->nodeComparator->areNodesEqual($greater->left, $comparedExprs->getFirstExpr()) && $this->nodeComparator->areNodesEqual($greater->right, $comparedExprs->getSecondExpr())) { return $this->evaluateTernaryDesc($ternary); } if (!$this->nodeComparator->areNodesEqual($greater->right, $comparedExprs->getFirstExpr())) { return null; } if (!$this->nodeComparator->areNodesEqual($greater->left, $comparedExprs->getSecondExpr())) { return null; } return $this->evaluateTernaryAsc($ternary); } /** * We look for: * * $firstValue < $secondValue ? -1 : 1 * * @return BattleshipCompareOrder::*|null */ private function evaluateSmaller(Smaller $smaller, Ternary $ternary, ComparedExprs $comparedExprs) : ?string { if (!$ternary->if instanceof Expr) { return null; } if ($this->nodeComparator->areNodesEqual($smaller->left, $comparedExprs->getFirstExpr()) && $this->nodeComparator->areNodesEqual($smaller->right, $comparedExprs->getSecondExpr())) { return $this->evaluateTernaryAsc($ternary); } if (!$this->nodeComparator->areNodesEqual($smaller->right, $comparedExprs->getFirstExpr())) { return null; } if (!$this->nodeComparator->areNodesEqual($smaller->left, $comparedExprs->getSecondExpr())) { return null; } return $this->evaluateTernaryDesc($ternary); } private function isValueOneAndMinusOne(Expr $firstExpr, Expr $seconcExpr) : bool { if (!$this->valueResolver->isValue($firstExpr, 1)) { return \false; } return $this->valueResolver->isValue($seconcExpr, -1); } /** * @return BattleshipCompareOrder::*|null */ private function evaluateTernaryAsc(Ternary $ternary) : ?string { if (!$ternary->if instanceof Expr) { return null; } if ($this->isValueOneAndMinusOne($ternary->if, $ternary->else)) { return BattleshipCompareOrder::ASC; } if ($this->isValueOneAndMinusOne($ternary->else, $ternary->if)) { return BattleshipCompareOrder::DESC; } return null; } /** * @return BattleshipCompareOrder::*|null */ private function evaluateTernaryDesc(Ternary $ternary) : ?string { if (!$ternary->if instanceof Expr) { return null; } if ($this->isValueOneAndMinusOne($ternary->if, $ternary->else)) { return BattleshipCompareOrder::DESC; } if ($this->isValueOneAndMinusOne($ternary->else, $ternary->if)) { return BattleshipCompareOrder::ASC; } return null; } } Rector/Switch_/ReduceMultipleDefaultSwitchRector.php 0000644 00000004125 15127645323 0016716 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\Switch_; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Stmt\Switch_; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\Switch_\ReduceMultipleDefaultSwitchRector\ReduceMultipleDefaultSwitchRectorTest */ final class ReduceMultipleDefaultSwitchRector extends AbstractRector implements MinPhpVersionInterface { public function provideMinPhpVersion() : int { return PhpVersionFeature::NO_MULTIPLE_DEFAULT_SWITCH; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Remove first default switch, that is ignored', [new CodeSample(<<<'CODE_SAMPLE' switch ($expr) { default: echo "Hello World"; default: echo "Goodbye Moon!"; break; } CODE_SAMPLE , <<<'CODE_SAMPLE' switch ($expr) { default: echo "Goodbye Moon!"; break; } CODE_SAMPLE )]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [Switch_::class]; } /** * @param Switch_ $node */ public function refactor(Node $node) : ?Node { $defaultCases = []; foreach ($node->cases as $key => $case) { if ($case->cond instanceof Expr) { continue; } $defaultCases[$key] = $case; } $defaultCaseCount = \count($defaultCases); if ($defaultCaseCount < 2) { return null; } foreach ($node->cases as $key => $case) { if ($case->cond instanceof Expr) { continue; } // remove previous default cases if ($defaultCaseCount > 1) { unset($node->cases[$key]); --$defaultCaseCount; } } return $node; } } Rector/StaticCall/StaticCallOnNonStaticToInstanceCallRector.php 0000644 00000014523 15127645323 0020661 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\StaticCall; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\New_; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\ObjectType; use Rector\CodingStyle\ValueObject\ObjectMagicMethods; use Rector\Enum\ObjectReference; use Rector\NodeCollector\ScopeResolver\ParentClassScopeResolver; use Rector\NodeCollector\StaticAnalyzer; use Rector\Rector\AbstractScopeAwareRector; use Rector\Reflection\ReflectionResolver; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use ReflectionMethod; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector\StaticCallOnNonStaticToInstanceCallRectorTest */ final class StaticCallOnNonStaticToInstanceCallRector extends AbstractScopeAwareRector implements MinPhpVersionInterface { /** * @readonly * @var \Rector\NodeCollector\StaticAnalyzer */ private $staticAnalyzer; /** * @readonly * @var \PHPStan\Reflection\ReflectionProvider */ private $reflectionProvider; /** * @readonly * @var \Rector\Reflection\ReflectionResolver */ private $reflectionResolver; /** * @readonly * @var \Rector\NodeCollector\ScopeResolver\ParentClassScopeResolver */ private $parentClassScopeResolver; public function __construct(StaticAnalyzer $staticAnalyzer, ReflectionProvider $reflectionProvider, ReflectionResolver $reflectionResolver, ParentClassScopeResolver $parentClassScopeResolver) { $this->staticAnalyzer = $staticAnalyzer; $this->reflectionProvider = $reflectionProvider; $this->reflectionResolver = $reflectionResolver; $this->parentClassScopeResolver = $parentClassScopeResolver; } public function provideMinPhpVersion() : int { return PhpVersionFeature::INSTANCE_CALL; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Changes static call to instance call, where not useful', [new CodeSample(<<<'CODE_SAMPLE' class Something { public function doWork() { } } class Another { public function run() { return Something::doWork(); } } CODE_SAMPLE , <<<'CODE_SAMPLE' class Something { public function doWork() { } } class Another { public function run() { return (new Something)->doWork(); } } CODE_SAMPLE )]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [StaticCall::class]; } /** * @param StaticCall $node */ public function refactorWithScope(Node $node, Scope $scope) : ?Node { if ($node->name instanceof Expr) { return null; } $methodName = $this->getName($node->name); $className = $this->resolveStaticCallClassName($node); if ($methodName === null) { return null; } if ($className === null) { return null; } if ($this->shouldSkip($methodName, $className, $node, $scope)) { return null; } if ($this->isInstantiable($className, $scope)) { $new = new New_($node->class); return new MethodCall($new, $node->name, $node->args); } return null; } private function resolveStaticCallClassName(StaticCall $staticCall) : ?string { if ($staticCall->class instanceof PropertyFetch) { $objectType = $this->getType($staticCall->class); if ($objectType instanceof ObjectType) { return $objectType->getClassName(); } } return $this->getName($staticCall->class); } private function shouldSkip(string $methodName, string $className, StaticCall $staticCall, Scope $scope) : bool { if (\in_array($methodName, ObjectMagicMethods::METHOD_NAMES, \true)) { return \true; } if (!$this->reflectionProvider->hasClass($className)) { return \true; } $classReflection = $this->reflectionProvider->getClass($className); if ($classReflection->isAbstract()) { return \true; } // does the method even exist? if (!$classReflection->hasMethod($methodName)) { return \true; } $isStaticMethod = $this->staticAnalyzer->isStaticMethod($classReflection, $methodName); if ($isStaticMethod) { return \true; } $reflection = $scope->getClassReflection(); if ($reflection instanceof ClassReflection && $reflection->isSubclassOf($className)) { return \true; } $className = $this->getName($staticCall->class); if (\in_array($className, [ObjectReference::PARENT, ObjectReference::SELF, ObjectReference::STATIC], \true)) { return \true; } if ($className === 'class') { return \true; } $parentClassName = $this->parentClassScopeResolver->resolveParentClassName($scope); return $className === $parentClassName; } private function isInstantiable(string $className, Scope $scope) : bool { if (!$this->reflectionProvider->hasClass($className)) { return \false; } $methodReflection = $this->reflectionResolver->resolveMethodReflection($className, '__callStatic', $scope); if ($methodReflection instanceof MethodReflection) { return \false; } $classReflection = $this->reflectionProvider->getClass($className); $nativeReflection = $classReflection->getNativeReflection(); $reflectionMethod = $nativeReflection->getConstructor(); if (!$reflectionMethod instanceof ReflectionMethod) { return \true; } if (!$reflectionMethod->isPublic()) { return \false; } // required parameters in constructor, nothing we can do return !(bool) $reflectionMethod->getNumberOfRequiredParameters(); } } Rector/Variable/WrapVariableVariableNameInCurlyBracesRector.php 0000644 00000003500 15127645323 0020701 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\Variable; use PhpParser\Node; use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\Variable; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\Variable\WrapVariableVariableNameInCurlyBracesRector\WrapVariableVariableNameInCurlyBracesRectorTest */ final class WrapVariableVariableNameInCurlyBracesRector extends AbstractRector implements MinPhpVersionInterface { public function provideMinPhpVersion() : int { return PhpVersionFeature::WRAP_VARIABLE_VARIABLE; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Ensure variable variables are wrapped in curly braces', [new CodeSample(<<<'CODE_SAMPLE' function run($foo) { global $$foo->bar; } CODE_SAMPLE , <<<'CODE_SAMPLE' function run($foo) { global ${$foo->bar}; } CODE_SAMPLE )]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [Variable::class]; } /** * @param Variable $node */ public function refactor(Node $node) : ?Node { $nodeName = $node->name; if (!$nodeName instanceof PropertyFetch && !$nodeName instanceof Variable) { return null; } if ($node->getEndTokenPos() !== $nodeName->getEndTokenPos()) { return null; } if ($nodeName instanceof PropertyFetch) { return new Variable(new PropertyFetch($nodeName->var, $nodeName->name)); } return new Variable(new Variable($nodeName->name)); } } Rector/MethodCall/ThisCallOnStaticMethodToStaticCallRector.php 0000644 00000011756 15127645323 0020510 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\MethodCall; use PhpParser\Node; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; use PhpParser\Node\Scalar\Encapsed; use PhpParser\Node\Stmt\Class_; use PhpParser\NodeTraverser; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\Php\PhpMethodReflection; use Rector\Enum\ObjectReference; use Rector\NodeCollector\StaticAnalyzer; use Rector\Rector\AbstractScopeAwareRector; use Rector\Reflection\ReflectionResolver; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\MethodCall\ThisCallOnStaticMethodToStaticCallRector\ThisCallOnStaticMethodToStaticCallRectorTest */ final class ThisCallOnStaticMethodToStaticCallRector extends AbstractScopeAwareRector implements MinPhpVersionInterface { /** * @readonly * @var \Rector\NodeCollector\StaticAnalyzer */ private $staticAnalyzer; /** * @readonly * @var \Rector\Reflection\ReflectionResolver */ private $reflectionResolver; /** * @var bool */ private $hasChanged = \false; public function __construct(StaticAnalyzer $staticAnalyzer, ReflectionResolver $reflectionResolver) { $this->staticAnalyzer = $staticAnalyzer; $this->reflectionResolver = $reflectionResolver; } public function provideMinPhpVersion() : int { return PhpVersionFeature::STATIC_CALL_ON_NON_STATIC; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Changes $this->call() to static method to static call', [new CodeSample(<<<'CODE_SAMPLE' class SomeClass { public static function run() { $this->eat(); } public static function eat() { } } CODE_SAMPLE , <<<'CODE_SAMPLE' class SomeClass { public static function run() { static::eat(); } public static function eat() { } } CODE_SAMPLE )]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [Class_::class]; } /** * @param Class_ $node */ public function refactorWithScope(Node $node, Scope $scope) : ?Node { if (!$scope->isInClass()) { return null; } $classReflection = $scope->getClassReflection(); // skip PHPUnit calls, as they accept both self:: and $this-> formats if ($classReflection->isSubclassOf('PHPUnit\\Framework\\TestCase')) { return null; } $this->hasChanged = \false; $this->processThisToStatic($node, $classReflection); if ($this->hasChanged) { return $node; } return null; } private function processThisToStatic(Class_ $class, ClassReflection $classReflection) : void { $this->traverseNodesWithCallable($class, function (Node $subNode) use($class, $classReflection) { if ($subNode instanceof Encapsed) { return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; } if (!$subNode instanceof MethodCall) { return null; } if (!$subNode->var instanceof Variable) { return null; } if (!$this->nodeNameResolver->isName($subNode->var, 'this')) { return null; } if (!$subNode->name instanceof Identifier) { return null; } $methodName = $this->getName($subNode->name); if ($methodName === null) { return null; } $isStaticMethod = $this->staticAnalyzer->isStaticMethod($classReflection, $methodName, $class); if (!$isStaticMethod) { return null; } if ($subNode->isFirstClassCallable()) { return null; } $this->hasChanged = \true; $objectReference = $this->resolveClassSelf($classReflection, $subNode); return $this->nodeFactory->createStaticCall($objectReference, $methodName, $subNode->args); }); } /** * @return ObjectReference::STATIC|ObjectReference::SELF */ private function resolveClassSelf(ClassReflection $classReflection, MethodCall $methodCall) : string { if ($classReflection->isFinalByKeyword()) { return ObjectReference::SELF; } $methodReflection = $this->reflectionResolver->resolveMethodReflectionFromMethodCall($methodCall); if (!$methodReflection instanceof PhpMethodReflection) { return ObjectReference::STATIC; } if (!$methodReflection->isPrivate()) { return ObjectReference::STATIC; } return ObjectReference::SELF; } } Rector/Assign/ListSwapArrayOrderRector.php 0000644 00000005616 15127645323 0014677 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\Assign; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\List_; use Rector\PhpParser\Printer\BetterStandardPrinter; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\Assign\ListSwapArrayOrderRector\ListSwapArrayOrderRectorTest */ final class ListSwapArrayOrderRector extends AbstractRector implements MinPhpVersionInterface { /** * @readonly * @var \Rector\PhpParser\Printer\BetterStandardPrinter */ private $betterStandardPrinter; public function __construct(BetterStandardPrinter $betterStandardPrinter) { $this->betterStandardPrinter = $betterStandardPrinter; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('list() assigns variables in reverse order - relevant in array assign', [new CodeSample('list($a[], $a[]) = [1, 2];', 'list($a[], $a[]) = array_reverse([1, 2]);')]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [Assign::class]; } /** * @param Assign $node */ public function refactor(Node $node) : ?Node { if ($this->shouldSkipAssign($node)) { return null; } /** @var List_ $list */ $list = $node->var; $printedVariables = []; foreach ($list->items as $arrayItem) { if (!$arrayItem instanceof ArrayItem) { continue; } if ($arrayItem->value instanceof ArrayDimFetch && !$arrayItem->value->dim instanceof Expr) { $printedVariables[] = $this->betterStandardPrinter->print($arrayItem->value->var); } else { return null; } } // relevant only in 1 variable type $uniqueVariables = \array_unique($printedVariables); if (\count($uniqueVariables) !== 1) { return null; } // wrap with array_reverse, to reflect reverse assign order in left $node->expr = $this->nodeFactory->createFuncCall('array_reverse', [$node->expr]); return $node; } public function provideMinPhpVersion() : int { return PhpVersionFeature::LIST_SWAP_ORDER; } private function shouldSkipAssign(Assign $assign) : bool { if (!$assign->var instanceof List_) { return \true; } // already converted return $assign->expr instanceof FuncCall && $this->isName($assign->expr, 'array_reverse'); } } Rector/Assign/ListSplitStringRector.php 0000644 00000003000 15127645323 0014235 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\Assign; use PhpParser\Node; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\List_; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\Assign\ListSplitStringRector\ListSplitStringRectorTest */ final class ListSplitStringRector extends AbstractRector implements MinPhpVersionInterface { public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('list() cannot split string directly anymore, use str_split()', [new CodeSample('list($foo) = "string";', 'list($foo) = str_split("string");')]); } public function provideMinPhpVersion() : int { return PhpVersionFeature::NO_LIST_SPLIT_STRING; } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [Assign::class]; } /** * @param Assign $node */ public function refactor(Node $node) : ?Node { if (!$node->var instanceof List_) { return null; } $exprType = $this->getType($node->expr); if (!$exprType->isString()->yes()) { return null; } $node->expr = $this->nodeFactory->createFuncCall('str_split', [$node->expr]); return $node; } } Rector/ClassMethod/Php4ConstructorRector.php 0000644 00000014001 15127645323 0015165 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\ClassMethod; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; use PHPStan\Analyser\Scope; use PHPStan\Reflection\ClassReflection; use Rector\Enum\ObjectReference; use Rector\NodeCollector\ScopeResolver\ParentClassScopeResolver; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\Php70\NodeAnalyzer\Php4ConstructorClassMethodAnalyzer; use Rector\Rector\AbstractScopeAwareRector; use Rector\ValueObject\MethodName; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\ClassMethod\Php4ConstructorRector\Php4ConstructorRectorTest */ final class Php4ConstructorRector extends AbstractScopeAwareRector implements MinPhpVersionInterface { /** * @readonly * @var \Rector\Php70\NodeAnalyzer\Php4ConstructorClassMethodAnalyzer */ private $php4ConstructorClassMethodAnalyzer; /** * @readonly * @var \Rector\NodeCollector\ScopeResolver\ParentClassScopeResolver */ private $parentClassScopeResolver; public function __construct(Php4ConstructorClassMethodAnalyzer $php4ConstructorClassMethodAnalyzer, ParentClassScopeResolver $parentClassScopeResolver) { $this->php4ConstructorClassMethodAnalyzer = $php4ConstructorClassMethodAnalyzer; $this->parentClassScopeResolver = $parentClassScopeResolver; } public function provideMinPhpVersion() : int { return PhpVersionFeature::NO_PHP4_CONSTRUCTOR; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Changes PHP 4 style constructor to __construct.', [new CodeSample(<<<'CODE_SAMPLE' class SomeClass { public function SomeClass() { } } CODE_SAMPLE , <<<'CODE_SAMPLE' class SomeClass { public function __construct() { } } CODE_SAMPLE )]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [Class_::class]; } /** * @param Class_ $node * @return \PhpParser\Node\Stmt\Class_|int|null */ public function refactorWithScope(Node $node, Scope $scope) { $className = $this->getName($node); if (!\is_string($className)) { return null; } $psr4ConstructorMethod = $node->getMethod(\lcfirst($className)) ?? $node->getMethod($className); if (!$psr4ConstructorMethod instanceof ClassMethod) { return null; } if (!$this->php4ConstructorClassMethodAnalyzer->detect($psr4ConstructorMethod, $scope)) { return null; } $classReflection = $scope->getClassReflection(); if (!$classReflection instanceof ClassReflection) { return null; } // process parent call references first $this->processClassMethodStatementsForParentConstructorCalls($psr4ConstructorMethod, $scope); // does it already have a __construct method? if (!$classReflection->hasNativeMethod(MethodName::CONSTRUCT)) { $psr4ConstructorMethod->name = new Identifier(MethodName::CONSTRUCT); } $classMethodStmts = $psr4ConstructorMethod->stmts; if ($classMethodStmts === null) { return null; } if (\count($classMethodStmts) === 1) { $stmt = $psr4ConstructorMethod->stmts[0]; if (!$stmt instanceof Expression) { return null; } if ($this->isLocalMethodCallNamed($stmt->expr, MethodName::CONSTRUCT)) { $stmtKey = $psr4ConstructorMethod->getAttribute(AttributeKey::STMT_KEY); unset($node->stmts[$stmtKey]); } } return $node; } private function processClassMethodStatementsForParentConstructorCalls(ClassMethod $classMethod, Scope $scope) : void { if (!\is_iterable($classMethod->stmts)) { return; } foreach ($classMethod->stmts as $methodStmt) { if (!$methodStmt instanceof Expression) { continue; } $methodStmt = $methodStmt->expr; if (!$methodStmt instanceof StaticCall) { continue; } $this->processParentPhp4ConstructCall($methodStmt, $scope); } } private function processParentPhp4ConstructCall(StaticCall $staticCall, Scope $scope) : void { $parentClassReflection = $this->parentClassScopeResolver->resolveParentClassReflection($scope); // no parent class if (!$parentClassReflection instanceof ClassReflection) { return; } if (!$staticCall->class instanceof Name) { return; } // rename ParentClass if ($this->isName($staticCall->class, $parentClassReflection->getName())) { $staticCall->class = new Name(ObjectReference::PARENT); } if (!$this->isName($staticCall->class, ObjectReference::PARENT)) { return; } // it's not a parent PHP 4 constructor call if (!$this->isName($staticCall->name, $parentClassReflection->getName())) { return; } $staticCall->name = new Identifier(MethodName::CONSTRUCT); } private function isLocalMethodCallNamed(Expr $expr, string $name) : bool { if (!$expr instanceof MethodCall) { return \false; } if ($expr->var instanceof StaticCall) { return \false; } if ($expr->var instanceof MethodCall) { return \false; } if (!$this->isName($expr->var, 'this')) { return \false; } return $this->isName($expr->name, $name); } } Rector/StmtsAwareInterface/IfIssetToCoalescingRector.php 0000644 00000006346 15127645323 0017467 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\StmtsAwareInterface; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Coalesce; use PhpParser\Node\Expr\Isset_; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; use Rector\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\StmtsAwareInterface\IfIssetToCoalescingRector\IfIssetToCoalescingRectorTest */ final class IfIssetToCoalescingRector extends AbstractRector implements MinPhpVersionInterface { public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Change if with isset and return to coalesce', [new CodeSample(<<<'CODE_SAMPLE' class SomeClass { private $items = []; public function resolve($key) { if (isset($this->items[$key])) { return $this->items[$key]; } return 'fallback value'; } } CODE_SAMPLE , <<<'CODE_SAMPLE' class SomeClass { private $items = []; public function resolve($key) { return $this->items[$key] ?? 'fallback value'; } } CODE_SAMPLE )]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [StmtsAwareInterface::class]; } /** * @param StmtsAwareInterface $node */ public function refactor(Node $node) : ?Node { if ($node->stmts === null) { return null; } foreach ($node->stmts as $key => $stmt) { if (!$stmt instanceof Return_) { continue; } if (!$stmt->expr instanceof Expr) { continue; } $previousStmt = $node->stmts[$key - 1] ?? null; if (!$previousStmt instanceof If_) { continue; } if (!$previousStmt->cond instanceof Isset_) { continue; } $ifOnlyStmt = $this->matchBareIfOnlyStmt($previousStmt); if (!$ifOnlyStmt instanceof Return_) { continue; } if (!$ifOnlyStmt->expr instanceof Expr) { continue; } $ifIsset = $previousStmt->cond; if (!$this->nodeComparator->areNodesEqual($ifOnlyStmt->expr, $ifIsset->vars[0])) { continue; } unset($node->stmts[$key - 1]); $stmt->expr = new Coalesce($ifOnlyStmt->expr, $stmt->expr); return $node; } return null; } public function provideMinPhpVersion() : int { return PhpVersionFeature::NULL_COALESCE; } private function matchBareIfOnlyStmt(If_ $if) : ?Stmt { if ($if->else instanceof Else_) { return null; } if ($if->elseifs !== []) { return null; } if (\count($if->stmts) !== 1) { return null; } return $if->stmts[0]; } } Rector/Ternary/TernaryToSpaceshipRector.php 0000644 00000007672 15127645323 0015131 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\Ternary; use PhpParser\Node; use PhpParser\Node\Expr\BinaryOp; use PhpParser\Node\Expr\BinaryOp\Greater; use PhpParser\Node\Expr\BinaryOp\Smaller; use PhpParser\Node\Expr\BinaryOp\Spaceship; use PhpParser\Node\Expr\Ternary; use Rector\PhpParser\Node\Value\ValueResolver; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\Ternary\TernaryToSpaceshipRector\TernaryToSpaceshipRectorTest */ final class TernaryToSpaceshipRector extends AbstractRector implements MinPhpVersionInterface { /** * @readonly * @var \Rector\PhpParser\Node\Value\ValueResolver */ private $valueResolver; public function __construct(ValueResolver $valueResolver) { $this->valueResolver = $valueResolver; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Use <=> spaceship instead of ternary with same effect', [new CodeSample(<<<'CODE_SAMPLE' function order_func($a, $b) { return ($a < $b) ? -1 : (($a > $b) ? 1 : 0); } CODE_SAMPLE , <<<'CODE_SAMPLE' function order_func($a, $b) { return $a <=> $b; } CODE_SAMPLE )]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [Ternary::class]; } /** * @param Ternary $node */ public function refactor(Node $node) : ?Node { if ($this->shouldSkip($node)) { return null; } /** @var Ternary $nestedTernary */ $nestedTernary = $node->else; $spaceshipNode = $this->processSmallerThanTernary($node, $nestedTernary); if ($spaceshipNode instanceof Spaceship) { return $spaceshipNode; } return $this->processGreaterThanTernary($node, $nestedTernary); } public function provideMinPhpVersion() : int { return PhpVersionFeature::SPACESHIP; } private function shouldSkip(Ternary $ternary) : bool { if (!$ternary->cond instanceof BinaryOp) { return \true; } if (!$ternary->else instanceof Ternary) { return \true; } $nestedTernary = $ternary->else; if (!$nestedTernary->cond instanceof BinaryOp) { return \true; } // $a X $b ? . : ($a X $b ? . : .) if (!$this->nodeComparator->areNodesEqual($ternary->cond->left, $nestedTernary->cond->left)) { return \true; } // $a X $b ? . : ($a X $b ? . : .) return !$this->nodeComparator->areNodesEqual($ternary->cond->right, $nestedTernary->cond->right); } /** * Matches "$a < $b ? -1 : ($a > $b ? 1 : 0)" */ private function processSmallerThanTernary(Ternary $node, Ternary $nestedTernary) : ?Spaceship { if (!$node->cond instanceof Smaller) { return null; } if (!$nestedTernary->cond instanceof Greater) { return null; } if (!$this->valueResolver->areValuesEqual([$node->if, $nestedTernary->if, $nestedTernary->else], [-1, 1, 0])) { return null; } return new Spaceship($node->cond->left, $node->cond->right); } /** * Matches "$a > $b ? -1 : ($a < $b ? 1 : 0)" */ private function processGreaterThanTernary(Ternary $node, Ternary $nestedTernary) : ?Spaceship { if (!$node->cond instanceof Greater) { return null; } if (!$nestedTernary->cond instanceof Smaller) { return null; } if (!$this->valueResolver->areValuesEqual([$node->if, $nestedTernary->if, $nestedTernary->else], [-1, 1, 0])) { return null; } return new Spaceship($node->cond->right, $node->cond->left); } } Rector/Ternary/TernaryToNullCoalescingRector.php 0000644 00000007150 15127645323 0016103 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\Ternary; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Coalesce; use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\Isset_; use PhpParser\Node\Expr\Ternary; use Rector\PhpParser\Node\Value\ValueResolver; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\Ternary\TernaryToNullCoalescingRector\TernaryToNullCoalescingRectorTest */ final class TernaryToNullCoalescingRector extends AbstractRector implements MinPhpVersionInterface { /** * @readonly * @var \Rector\PhpParser\Node\Value\ValueResolver */ private $valueResolver; public function __construct(ValueResolver $valueResolver) { $this->valueResolver = $valueResolver; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Changes unneeded null check to ?? operator', [new CodeSample('$value === null ? 10 : $value;', '$value ?? 10;'), new CodeSample('isset($value) ? $value : 10;', '$value ?? 10;')]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [Ternary::class]; } /** * @param Ternary $node */ public function refactor(Node $node) : ?Node { if ($node->cond instanceof Isset_) { return $this->processTernaryWithIsset($node, $node->cond); } if ($node->cond instanceof Identical) { $checkedNode = $node->else; $fallbackNode = $node->if; } elseif ($node->cond instanceof NotIdentical) { $checkedNode = $node->if; $fallbackNode = $node->else; } else { // not a match return null; } if (!$checkedNode instanceof Expr) { return null; } if (!$fallbackNode instanceof Expr) { return null; } /** @var Identical|NotIdentical $ternaryCompareNode */ $ternaryCompareNode = $node->cond; if ($this->isNullMatch($ternaryCompareNode->left, $ternaryCompareNode->right, $checkedNode)) { return new Coalesce($checkedNode, $fallbackNode); } if ($this->isNullMatch($ternaryCompareNode->right, $ternaryCompareNode->left, $checkedNode)) { return new Coalesce($checkedNode, $fallbackNode); } return null; } public function provideMinPhpVersion() : int { return PhpVersionFeature::NULL_COALESCE; } private function processTernaryWithIsset(Ternary $ternary, Isset_ $isset) : ?Coalesce { if (!$ternary->if instanceof Expr) { return null; } if ($isset->vars === null) { return null; } // none or multiple isset values cannot be handled here if (\count($isset->vars) > 1) { return null; } if (!$this->nodeComparator->areNodesEqual($ternary->if, $isset->vars[0])) { return null; } return new Coalesce($ternary->if, $ternary->else); } private function isNullMatch(Expr $possibleNullExpr, Expr $firstNode, Expr $secondNode) : bool { if (!$this->valueResolver->isNull($possibleNullExpr)) { return \false; } return $this->nodeComparator->areNodesEqual($firstNode, $secondNode); } } Rector/Break_/BreakNotInLoopOrSwitchToReturnRector.php 0000644 00000006104 15127645323 0017102 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\Break_; use PhpParser\Node; use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\FunctionLike; use PhpParser\Node\Stmt\Break_; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Return_; use PhpParser\Node\Stmt\Switch_; use PhpParser\NodeTraverser; use Rector\NodeNestingScope\ContextAnalyzer; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector\BreakNotInLoopOrSwitchToReturnRectorTest */ final class BreakNotInLoopOrSwitchToReturnRector extends AbstractRector implements MinPhpVersionInterface { /** * @readonly * @var \Rector\NodeNestingScope\ContextAnalyzer */ private $contextAnalyzer; /** * @var string */ private const IS_BREAK_IN_SWITCH = 'is_break_in_switch'; public function __construct(ContextAnalyzer $contextAnalyzer) { $this->contextAnalyzer = $contextAnalyzer; } public function provideMinPhpVersion() : int { return PhpVersionFeature::NO_BREAK_OUTSIDE_LOOP; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Convert break outside for/foreach/switch context to return', [new CodeSample(<<<'CODE_SAMPLE' class SomeClass { public function run() { if ($isphp5) return 1; else return 2; break; } } CODE_SAMPLE , <<<'CODE_SAMPLE' class SomeClass { public function run() { if ($isphp5) return 1; else return 2; return; } } CODE_SAMPLE )]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [Switch_::class, Break_::class]; } /** * @param Switch_|Break_ $node * @return \PhpParser\Node\Stmt\Return_|null|int */ public function refactor(Node $node) { if ($node instanceof Switch_) { $this->traverseNodesWithCallable($node->cases, static function (Node $subNode) : ?int { if ($subNode instanceof Class_ || $subNode instanceof FunctionLike && !$subNode instanceof ArrowFunction) { return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; } if (!$subNode instanceof Break_) { return null; } $subNode->setAttribute(self::IS_BREAK_IN_SWITCH, \true); return null; }); return null; } if ($this->contextAnalyzer->isInLoop($node)) { return null; } if ($node->getAttribute(self::IS_BREAK_IN_SWITCH) === \true) { return null; } if ($this->contextAnalyzer->isInIf($node)) { return new Return_(); } return NodeTraverser::REMOVE_NODE; } } Rector/If_/IfToSpaceshipRector.php 0000644 00000014166 15127645323 0013110 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\If_; use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\Spaceship; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; use Rector\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\Php70\Enum\BattleshipCompareOrder; use Rector\Php70\NodeAnalyzer\BattleshipTernaryAnalyzer; use Rector\Php70\ValueObject\ComparedExprs; use Rector\PhpParser\Node\Value\ValueResolver; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\If_\IfToSpaceshipRector\IfToSpaceshipRectorTest */ final class IfToSpaceshipRector extends AbstractRector implements MinPhpVersionInterface { /** * @readonly * @var \Rector\Php70\NodeAnalyzer\BattleshipTernaryAnalyzer */ private $battleshipTernaryAnalyzer; /** * @readonly * @var \Rector\PhpParser\Node\Value\ValueResolver */ private $valueResolver; public function __construct(BattleshipTernaryAnalyzer $battleshipTernaryAnalyzer, ValueResolver $valueResolver) { $this->battleshipTernaryAnalyzer = $battleshipTernaryAnalyzer; $this->valueResolver = $valueResolver; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Changes if/else to spaceship <=> where useful', [new CodeSample(<<<'CODE_SAMPLE' usort($languages, function ($first, $second) { if ($first[0] === $second[0]) { return 0; } return ($first[0] < $second[0]) ? 1 : -1; }); CODE_SAMPLE , <<<'CODE_SAMPLE' usort($languages, function ($first, $second) { return $second[0] <=> $first[0]; }); CODE_SAMPLE )]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [StmtsAwareInterface::class, If_::class]; } /** * @param StmtsAwareInterface|If_ $node */ public function refactor(Node $node) : ?Node { if ($node instanceof If_) { return $this->refactorIf($node); } if ($node->stmts === null) { return null; } foreach ($node->stmts as $key => $stmt) { if (!$stmt instanceof Return_) { continue; } if (!$stmt->expr instanceof Ternary) { continue; } // preceeded by if $prevStmt = $node->stmts[$key - 1] ?? null; if (!$prevStmt instanceof If_) { continue; } $comparedExprs = $this->matchExprComparedExprsReturnZero($prevStmt); if (!$comparedExprs instanceof ComparedExprs) { continue; } $battleshipCompareOrder = $this->battleshipTernaryAnalyzer->isGreaterLowerCompareReturnOneAndMinusOne($stmt->expr, $comparedExprs); $returnSpaceship = $this->createReturnSpaceship($battleshipCompareOrder, $comparedExprs); if (!$returnSpaceship instanceof Return_) { continue; } unset($node->stmts[$key - 1]); $node->stmts[$key] = $returnSpaceship; return $node; } return null; } public function provideMinPhpVersion() : int { return PhpVersionFeature::SPACESHIP; } private function refactorIf(If_ $if) : ?Return_ { if ($if->elseifs !== []) { return null; } if (!$if->else instanceof Else_) { return null; } $comparedExprs = $this->matchExprComparedExprsReturnZero($if); if (!$comparedExprs instanceof ComparedExprs) { return null; } $ternary = $this->matchElseOnlyStmtTernary($if->else); if (!$ternary instanceof Ternary) { return null; } $battleshipCompareOrder = $this->battleshipTernaryAnalyzer->isGreaterLowerCompareReturnOneAndMinusOne($ternary, $comparedExprs); return $this->createReturnSpaceship($battleshipCompareOrder, $comparedExprs); } /** * We look for: * * if ($firstValue === $secondValue) { * return 0; * } */ private function matchExprComparedExprsReturnZero(If_ $if) : ?ComparedExprs { if (!$if->cond instanceof Equal && !$if->cond instanceof Identical) { return null; } $binaryOp = $if->cond; if (\count($if->stmts) !== 1) { return null; } $onlyStmt = $if->stmts[0]; if (!$onlyStmt instanceof Return_) { return null; } if (!$onlyStmt->expr instanceof Expr) { return null; } if (!$this->valueResolver->isValue($onlyStmt->expr, 0)) { return null; } return new ComparedExprs($binaryOp->left, $binaryOp->right); } /** * @param BattleshipCompareOrder::*|null $battleshipCompareOrder */ private function createReturnSpaceship(?string $battleshipCompareOrder, ComparedExprs $comparedExprs) : ?Return_ { if ($battleshipCompareOrder === null) { return null; } if ($battleshipCompareOrder === BattleshipCompareOrder::DESC) { $spaceship = new Spaceship($comparedExprs->getFirstExpr(), $comparedExprs->getSecondExpr()); } else { $spaceship = new Spaceship($comparedExprs->getSecondExpr(), $comparedExprs->getFirstExpr()); } return new Return_($spaceship); } private function matchElseOnlyStmtTernary(Else_ $else) : ?\PhpParser\Node\Expr\Ternary { if (\count($else->stmts) !== 1) { return null; } $onlyElseStmt = $else->stmts[0]; if (!$onlyElseStmt instanceof Return_) { return null; } if (!$onlyElseStmt->expr instanceof Ternary) { return null; } return $onlyElseStmt->expr; } } Rector/FuncCall/EregToPregMatchRector.php 0000644 00000014751 15127645323 0014361 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\FuncCall; use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\BinaryOp\Concat; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Name; use PhpParser\Node\Scalar\LNumber; use PhpParser\Node\Scalar\String_; use Rector\Php70\EregToPcreTransformer; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use RectorPrefix202411\Webmozart\Assert\Assert; /** * @see \Rector\Tests\Php70\Rector\FuncCall\EregToPregMatchRector\EregToPregMatchRectorTest */ final class EregToPregMatchRector extends AbstractRector implements MinPhpVersionInterface { /** * @readonly * @var \Rector\Php70\EregToPcreTransformer */ private $eregToPcreTransformer; /** * @var array<string, string> */ private const OLD_NAMES_TO_NEW_ONES = ['ereg' => 'preg_match', 'eregi' => 'preg_match', 'ereg_replace' => 'preg_replace', 'eregi_replace' => 'preg_replace', 'split' => 'preg_split', 'spliti' => 'preg_split']; public function __construct(EregToPcreTransformer $eregToPcreTransformer) { $this->eregToPcreTransformer = $eregToPcreTransformer; } public function provideMinPhpVersion() : int { return PhpVersionFeature::NO_EREG_FUNCTION; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Changes ereg*() to preg*() calls', [new CodeSample('ereg("hi")', 'preg_match("#hi#");')]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [FuncCall::class, Assign::class]; } /** * @param FuncCall|Assign $node */ public function refactor(Node $node) : ?Node { if ($node instanceof FuncCall) { return $this->refactorFuncCall($node); } if (!$this->isEregFuncCallWithThreeArgs($node->expr)) { return null; } /** @var FuncCall $funcCall */ $funcCall = $node->expr; $node->expr = $this->createTernaryWithStrlenOfFirstMatch($funcCall); return $node; } private function shouldSkipFuncCall(FuncCall $funcCall) : bool { $functionName = $this->getName($funcCall); if ($functionName === null) { return \true; } if (!isset(self::OLD_NAMES_TO_NEW_ONES[$functionName])) { return \true; } if ($funcCall->isFirstClassCallable()) { return \true; } return !isset($funcCall->getArgs()[0]); } private function processStringPattern(FuncCall $funcCall, String_ $string, string $functionName) : void { $pattern = $string->value; $pattern = $this->eregToPcreTransformer->transform($pattern, $this->isCaseInsensitiveFunction($functionName)); $firstArg = $funcCall->getArgs()[0]; Assert::isInstanceOf($firstArg->value, String_::class); $firstArg->value->value = $pattern; } private function processVariablePattern(FuncCall $funcCall, Variable $variable, string $functionName) : void { $pregQuotePatternNode = $this->nodeFactory->createFuncCall('preg_quote', [new Arg($variable), new Arg(new String_('#'))]); $startConcat = new Concat(new String_('#'), $pregQuotePatternNode); $endDelimiter = $this->isCaseInsensitiveFunction($functionName) ? '#mi' : '#m'; $concat = new Concat($startConcat, new String_($endDelimiter)); /** @var Arg $arg */ $arg = $funcCall->args[0]; $arg->value = $concat; } /** * Equivalent of: * split(' ', 'hey Tom', 0); * ↓ * preg_split('# #', 'hey Tom', 1); */ private function processSplitLimitArgument(FuncCall $funcCall, string $functionName) : void { if (!isset($funcCall->args[2])) { return; } if (!$funcCall->args[2] instanceof Arg) { return; } if (\strncmp($functionName, 'split', \strlen('split')) !== 0) { return; } // 3rd argument - $limit, 0 → 1 if (!$funcCall->args[2]->value instanceof LNumber) { return; } /** @var LNumber $limitNumberNode */ $limitNumberNode = $funcCall->args[2]->value; if ($limitNumberNode->value !== 0) { return; } $limitNumberNode->value = 1; } private function createTernaryWithStrlenOfFirstMatch(FuncCall $funcCall) : Ternary { $thirdArg = $funcCall->getArgs()[2]; $arrayDimFetch = new ArrayDimFetch($thirdArg->value, new LNumber(0)); $strlenFuncCall = $this->nodeFactory->createFuncCall('strlen', [$arrayDimFetch]); return new Ternary($funcCall, $strlenFuncCall, $this->nodeFactory->createFalse()); } private function isCaseInsensitiveFunction(string $functionName) : bool { if (\strpos($functionName, 'eregi') !== \false) { return \true; } return \strpos($functionName, 'spliti') !== \false; } private function isEregFuncCallWithThreeArgs(Expr $expr) : bool { if (!$expr instanceof FuncCall) { return \false; } $functionName = $this->getName($expr); if (!\is_string($functionName)) { return \false; } if (!\in_array($functionName, ['ereg', 'eregi'], \true)) { return \false; } return isset($expr->getArgs()[2]); } private function refactorFuncCall(FuncCall $funcCall) : ?FuncCall { if ($this->shouldSkipFuncCall($funcCall)) { return null; } /** @var string $functionName */ $functionName = $this->getName($funcCall); $firstArg = $funcCall->getArgs()[0]; $patternExpr = $firstArg->value; if ($patternExpr instanceof String_) { $this->processStringPattern($funcCall, $patternExpr, $functionName); } elseif ($patternExpr instanceof Variable) { $this->processVariablePattern($funcCall, $patternExpr, $functionName); } $this->processSplitLimitArgument($funcCall, $functionName); $funcCall->name = new Name(self::OLD_NAMES_TO_NEW_ONES[$functionName]); return $funcCall; } } Rector/FuncCall/RandomFunctionRector.php 0000644 00000005727 15127645323 0014332 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\FuncCall; use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; use PhpParser\Node\Scalar\LNumber; use Rector\PhpParser\Node\Value\ValueResolver; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\FuncCall\RandomFunctionRector\RandomFunctionRectorTest */ final class RandomFunctionRector extends AbstractRector implements MinPhpVersionInterface { /** * @readonly * @var \Rector\PhpParser\Node\Value\ValueResolver */ private $valueResolver; /** * @var array<string, string> */ private const OLD_TO_NEW_FUNCTION_NAMES = ['getrandmax' => 'mt_getrandmax', 'srand' => 'mt_srand', 'rand' => 'random_int']; public function __construct(ValueResolver $valueResolver) { $this->valueResolver = $valueResolver; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Changes rand, srand, and getrandmax to newer alternatives', [new CodeSample('rand();', 'random_int();')]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [FuncCall::class]; } /** * @param FuncCall $node */ public function refactor(Node $node) : ?\PhpParser\Node\Expr\FuncCall { if ($node->isFirstClassCallable()) { return null; } foreach (self::OLD_TO_NEW_FUNCTION_NAMES as $oldFunctionName => $newFunctionName) { if ($this->isName($node, $oldFunctionName)) { $node->name = new Name($newFunctionName); // special case: random_int(); → random_int(0, getrandmax()); if ($newFunctionName === 'random_int') { $args = $node->getArgs(); if ($args === []) { $node->args[0] = new Arg(new LNumber(0)); $node->args[1] = new Arg($this->nodeFactory->createFuncCall('mt_getrandmax')); } elseif (\count($args) === 2) { $minValue = $this->valueResolver->getValue($args[0]->value); $maxValue = $this->valueResolver->getValue($args[1]->value); if (\is_int($minValue) && \is_int($maxValue) && $minValue > $maxValue) { $temp = $node->args[0]; $node->args[0] = $node->args[1]; $node->args[1] = $temp; } } } return $node; } } return null; } public function provideMinPhpVersion() : int { return PhpVersionFeature::CSPRNG_FUNCTIONS; } } Rector/FuncCall/CallUserMethodRector.php 0000644 00000004216 15127645323 0014247 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\FuncCall; use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\FuncCall\CallUserMethodRector\CallUserMethodRectorTest */ final class CallUserMethodRector extends AbstractRector implements MinPhpVersionInterface { /** * @var array<string, string> */ private const OLD_TO_NEW_FUNCTIONS = ['call_user_method' => 'call_user_func', 'call_user_method_array' => 'call_user_func_array']; public function provideMinPhpVersion() : int { return PhpVersionFeature::NO_CALL_USER_METHOD; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Changes call_user_method()/call_user_method_array() to call_user_func()/call_user_func_array()', [new CodeSample('call_user_method($method, $obj, "arg1", "arg2");', 'call_user_func(array(&$obj, "method"), "arg1", "arg2");')]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [FuncCall::class]; } /** * @param FuncCall $node */ public function refactor(Node $node) : ?Node { $oldFunctionNames = \array_keys(self::OLD_TO_NEW_FUNCTIONS); if (!$this->isNames($node, $oldFunctionNames)) { return null; } if ($node->isFirstClassCallable()) { return null; } $newName = self::OLD_TO_NEW_FUNCTIONS[$this->getName($node)]; $node->name = new Name($newName); /** @var Arg[] $oldArgs */ $oldArgs = $node->args; unset($node->args[1]); $newArgs = [$this->nodeFactory->createArg([$oldArgs[1]->value, $oldArgs[0]->value])]; unset($oldArgs[0]); unset($oldArgs[1]); $node->args = \array_merge($newArgs, $oldArgs); return $node; } } Rector/FuncCall/MultiDirnameRector.php 0000644 00000006031 15127645323 0013763 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\FuncCall; use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Scalar\LNumber; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\FuncCall\MultiDirnameRector\MultiDirnameRectorTest */ final class MultiDirnameRector extends AbstractRector implements MinPhpVersionInterface { /** * @var string */ private const DIRNAME = 'dirname'; /** * @var int */ private $nestingLevel = 0; public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Changes multiple dirname() calls to one with nesting level', [new CodeSample('dirname(dirname($path));', 'dirname($path, 2);')]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [FuncCall::class]; } /** * @param FuncCall $node */ public function refactor(Node $node) : ?Node { $this->nestingLevel = 0; if (!$this->isName($node, self::DIRNAME)) { return null; } $activeFuncCallNode = $node; $lastFuncCallNode = $node; while (($activeFuncCallNode = $this->matchNestedDirnameFuncCall($activeFuncCallNode)) instanceof FuncCall) { $lastFuncCallNode = $activeFuncCallNode; } // nothing to improve if ($this->shouldSkip()) { return null; } $node->args[0] = $lastFuncCallNode->args[0]; $node->args[1] = new Arg(new LNumber($this->nestingLevel)); return $node; } public function provideMinPhpVersion() : int { return PhpVersionFeature::DIRNAME_LEVELS; } private function shouldSkip() : bool { return $this->nestingLevel < 2; } private function matchNestedDirnameFuncCall(FuncCall $funcCall) : ?FuncCall { if (!$this->isName($funcCall, self::DIRNAME)) { return null; } if ($funcCall->isFirstClassCallable()) { return null; } $args = $funcCall->getArgs(); if (\count($args) >= 3) { return null; } // dirname($path, <LEVEL>); if (\count($args) === 2) { if (!$args[1]->value instanceof LNumber) { return null; } /** @var LNumber $levelNumber */ $levelNumber = $args[1]->value; $this->nestingLevel += $levelNumber->value; } else { ++$this->nestingLevel; } $nestedFuncCallNode = $args[0]->value; if (!$nestedFuncCallNode instanceof FuncCall) { return null; } if ($this->isName($nestedFuncCallNode, self::DIRNAME)) { return $nestedFuncCallNode; } return null; } } Rector/FuncCall/RenameMktimeWithoutArgsToTimeRector.php 0000644 00000003247 15127645323 0017300 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\FuncCall; use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\FuncCall\RenameMktimeWithoutArgsToTimeRector\RenameMktimeWithoutArgsToTimeRectorTest */ final class RenameMktimeWithoutArgsToTimeRector extends AbstractRector implements MinPhpVersionInterface { public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Renames mktime() without arguments to time()', [new CodeSample(<<<'CODE_SAMPLE' class SomeClass { public function run() { $time = mktime(1, 2, 3); $nextTime = mktime(); } } CODE_SAMPLE , <<<'CODE_SAMPLE' class SomeClass { public function run() { $time = mktime(1, 2, 3); $nextTime = time(); } } CODE_SAMPLE )]); } public function provideMinPhpVersion() : int { return PhpVersionFeature::NO_MKTIME_WITHOUT_ARG; } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [FuncCall::class]; } /** * @param FuncCall $node */ public function refactor(Node $node) : ?Node { if (!$this->isName($node, 'mktime')) { return null; } if ($node->args !== []) { return null; } $node->name = new Name('time'); return $node; } } Rector/List_/EmptyListRector.php 0000644 00000002712 15127645323 0012710 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\List_; use PhpParser\Node; use PhpParser\Node\Expr\ArrayItem; use PhpParser\Node\Expr\List_; use PhpParser\Node\Expr\Variable; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\List_\EmptyListRector\EmptyListRectorTest */ final class EmptyListRector extends AbstractRector implements MinPhpVersionInterface { public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('list() cannot be empty', [new CodeSample(<<<'CODE_SAMPLE' 'list() = $values;' CODE_SAMPLE , <<<'CODE_SAMPLE' 'list($unusedGenerated) = $values;' CODE_SAMPLE )]); } public function provideMinPhpVersion() : int { return PhpVersionFeature::NO_EMPTY_LIST; } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [List_::class]; } /** * @param List_ $node */ public function refactor(Node $node) : ?Node { foreach ($node->items as $item) { if ($item instanceof ArrayItem) { return null; } } $node->items[0] = new ArrayItem(new Variable('unusedGenerated')); return $node; } } Rector/FunctionLike/ExceptionHandlerTypehintRector.php 0000644 00000005046 15127645323 0017262 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Rector\FunctionLike; use PhpParser\Node; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\NullableType; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; use Rector\Rector\AbstractRector; use Rector\Util\StringUtils; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** * @see \Rector\Tests\Php70\Rector\FunctionLike\ExceptionHandlerTypehintRector\ExceptionHandlerTypehintRectorTest */ final class ExceptionHandlerTypehintRector extends AbstractRector implements MinPhpVersionInterface { /** * @var string * @see https://regex101.com/r/VBFXCR/1 */ private const HANDLE_INSENSITIVE_REGEX = '#handle#i'; public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Change typehint from `Exception` to `Throwable`.', [new CodeSample(<<<'CODE_SAMPLE' function handler(Exception $exception) { ... } set_exception_handler('handler'); CODE_SAMPLE , <<<'CODE_SAMPLE' function handler(Throwable $exception) { ... } set_exception_handler('handler'); CODE_SAMPLE )]); } /** * @return array<class-string<Node>> */ public function getNodeTypes() : array { return [Function_::class, ClassMethod::class]; } /** * @param Function_|ClassMethod $node */ public function refactor(Node $node) : ?Node { // exception handle has 1 param exactly if (\count($node->params) !== 1) { return null; } $paramNode = $node->params[0]; if ($paramNode->type === null) { return null; } // handle only Exception typehint $actualType = $paramNode->type instanceof NullableType ? $this->getName($paramNode->type->type) : $this->getName($paramNode->type); if ($actualType !== 'Exception') { return null; } // is probably handling exceptions if (!StringUtils::isMatch((string) $node->name, self::HANDLE_INSENSITIVE_REGEX)) { return null; } if (!$paramNode->type instanceof NullableType) { $paramNode->type = new FullyQualified('Throwable'); } else { $paramNode->type->type = new FullyQualified('Throwable'); } return $node; } public function provideMinPhpVersion() : int { return PhpVersionFeature::THROWABLE_TYPE; } } Enum/BattleshipCompareOrder.php 0000644 00000000352 15127645323 0012573 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70\Enum; final class BattleshipCompareOrder { /** * @var string */ public const ASC = 'asc'; /** * @var string */ public const DESC = 'desc'; } EregToPcreTransformer.php 0000644 00000025064 15127645323 0011516 0 ustar 00 <?php declare (strict_types=1); namespace Rector\Php70; use RectorPrefix202411\Nette\Utils\Strings; use Rector\Php70\Exception\InvalidEregException; /** * @changelog https://gist.github.com/lifthrasiir/704754/7e486f43e62fd1c9d3669330c251f8ca4a59a3f8 * * @see \Rector\Tests\Php70\EregToPcreTransformerTest */ final class EregToPcreTransformer { /** * @readonly * @var string */ private $pcreDelimiter = '#'; /** * @var array<string, string> */ private const CHARACTER_CLASS_MAP = [ ':alnum:' => '[:alnum:]', ':alpha:' => '[:alpha:]', ':blank:' => '[:blank:]', ':cntrl:' => '[:cntrl:]', ':digit:' => '\\d', ':graph:' => '[:graph:]', ':lower:' => '[:lower:]', ':print:' => '[:print:]', ':punct:' => '[:punct:]', // should include VT ':space:' => '013\\s', ':upper:' => '[:upper:]', ':xdigit:' => '[:xdigit:]', ]; /** * @var string * @see https://regex101.com/r/htpXFg/1 */ private const BOUND_REGEX = '/^(?<' . self::MINIMAL_NUMBER_PART . '>\\d|[1-9]\\d|1\\d\\d| 2[0-4]\\d|25[0-5]) (?<comma>,(?<' . self::MAXIMAL_NUMBER_PART . '>\\d|[1-9]\\d|1\\d\\d| 2[0-4]\\d|25[0-5])?)?$/x'; /** * @var string */ private const MINIMAL_NUMBER_PART = 'minimal_number'; /** * @var string */ private const MAXIMAL_NUMBER_PART = 'maximal_number'; /** * @var array<string, string> */ private $icache = []; /** * @var array<string, string> */ private $cache = []; /** * Change this via services configuratoin in rector.php if you need it * Single type is chosen to prevent every regular with different delimiter. */ public function __construct(string $pcreDelimiter = '#') { $this->pcreDelimiter = $pcreDelimiter; } // converts the ERE $s into the PCRE $r. triggers error on any invalid input. public function transform(string $content, bool $ignorecase) : string { if ($ignorecase) { if (isset($this->icache[$content])) { return $this->icache[$content]; } } elseif (isset($this->cache[$content])) { return $this->cache[$content]; } [$r, $i] = $this->_ere2pcre($content, 0); if ($i !== \strlen($content)) { throw new InvalidEregException('unescaped metacharacter ")"'); } if ($ignorecase) { return $this->icache[$content] = $this->pcreDelimiter . $r . $this->pcreDelimiter . 'mi'; } return $this->cache[$content] = $this->pcreDelimiter . $r . $this->pcreDelimiter . 'm'; } /** * Recursively converts ERE into PCRE, starting at the position $i. * * @return float[]|int[]|string[] */ private function _ere2pcre(string $content, int $i) : array { $r = ['']; $rr = 0; $l = \strlen($content); $normalizeUnprintableChar = \false; while ($i < $l) { // atom $char = $content[$i]; if ($char === '(') { $i = (int) $i; $i = $this->processBracket($content, $i, $l, $r, $rr); } elseif ($char === '[') { ++$i; $cls = ''; if ($i < $l && $content[$i] === '^') { $cls .= '^'; ++$i; } if ($i >= $l) { throw new InvalidEregException('"[" does not have a matching "]"'); } $start = \true; $i = (int) $i; [$cls, $i] = $this->processSquareBracket($content, $i, $l, $cls, $start); if ($i >= $l) { throw new InvalidEregException('"[" does not have a matching "]"'); } $r[$rr] .= '[' . $cls . ']'; } elseif ($char === ')') { break; } elseif ($char === '*' || $char === '+' || $char === '?') { throw new InvalidEregException('unescaped metacharacter "' . $char . '"'); } elseif ($char === '{') { if ($i + 1 < $l && \strpos('0123456789', $content[$i + 1]) !== \false) { $r[$rr] .= '\\{'; } else { throw new InvalidEregException('unescaped metacharacter "' . $char . '"'); } } elseif ($char === '.') { $r[$rr] .= $char; } elseif ($char === '^' || $char === '$') { $r[$rr] .= $char; ++$i; continue; } elseif ($char === '|') { if ($r[$rr] === '') { $normalizeUnprintableChar = \true; } $r[] = ''; ++$rr; ++$i; continue; } elseif ($char === '\\') { if (++$i >= $l) { throw new InvalidEregException('an invalid escape sequence at the end'); } $r[$rr] .= $this->_ere2pcre_escape($content[$i]); } else { // including ] and } which are allowed as a literal character $r[$rr] .= $this->_ere2pcre_escape($char); } ++$i; if ($i >= $l) { break; } // piece after the atom (only ONE of them is possible) $char = $content[$i]; if ($char === '*' || $char === '+' || $char === '?') { $r[$rr] .= $char; ++$i; } elseif ($char === '{') { $i = (int) $i; $i = $this->processCurlyBracket($content, $i, $r, $rr); } } if ($r[$rr] === '') { throw new InvalidEregException('empty regular expression or branch'); } return [$this->normalize(\implode('|', $r), $normalizeUnprintableChar), $i]; } private function normalize(string $content, bool $normalizeUnprintableChar) : string { if ($normalizeUnprintableChar) { $content = \str_replace("\f", '\\\\f', $content); } return \str_replace($this->pcreDelimiter, '\\' . $this->pcreDelimiter, $content); } /** * @param mixed[] $r */ private function processBracket(string $content, int $i, int $l, array &$r, int $rr) : int { // special case if ($i + 1 < $l && $content[$i + 1] === ')') { $r[$rr] .= '()'; ++$i; } else { $position = $i + 1; [$t, $ii] = $this->_ere2pcre($content, $position); if ($ii >= $l || $content[$ii] !== ')') { throw new InvalidEregException('"(" does not have a matching ")"'); } $r[$rr] .= '(' . $t . ')'; $i = $ii; } // retype $i = (int) $i; return $i; } /** * @return float[]|int[]|string[] */ private function processSquareBracket(string $s, int $i, int $l, string $cls, bool $start) : array { do { if ($s[$i] === '[' && $i + 1 < $l && \strpos('.=:', $s[$i + 1]) !== \false) { /** @var string $cls */ [$cls, $i] = $this->processCharacterClass($s, $i, $cls); } else { $a = $s[$i]; ++$i; if ($a === '-' && !$start && !($i < $l && $s[$i] === ']')) { throw new InvalidEregException('"-" is invalid for the start character in the brackets'); } if ($i < $l && $s[$i] === '-') { $b = $s[++$i]; if ($b === ']') { $cls .= $this->_ere2pcre_escape($a) . '\\-'; break; } elseif (\ord($a) > \ord($b)) { $errorMessage = \sprintf('an invalid character range %d-%d"', (int) $a, (int) $b); throw new InvalidEregException($errorMessage); } $cls .= $this->_ere2pcre_escape($a) . '-' . $this->_ere2pcre_escape($b); ++$i; } else { $cls .= $this->_ere2pcre_escape($a); } } $start = \false; } while ($i < $l && $s[$i] !== ']'); return [$cls, $i]; } private function _ere2pcre_escape(string $content) : string { if ($content === "\x00") { throw new InvalidEregException('a literal null byte in the regex'); } if (\strpos('\\^$.[]|()?*+{}-/', $content) !== \false) { return '\\' . $content; } return $content; } /** * @param mixed[] $r */ private function processCurlyBracket(string $s, int $i, array &$r, int $rr) : int { $ii = \strpos($s, '}', $i); if ($ii === \false) { throw new InvalidEregException('"{" does not have a matching "}"'); } $start = $i + 1; $length = $ii - ($i + 1); $bound = Strings::substring($s, $start, $length); $matches = Strings::match($bound, self::BOUND_REGEX); if ($matches === null) { throw new InvalidEregException('an invalid bound'); } if (isset($matches[self::MAXIMAL_NUMBER_PART])) { if ($matches[self::MINIMAL_NUMBER_PART] > $matches[self::MAXIMAL_NUMBER_PART]) { throw new InvalidEregException('an invalid bound'); } $r[$rr] .= '{' . $matches[self::MINIMAL_NUMBER_PART] . ',' . $matches[self::MAXIMAL_NUMBER_PART] . '}'; } elseif (isset($matches['comma'])) { $r[$rr] .= '{' . $matches[self::MINIMAL_NUMBER_PART] . ',}'; } else { $r[$rr] .= '{' . $matches[self::MINIMAL_NUMBER_PART] . '}'; } return $ii + 1; } /** * @return int[]|string[] */ private function processCharacterClass(string $content, int $i, string $cls) : array { $offset = $i; $ii = \strpos($content, ']', $offset); if ($ii === \false) { throw new InvalidEregException('"[" does not have a matching "]"'); } $start = $i + 1; $length = $ii - ($i + 1); $ccls = Strings::substring($content, $start, $length); if (!isset(self::CHARACTER_CLASS_MAP[$ccls])) { throw new InvalidEregException('an invalid or unsupported character class [' . $ccls . ']'); } $cls .= self::CHARACTER_CLASS_MAP[$ccls]; $i = $ii + 1; return [$cls, $i]; } }
| ver. 1.6 |
Github
|
.
| PHP 8.2.30 | ??????????? ?????????: 0 |
proxy
|
phpinfo
|
???????????