2019-10-13 05:59:52 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
2018-10-08 05:19:10 +00:00
|
|
|
|
2019-09-22 18:57:03 +00:00
|
|
|
namespace Rector\Php70\Rector\FuncCall;
|
2018-10-08 05:19:10 +00:00
|
|
|
|
|
|
|
use Nette\Utils\Strings;
|
|
|
|
use PhpParser\Node;
|
|
|
|
use PhpParser\Node\Arg;
|
|
|
|
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_;
|
2020-02-06 21:48:18 +00:00
|
|
|
use Rector\Core\Rector\AbstractRector;
|
2019-04-13 09:20:27 +00:00
|
|
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
2019-09-23 14:36:58 +00:00
|
|
|
use Rector\Php70\EregToPcreTransformer;
|
2020-11-16 17:50:38 +00:00
|
|
|
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
|
|
|
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
2018-10-08 05:19:10 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @see http://php.net/reference.pcre.pattern.posix
|
|
|
|
* @see https://stackoverflow.com/a/17033826/1348344
|
|
|
|
* @see https://docstore.mik.ua/orelly/webprog/pcook/ch13_02.htm
|
2020-02-23 17:51:22 +00:00
|
|
|
*
|
2019-09-23 11:43:13 +00:00
|
|
|
* @see \Rector\Php70\Tests\Rector\FuncCall\EregToPregMatchRector\EregToPregMatchRectorTest
|
2018-10-08 05:19:10 +00:00
|
|
|
*/
|
|
|
|
final class EregToPregMatchRector extends AbstractRector
|
|
|
|
{
|
|
|
|
/**
|
2021-02-23 01:25:34 +00:00
|
|
|
* @var array<string, string>
|
2018-10-08 05:19:10 +00:00
|
|
|
*/
|
2020-02-18 22:09:25 +00:00
|
|
|
private const OLD_NAMES_TO_NEW_ONES = [
|
2018-10-08 05:19:10 +00:00
|
|
|
'ereg' => 'preg_match',
|
|
|
|
'eregi' => 'preg_match',
|
|
|
|
'ereg_replace' => 'preg_replace',
|
|
|
|
'eregi_replace' => 'preg_replace',
|
|
|
|
'split' => 'preg_split',
|
|
|
|
'spliti' => 'preg_split',
|
|
|
|
];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var EregToPcreTransformer
|
|
|
|
*/
|
|
|
|
private $eregToPcreTransformer;
|
|
|
|
|
2018-10-15 04:36:58 +00:00
|
|
|
public function __construct(EregToPcreTransformer $eregToPcreTransformer)
|
2018-10-08 05:19:10 +00:00
|
|
|
{
|
|
|
|
$this->eregToPcreTransformer = $eregToPcreTransformer;
|
|
|
|
}
|
|
|
|
|
2020-11-16 17:50:38 +00:00
|
|
|
public function getRuleDefinition(): RuleDefinition
|
2018-10-08 05:19:10 +00:00
|
|
|
{
|
2020-11-16 17:50:38 +00:00
|
|
|
return new RuleDefinition(
|
2018-10-08 05:19:10 +00:00
|
|
|
'Changes ereg*() to preg*() calls',
|
|
|
|
[new CodeSample('ereg("hi")', 'preg_match("#hi#");')]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-02-27 00:06:15 +00:00
|
|
|
* @return array<class-string<Node>>
|
2018-10-08 05:19:10 +00:00
|
|
|
*/
|
|
|
|
public function getNodeTypes(): array
|
|
|
|
{
|
|
|
|
return [FuncCall::class];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-10-15 04:36:58 +00:00
|
|
|
* @param FuncCall $node
|
2018-10-08 05:19:10 +00:00
|
|
|
*/
|
2018-10-15 04:36:58 +00:00
|
|
|
public function refactor(Node $node): ?Node
|
2018-10-08 05:19:10 +00:00
|
|
|
{
|
2018-10-15 04:36:58 +00:00
|
|
|
$functionName = $this->getName($node);
|
2019-01-14 18:21:23 +00:00
|
|
|
if ($functionName === null) {
|
|
|
|
return null;
|
|
|
|
}
|
2018-10-08 05:19:10 +00:00
|
|
|
|
2020-02-18 22:09:25 +00:00
|
|
|
if (! isset(self::OLD_NAMES_TO_NEW_ONES[$functionName])) {
|
2018-10-21 10:19:14 +00:00
|
|
|
return null;
|
2018-10-08 05:19:10 +00:00
|
|
|
}
|
|
|
|
|
2018-10-15 04:36:58 +00:00
|
|
|
$patternNode = $node->args[0]->value;
|
2018-10-08 05:19:10 +00:00
|
|
|
if ($patternNode instanceof String_) {
|
2018-10-15 04:36:58 +00:00
|
|
|
$this->processStringPattern($node, $patternNode, $functionName);
|
2018-10-08 05:19:10 +00:00
|
|
|
} elseif ($patternNode instanceof Variable) {
|
2018-10-15 04:36:58 +00:00
|
|
|
$this->processVariablePattern($node, $patternNode, $functionName);
|
2018-10-08 05:19:10 +00:00
|
|
|
}
|
|
|
|
|
2018-10-15 04:36:58 +00:00
|
|
|
$this->processSplitLimitArgument($node, $functionName);
|
2018-10-08 05:19:10 +00:00
|
|
|
|
2020-02-18 22:09:25 +00:00
|
|
|
$node->name = new Name(self::OLD_NAMES_TO_NEW_ONES[$functionName]);
|
2018-10-08 05:19:10 +00:00
|
|
|
|
|
|
|
// ereg|eregi 3rd argument return value fix
|
2018-10-15 04:36:58 +00:00
|
|
|
if (in_array($functionName, ['ereg', 'eregi'], true) && isset($node->args[2])) {
|
2019-04-13 09:20:27 +00:00
|
|
|
$parentNode = $node->getAttribute(AttributeKey::PARENT_NODE);
|
2018-10-08 05:19:10 +00:00
|
|
|
if ($parentNode instanceof Assign) {
|
2018-10-15 04:36:58 +00:00
|
|
|
return $this->createTernaryWithStrlenOfFirstMatch($node);
|
2018-10-08 05:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-15 04:36:58 +00:00
|
|
|
return $node;
|
2018-10-08 05:19:10 +00:00
|
|
|
}
|
|
|
|
|
2020-06-29 21:19:37 +00:00
|
|
|
private function processStringPattern(FuncCall $funcCall, String_ $string, string $functionName): void
|
2018-10-08 05:19:10 +00:00
|
|
|
{
|
2020-06-29 21:19:37 +00:00
|
|
|
$pattern = $string->value;
|
2018-10-08 05:19:10 +00:00
|
|
|
$pattern = $this->eregToPcreTransformer->transform($pattern, $this->isCaseInsensitiveFunction($functionName));
|
|
|
|
|
2019-02-22 17:25:31 +00:00
|
|
|
$funcCall->args[0]->value = new String_($pattern);
|
2018-10-08 05:19:10 +00:00
|
|
|
}
|
|
|
|
|
2019-02-22 17:25:31 +00:00
|
|
|
private function processVariablePattern(FuncCall $funcCall, Variable $variable, string $functionName): void
|
2018-10-08 05:19:10 +00:00
|
|
|
{
|
2021-01-30 21:41:25 +00:00
|
|
|
$pregQuotePatternNode = $this->nodeFactory->createFuncCall('preg_quote', [
|
2019-02-22 17:25:31 +00:00
|
|
|
new Arg($variable),
|
2018-10-08 05:19:10 +00:00
|
|
|
new Arg(new String_('#')),
|
|
|
|
]);
|
|
|
|
|
|
|
|
$startConcat = new Concat(new String_('#'), $pregQuotePatternNode);
|
|
|
|
|
|
|
|
$endDelimiter = $this->isCaseInsensitiveFunction($functionName) ? '#mi' : '#m';
|
|
|
|
$concat = new Concat($startConcat, new String_($endDelimiter));
|
|
|
|
|
2019-02-22 17:25:31 +00:00
|
|
|
$funcCall->args[0]->value = $concat;
|
2018-10-08 05:19:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Equivalent of:
|
|
|
|
* split(' ', 'hey Tom', 0);
|
|
|
|
* ↓
|
|
|
|
* preg_split('# #', 'hey Tom', 1);
|
|
|
|
*/
|
2019-02-22 17:25:31 +00:00
|
|
|
private function processSplitLimitArgument(FuncCall $funcCall, string $functionName): void
|
2018-10-08 05:19:10 +00:00
|
|
|
{
|
|
|
|
if (! Strings::startsWith($functionName, 'split')) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3rd argument - $limit, 0 → 1
|
2019-02-22 17:25:31 +00:00
|
|
|
if (! isset($funcCall->args[2])) {
|
2018-10-08 05:19:10 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-02-22 17:25:31 +00:00
|
|
|
if (! $funcCall->args[2]->value instanceof LNumber) {
|
2018-10-08 05:19:10 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** @var LNumber $limitNumberNode */
|
2019-02-22 17:25:31 +00:00
|
|
|
$limitNumberNode = $funcCall->args[2]->value;
|
2018-10-08 05:19:10 +00:00
|
|
|
if ($limitNumberNode->value !== 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$limitNumberNode->value = 1;
|
|
|
|
}
|
|
|
|
|
2019-02-22 17:25:31 +00:00
|
|
|
private function createTernaryWithStrlenOfFirstMatch(FuncCall $funcCall): Ternary
|
2018-10-08 05:19:10 +00:00
|
|
|
{
|
2019-02-22 17:25:31 +00:00
|
|
|
$arrayDimFetch = new ArrayDimFetch($funcCall->args[2]->value, new LNumber(0));
|
2021-01-30 21:41:25 +00:00
|
|
|
$strlenFuncCall = $this->nodeFactory->createFuncCall('strlen', [$arrayDimFetch]);
|
2018-10-08 05:19:10 +00:00
|
|
|
|
2021-01-30 21:41:25 +00:00
|
|
|
return new Ternary($funcCall, $strlenFuncCall, $this->nodeFactory->createFalse());
|
2018-10-08 05:19:10 +00:00
|
|
|
}
|
2018-10-31 15:34:37 +00:00
|
|
|
|
|
|
|
private function isCaseInsensitiveFunction(string $functionName): bool
|
|
|
|
{
|
|
|
|
if (Strings::contains($functionName, 'eregi')) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return Strings::contains($functionName, 'spliti');
|
|
|
|
}
|
2018-10-08 05:19:10 +00:00
|
|
|
}
|