Skip to content

Commit

Permalink
Refactor PARENT_NODE away from EregToPregMatchRector (#3973)
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed May 26, 2023
1 parent 14fc013 commit d8e8a34
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 51 deletions.
1 change: 1 addition & 0 deletions phpstan.neon
Expand Up @@ -744,6 +744,7 @@ parameters:
- '#Anonymous variable in a (.*?)->stmts\[(.*?)]\->\.\.\.\(\)` method call can lead to false dead methods\. Make sure the variable type is known#'

- '#Access to an undefined property PhpParser\\Node\\Stmt\\ClassLike\|PhpParser\\Node\\Stmt\\Declare_\|Rector\\Core\\Contract\\PhpParser\\Node\\StmtsAwareInterface\:\:\$stmts#'
- '#Access to an undefined property \(PhpParser\\Node\\Stmt&Rector\\Core\\Contract\\PhpParser\\Node\\StmtsAwareInterface\)\|PhpParser\\Node\\Stmt\\ClassLike\|PhpParser\\Node\\Stmt\\Declare_\:\:\$stmts#'

# WIP
- '#Fetching deprecated class constant PARENT_NODE#'
Expand Down
26 changes: 6 additions & 20 deletions rules/MysqlToMysqli/Rector/Assign/MysqlAssignToMysqliRector.php
Expand Up @@ -10,14 +10,12 @@
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Scalar\LNumber;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Expression;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

Expand Down Expand Up @@ -98,7 +96,9 @@ public function refactor(Node $node)
}

if ($this->isName($funcCall, 'mysql_fetch_field')) {
return $this->processMysqlFetchField($assign, $funcCall);
$this->processMysqlFetchField($funcCall);

return $node;
}

if ($this->isName($funcCall, 'mysql_result')) {
Expand Down Expand Up @@ -149,19 +149,15 @@ private function processMysqliSelectDb(Assign $assign, FuncCall $funcCall): arra
$funcCall->name = new Name('mysqli_select_db');

$mysqliQueryAssign = new Assign($assign->var, new FuncCall(new Name('mysqli_query'), [$funcCall->args[1]]));

unset($funcCall->args[1]);

return [new Expression($funcCall), new Expression($mysqliQueryAssign)];
}

private function processMysqlFetchField(Assign $assign, FuncCall $funcCall): Assign
private function processMysqlFetchField(FuncCall $funcCall): void
{
$funcCall->name = isset($funcCall->args[1]) ? new Name('mysqli_fetch_field_direct') : new Name(
'mysqli_fetch_field'
);

return $assign;
$hasExactField = isset($funcCall->args[1]);
$funcCall->name = new Name($hasExactField ? 'mysqli_fetch_field_direct' : 'mysqli_fetch_field');
}

/**
Expand Down Expand Up @@ -193,16 +189,6 @@ private function processFieldToFieldDirect(Assign $assign, FuncCall $funcCall):
continue;
}

// @todo remove
$parentNode = $funcCall->getAttribute(AttributeKey::PARENT_NODE);
if ($parentNode instanceof PropertyFetch) {
continue;
}

if ($parentNode instanceof StaticPropertyFetch) {
continue;
}

$funcCall->name = new Name('mysqli_fetch_field_direct');
$assign->expr = new PropertyFetch($funcCall, $property);

Expand Down
86 changes: 55 additions & 31 deletions rules/Php70/Rector/FuncCall/EregToPregMatchRector.php
Expand Up @@ -6,6 +6,7 @@

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;
Expand All @@ -17,7 +18,6 @@
use PhpParser\Node\Scalar\String_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Php70\EregToPcreTransformer;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
Expand Down Expand Up @@ -65,49 +65,31 @@ public function getRuleDefinition(): RuleDefinition
*/
public function getNodeTypes(): array
{
return [FuncCall::class];
return [FuncCall::class, Assign::class];
}

/**
* @param FuncCall $node
* @param FuncCall|Assign $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkip($node)) {
return null;
if ($node instanceof FuncCall) {
return $this->refactorFuncCall($node);
}

/** @var string $functionName */
$functionName = $this->getName($node);

$firstArg = $node->getArgs()[0];
$patternNode = $firstArg->value;
if ($patternNode instanceof String_) {
$this->processStringPattern($node, $patternNode, $functionName);
} elseif ($patternNode instanceof Variable) {
$this->processVariablePattern($node, $patternNode, $functionName);
if (! $this->isEregFuncCallWithThreeArgs($node->expr)) {
return null;
}

$this->processSplitLimitArgument($node, $functionName);

$node->name = new Name(self::OLD_NAMES_TO_NEW_ONES[$functionName]);
/** @var FuncCall $funcCall */
$funcCall = $node->expr;

// ereg|eregi 3rd argument return value fix
if (in_array(
$functionName,
['ereg', 'eregi'],
true
) && isset($node->args[2]) && $node->args[2] instanceof Arg) {
$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
if ($parentNode instanceof Assign) {
return $this->createTernaryWithStrlenOfFirstMatch($node);
}
}
$node->expr = $this->createTernaryWithStrlenOfFirstMatch($funcCall);

return $node;
}

private function shouldSkip(FuncCall $funcCall): bool
private function shouldSkipFuncCall(FuncCall $funcCall): bool
{
$functionName = $this->getName($funcCall);
if ($functionName === null) {
Expand Down Expand Up @@ -183,8 +165,8 @@ private function processSplitLimitArgument(FuncCall $funcCall, string $functionN

private function createTernaryWithStrlenOfFirstMatch(FuncCall $funcCall): Ternary
{
/** @var Arg $thirdArg */
$thirdArg = $funcCall->args[2];
$thirdArg = $funcCall->getArgs()[2];

$arrayDimFetch = new ArrayDimFetch($thirdArg->value, new LNumber(0));
$strlenFuncCall = $this->nodeFactory->createFuncCall('strlen', [$arrayDimFetch]);

Expand All @@ -199,4 +181,46 @@ private function isCaseInsensitiveFunction(string $functionName): bool

return \str_contains($functionName, 'spliti');
}

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;
}
}

0 comments on commit d8e8a34

Please sign in to comment.