[Php55] Handle crash on concat variable single quote on PregReplaceEModifierRector (#2483)

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Abdul Malik Ikhsan 2022-06-13 13:53:39 +07:00 committed by GitHub
parent 28ed5cf916
commit 6f164da439
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 61 additions and 12 deletions

View File

@ -0,0 +1,33 @@
<?php
namespace Rector\Tests\Php55\Rector\FuncCall\PregReplaceEModifierRector\Fixture;
class ConcatVariable
{
public function run()
{
$test = 'become : ';
$string ='string';
echo preg_replace("#([a-z]*)#e", "'".$test."' . strtoupper('\\1')", $string);
}
}
?>
-----
<?php
namespace Rector\Tests\Php55\Rector\FuncCall\PregReplaceEModifierRector\Fixture;
class ConcatVariable
{
public function run()
{
$test = 'become : ';
$string ='string';
echo preg_replace_callback('#([a-z]*)#', function ($matches) use ($test) {
return $test . strtoupper($matches[1]);
}, $string);
}
}
?>

View File

@ -102,9 +102,7 @@ CODE_SAMPLE
/** @var Arg $secondArgument */
$secondArgument = $node->args[1];
$secondArgumentValue = $secondArgument->value;
$anonymousFunction = $this->anonymousFunctionFactory->createAnonymousFunctionFromString(
$secondArgumentValue
);
$anonymousFunction = $this->anonymousFunctionFactory->createAnonymousFunctionFromExpr($secondArgumentValue);
if (! $anonymousFunction instanceof Closure) {
return null;
}

View File

@ -39,11 +39,11 @@ use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use PHPStan\Type\VoidType;
use Rector\Core\Contract\PhpParser\NodePrinterInterface;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\AstResolver;
use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\Core\PhpParser\Parser\InlineCodeParser;
use Rector\Core\PhpParser\Parser\SimplePhpParser;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
@ -71,7 +71,8 @@ final class AnonymousFunctionFactory
private readonly NodeComparator $nodeComparator,
private readonly AstResolver $astResolver,
private readonly NodePrinterInterface $nodePrinter,
private readonly PrivatesAccessor $privatesAccessor
private readonly PrivatesAccessor $privatesAccessor,
private readonly InlineCodeParser $inlineCodeParser
) {
}
@ -142,14 +143,11 @@ final class AnonymousFunctionFactory
return $anonymousFunction;
}
public function createAnonymousFunctionFromString(Expr $expr): ?Closure
public function createAnonymousFunctionFromExpr(Expr $expr): ?Closure
{
if (! $expr instanceof String_) {
// not supported yet
throw new ShouldNotHappenException();
}
$stringValue = $this->inlineCodeParser->stringify($expr);
$phpCode = '<?php ' . $expr->value . ';';
$phpCode = '<?php ' . $stringValue . ';';
$contentStmts = $this->simplePhpParser->parseString($phpCode);
$anonymousFunction = new Closure();
@ -179,6 +177,15 @@ final class AnonymousFunctionFactory
$anonymousFunction->stmts[] = new Return_($stmt);
$anonymousFunction->params[] = new Param(new Variable('matches'));
$variables = $expr instanceof Variable
? []
: $this->betterNodeFinder->findInstanceOf($expr, Variable::class);
$anonymousFunction->uses = array_map(
fn (Variable $variable): ClosureUse => new ClosureUse($variable),
$variables
);
return $anonymousFunction;
}

View File

@ -41,6 +41,12 @@ final class InlineCodeParser
*/
private const ENDING_SEMI_COLON_REGEX = '#;(\s+)?$#';
/**
* @var string
* @see https://regex101.com/r/8fDjnR/1
*/
private const VARIABLE_IN_SINGLE_QUOTED_REGEX = '#\'(?<variable>\$.*)\'#U';
public function __construct(
private readonly NodePrinterInterface $nodePrinter,
private readonly NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator,
@ -83,7 +89,12 @@ final class InlineCodeParser
}
if ($expr instanceof Concat) {
return $this->stringify($expr->left) . $this->stringify($expr->right);
$string = $this->stringify($expr->left) . $this->stringify($expr->right);
return Strings::replace(
$string,
self::VARIABLE_IN_SINGLE_QUOTED_REGEX,
fn (array $match) => $match['variable']
);
}
return $this->nodePrinter->print($expr);