Skip to content

Commit

Permalink
[PHP 8.0] Add str_starts_with
Browse files Browse the repository at this point in the history
  • Loading branch information
TomasVotruba committed Apr 23, 2020
1 parent c2c7dba commit 0fec032
Show file tree
Hide file tree
Showing 44 changed files with 837 additions and 43 deletions.
1 change: 1 addition & 0 deletions config/set/php/php80.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
services:
Rector\Php80\Rector\FunctionLike\UnionTypesRector: null
Rector\Php80\Rector\NotIdentical\StrContainsRector: null
Rector\Php80\Rector\Identical\StrStartsWithRector: null
25 changes: 24 additions & 1 deletion docs/AllRectorsOverview.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# All 499 Rectors Overview
# All 500 Rectors Overview

- [Projects](#projects)
- [General](#general)
Expand Down Expand Up @@ -7874,6 +7874,29 @@ Replace strpos() !== false and strstr() with str_contains()

<br>

### `StrStartsWithRector`

- class: [`Rector\Php80\Rector\Identical\StrStartsWithRector`](/../master/rules/php80/src/Rector/Identical/StrStartsWithRector.php)
- [test fixtures](/../master/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture)

Change helper functions to str_starts_with()

```diff
class SomeClass
{
public function run()
{
- $isMatch = substr($haystack, 0, strlen($needle)) === $needle;
+ $isMatch = str_starts_with($haystack, $needle);

- $isNotMatch = substr($haystack, 0, strlen($needle)) !== $needle;
+ $isMatch = ! str_starts_with($haystack, $needle);
}
}
```

<br>

### `UnionTypesRector`

- class: [`Rector\Php80\Rector\FunctionLike\UnionTypesRector`](/../master/rules/php80/src/Rector/FunctionLike/UnionTypesRector.php)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use PhpParser\Node;
use PHPStan\Type\ObjectType;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\AliasedObjectType;
Expand Down Expand Up @@ -60,7 +59,7 @@ public function addUseImport(Node $positionNode, ObjectType $objectType): void
// fallback for freshly created Name nodes
$fileInfo = $this->currentFileInfoProvider->getSmartFileInfo();
if ($fileInfo === null) {
throw new ShouldNotHappenException();
return;
}
}

Expand Down
1 change: 0 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,3 @@ parameters:
- '#Method Rector\\Caching\\ChangedFilesDetector\:\:hashFile\(\) should return string but returns string\|false#'

- '#If condition is always false#'
- '#Parameter \#1 \$stmts of method Rector\\Core\\PhpParser\\Printer\\BetterStandardPrinter\:\:prettyPrintFile\(\) expects array<PhpParser\\Node\>, PhpParser\\Node\\Stmt\\Class_\|PhpParser\\Node\\Stmt\\Namespace_ given#'
2 changes: 2 additions & 0 deletions rector.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ parameters:
# bleeding edge feature
is_cache_enabled: true

auto_import_names: true

paths:
- src
- tests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ private function createInArrayFunction(Node $node, BinaryOp $binaryOp, Foreach_
$arguments[] = $this->createArg($this->createTrue());
}

return $this->createFunction('in_array', $arguments);
return $this->createFuncCall('in_array', $arguments);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ public function refactor(Node $node): ?Node
return $node;
}

return $this->createFunction('func_num_args');
return $this->createFuncCall('func_num_args');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function (Node $node): bool {
/** @var ConstFetch $boolConstFetch */
[$arraySearchFuncCall, $boolConstFetch] = $matchedNodes;

$inArrayFuncCall = $this->createFunction('in_array', [
$inArrayFuncCall = $this->createFuncCall('in_array', [
$arraySearchFuncCall->args[0],
$arraySearchFuncCall->args[1],
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,6 @@ public function refactor(Node $node): ?Node
$args = [];
$args[] = new Arg($node->args[1]->value, false, true);

return $this->createFunction($functionName, $args);
return $this->createFuncCall($functionName, $args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function refactor(Node $node): ?Node
if (array_key_exists($classname, $this->getClassmap())) {
$newClassname = new ClassConstFetch(new FullyQualified($this->getClassmap()[$classname]), 'class');

return $this->createFunction('oxNew', array_merge([$newClassname], $arguments));
return $this->createFuncCall('oxNew', array_merge([$newClassname], $arguments));
}

return $node;
Expand Down
4 changes: 2 additions & 2 deletions rules/php70/src/Rector/FuncCall/EregToPregMatchRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ private function processStringPattern(FuncCall $funcCall, String_ $patternNode,

private function processVariablePattern(FuncCall $funcCall, Variable $variable, string $functionName): void
{
$pregQuotePatternNode = $this->createFunction('preg_quote', [
$pregQuotePatternNode = $this->createFuncCall('preg_quote', [
new Arg($variable),
new Arg(new String_('#')),
]);
Expand Down Expand Up @@ -161,7 +161,7 @@ private function processSplitLimitArgument(FuncCall $funcCall, string $functionN
private function createTernaryWithStrlenOfFirstMatch(FuncCall $funcCall): Ternary
{
$arrayDimFetch = new ArrayDimFetch($funcCall->args[2]->value, new LNumber(0));
$strlenFuncCall = $this->createFunction('strlen', [$arrayDimFetch]);
$strlenFuncCall = $this->createFuncCall('strlen', [$arrayDimFetch]);

return new Ternary($funcCall, $strlenFuncCall, $this->createFalse());
}
Expand Down
2 changes: 1 addition & 1 deletion rules/php70/src/Rector/FuncCall/RandomFunctionRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function refactor(Node $node): ?Node
// special case: random_int(); → random_int(0, getrandmax());
if ($newFunctionName === 'random_int' && count($node->args) === 0) {
$node->args[0] = new Arg(new LNumber(0));
$node->args[1] = new Arg($this->createFunction('mt_getrandmax'));
$node->args[1] = new Arg($this->createFuncCall('mt_getrandmax'));
}

return $node;
Expand Down
2 changes: 1 addition & 1 deletion rules/php70/src/Rector/List_/ListSplitStringRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function refactor(Node $node): ?Node
return null;
}

$node->expr = $this->createFunction('str_split', [$node->expr]);
$node->expr = $this->createFuncCall('str_split', [$node->expr]);

return $node;
}
Expand Down
2 changes: 1 addition & 1 deletion rules/php70/src/Rector/List_/ListSwapArrayOrderRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public function refactor(Node $node): ?Node
}

// wrap with array_reverse, to reflect reverse assign order in left
$node->expr = $this->createFunction('array_reverse', [$node->expr]);
$node->expr = $this->createFuncCall('array_reverse', [$node->expr]);

return $node;
}
Expand Down
2 changes: 1 addition & 1 deletion rules/php71/src/Rector/FuncCall/CountOnNullRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public function refactor(Node $node): ?Node
$conditionNode = new FuncCall(new Name('is_countable'), [new Arg($countedNode)]);
} else {
$conditionNode = new BooleanOr(
$this->createFunction('is_array', [new Arg($countedNode)]),
$this->createFuncCall('is_array', [new Arg($countedNode)]),
new Instanceof_($countedNode, new FullyQualified('Countable'))
);
}
Expand Down
12 changes: 6 additions & 6 deletions rules/php72/src/Rector/Each/ListEachRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,16 @@ public function refactor(Node $node): ?Node

// only key: list($key, ) = each($values);
if ($listNode->items[0] && $listNode->items[1] === null) {
$keyFuncCall = $this->createFunction('key', $eachFuncCall->args);
$keyFuncCall = $this->createFuncCall('key', $eachFuncCall->args);
return new Assign($listNode->items[0]->value, $keyFuncCall);
}

// only value: list(, $value) = each($values);
if ($listNode->items[1] && $listNode->items[0] === null) {
$nextFuncCall = $this->createFunction('next', $eachFuncCall->args);
$nextFuncCall = $this->createFuncCall('next', $eachFuncCall->args);
$this->addNodeAfterNode($nextFuncCall, $node);

$currentFuncCall = $this->createFunction('current', $eachFuncCall->args);
$currentFuncCall = $this->createFuncCall('current', $eachFuncCall->args);
return new Assign($listNode->items[1]->value, $currentFuncCall);
}

Expand All @@ -93,16 +93,16 @@ public function refactor(Node $node): ?Node
// $key = key($values);
// $value = current($values);
// next($values); - only inside a loop
$currentFuncCall = $this->createFunction('current', $eachFuncCall->args);
$currentFuncCall = $this->createFuncCall('current', $eachFuncCall->args);
$assignCurrentNode = new Assign($listNode->items[1]->value, $currentFuncCall);
$this->addNodeAfterNode($assignCurrentNode, $node);

if ($this->isInsideDoWhile($node)) {
$nextFuncCall = $this->createFunction('next', $eachFuncCall->args);
$nextFuncCall = $this->createFuncCall('next', $eachFuncCall->args);
$this->addNodeAfterNode($nextFuncCall, $node);
}

$keyFuncCall = $this->createFunction('key', $eachFuncCall->args);
$keyFuncCall = $this->createFuncCall('key', $eachFuncCall->args);
return new Assign($listNode->items[0]->value, $keyFuncCall);
}

Expand Down
2 changes: 1 addition & 1 deletion rules/php72/src/Rector/Each/WhileEachToForeachRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public function refactor(Node $node): ?Node
/** @var List_ $listNode */
$listNode = $assignNode->var;

$foreachedExpr = count($listNode->items) === 1 ? $this->createFunction(
$foreachedExpr = count($listNode->items) === 1 ? $this->createFuncCall(
'array_keys',
[$eachFuncCall->args[0]]
) : $eachFuncCall->args[0]->value;
Expand Down
2 changes: 1 addition & 1 deletion rules/php72/src/Rector/Unset_/UnsetCastRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public function refactor(Node $node): ?Node
$unset = $node->expr;

if ($this->areNodesEqual($node->var, $unset->expr)) {
return $this->createFunction('unset', [$node->var]);
return $this->createFuncCall('unset', [$node->var]);
}
}

Expand Down
10 changes: 10 additions & 0 deletions rules/php80/config/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
_defaults:
public: true
autowire: true

Rector\Php80\:
resource: '../src'
exclude:
- '../src/Rector/**/*Rector.php'
- '../src/ValueObject/*'
51 changes: 51 additions & 0 deletions rules/php80/src/MatchAndRefactor/AbstractMatchAndRefactor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace Rector\Php80\MatchAndRefactor;

use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use Rector\Core\PhpParser\Node\Value\ValueResolver;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
use Rector\NodeNameResolver\NodeNameResolver;

abstract class AbstractMatchAndRefactor
{
/**
* @var NodeNameResolver
*/
protected $nodeNameResolver;

/**
* @var ValueResolver
*/
protected $valueResolver;

/**
* @var BetterStandardPrinter
*/
protected $betterStandardPrinter;

/**
* @required
*/
public function autowireAbstractMatchAndRefactor(
NodeNameResolver $nodeNameResolver,
ValueResolver $valueResolver,
BetterStandardPrinter $betterStandardPrinter
): void {
$this->nodeNameResolver = $nodeNameResolver;
$this->valueResolver = $valueResolver;
$this->betterStandardPrinter = $betterStandardPrinter;
}

protected function isFuncCallName(Node $node, string $name): bool
{
if (! $node instanceof FuncCall) {
return false;
}

return $this->nodeNameResolver->isName($node, $name);
}
}
80 changes: 80 additions & 0 deletions rules/php80/src/MatchAndRefactor/StrncmpMatchAndRefactor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace Rector\Php80\MatchAndRefactor;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp\Identical;
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use Rector\Php80\ValueObject\StrncmpFuncCallToHaystack;

final class StrncmpMatchAndRefactor extends AbstractMatchAndRefactor
{
/**
* @var string
*/
private const FUNCTION_NAME = 'strncmp';

/**
* @param Identical|NotIdentical $binaryOp
*/
public function match(BinaryOp $binaryOp): ?StrncmpFuncCallToHaystack
{
$isPositive = $binaryOp instanceof Identical ? true : false;

if ($this->isFuncCallName($binaryOp->left, self::FUNCTION_NAME)) {
/** @var FuncCall $funcCall */
$funcCall = $binaryOp->left;
return new StrncmpFuncCallToHaystack($funcCall, $isPositive);
}

if ($this->isFuncCallName($binaryOp->right, self::FUNCTION_NAME)) {
/** @var FuncCall $funcCall */
$funcCall = $binaryOp->right;
return new StrncmpFuncCallToHaystack($funcCall, $isPositive);
}

return null;
}

public function refactor(StrncmpFuncCallToHaystack $strncmpFuncCallToHaystack): ?Node
{
$strncmpFuncCall = $strncmpFuncCallToHaystack->getStrncmpFuncCall();

$needleExpr = $strncmpFuncCall->args[1]->value;

if (! $this->isFuncCallName($strncmpFuncCall->args[2]->value, 'strlen')) {
return null;
}

/** @var FuncCall $strlenFuncCall */
$strlenFuncCall = $strncmpFuncCall->args[2]->value;
$strlenArgumentValue = $strlenFuncCall->args[0]->value;

if (! $this->betterStandardPrinter->areNodesEqual($needleExpr, $strlenArgumentValue)) {
return null;
}

$strStartsWith = $this->createStrStartsWith($strncmpFuncCall, $needleExpr);
if ($strncmpFuncCallToHaystack->isPositive()) {
return $strStartsWith;
}

return new BooleanNot($strStartsWith);
}

private function createStrStartsWith(FuncCall $strncmpFuncCall, Expr $needleExpr): FuncCall
{
$haystackExpr = $strncmpFuncCall->args[0]->value;
$args = [new Arg($haystackExpr), new Arg($needleExpr)];

return new FuncCall(new Name('str_starts_with'), $args);
}
}
Loading

0 comments on commit 0fec032

Please sign in to comment.