Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ script:

mkdir laravel-dir
composer create-project laravel/framework laravel-dir
# missing in laravel for some reason
composer require doctrine/dbal -d laravel-dir
composer dump-autoload --no-dev -d laravel-dir

# 2. run an nother project
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Rector\CodeQuality\Rector\Array_;

use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
Expand Down Expand Up @@ -93,14 +95,21 @@ public function getNodeTypes(): array
*/
public function refactor(Node $node): ?Node
{
if (count($node->items) !== 2) {
if ($this->shouldSkipArray($node)) {
return null;
}

// is callable
$objectVariable = $node->items[0]->value;
if (! $objectVariable instanceof Variable && ! $objectVariable instanceof PropertyFetch) {
return null;
}

$methodName = $node->items[1]->value;
if (! $methodName instanceof String_) {
return null;
}

$classMethod = $this->matchCallableMethod($objectVariable, $node->items[1]->value);
$classMethod = $this->matchCallableMethod($objectVariable, $methodName);
if ($classMethod === null) {
return null;
}
Expand Down Expand Up @@ -140,7 +149,10 @@ private function convertParamsToArgs(array $params): array
return $args;
}

private function matchCallableMethod(Expr $objectExpr, Expr $methodExpr): ?ClassMethod
/**
* @param Variable|PropertyFetch $objectExpr
*/
private function matchCallableMethod(Expr $objectExpr, String_ $methodExpr): ?ClassMethod
{
$methodName = $this->getValue($methodExpr);

Expand Down Expand Up @@ -168,4 +180,15 @@ private function matchCallableMethod(Expr $objectExpr, Expr $methodExpr): ?Class

return null;
}

private function shouldSkipArray(Array_ $array): bool
{
// callback is exactly "[$two, 'items']"
if (count($array->items) !== 2) {
return true;
}

// can be totally empty in case of "[, $value]"
return $array->items[0] === null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public function test(): void
__DIR__ . '/Fixture/fixture.php.inc',
__DIR__ . '/Fixture/skip.php.inc',
__DIR__ . '/Fixture/another_class.php.inc',
__DIR__ . '/Fixture/skip_another_class.php.inc',
__DIR__ . '/Fixture/skip_empty_first_array.php.inc',
__DIR__ . '/Fixture/skip_as_well.php.inc',
]);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
<?php declare(strict_types=1);

namespace Rector\CodeQuality\Tests\Rector\Array_\CallableThisArrayToAnonymousFunctionRector\Fixture;

use PhpParser\Node;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Property;
use Rector\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
use Rector\PhpParser\Node\Manipulator\ClassManipulator;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\ConfiguredCodeSample;
use Rector\RectorDefinition\RectorDefinition;

final class PseudoNamespaceToNamespaceRector
{
/**
* @var string|null
*/
private $newNamespace;

/**
* @var string[][]|null[]
*/
private $namespacePrefixesWithExcludedClasses = [];

/**
* @var ClassManipulator
*/
private $classManipulator;

/**
* @var DocBlockManipulator
*/
private $docBlockManipulator;

/**
* @param string[][]|null[] $namespacePrefixesWithExcludedClasses
*/
public function __construct(
ClassManipulator $classManipulator,
DocBlockManipulator $docBlockManipulator,
array $namespacePrefixesWithExcludedClasses = []
) {
$this->classManipulator = $classManipulator;
$this->namespacePrefixesWithExcludedClasses = $namespacePrefixesWithExcludedClasses;
$this->docBlockManipulator = $docBlockManipulator;
}

public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Replaces defined Pseudo_Namespaces by Namespace\Ones.', [
new ConfiguredCodeSample(
'$someService = new Some_Object;',
'$someService = new Some\Object;',
[
['Some_' => []],
]
),
new ConfiguredCodeSample(
<<<'CODE_SAMPLE'
/** @var Some_Object $someService */
$someService = new Some_Object;
$someClassToKeep = new Some_Class_To_Keep;
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
/** @var Some\Object $someService */
$someService = new Some\Object;
$someClassToKeep = new Some_Class_To_Keep;
CODE_SAMPLE
,
[
['Some_' => ['Some_Class_To_Keep']],
]
),
]);
}

/**
* @return string[]
*/
public function getNodeTypes(): array
{
// property, method
return [Name::class, Identifier::class, Property::class, FunctionLike::class, Expression::class];
}

/**
* @param Name|Identifier|Property|FunctionLike $node
*/
public function refactor(Node $node): ?Node
{
// replace on @var/@param/@return/@throws
foreach ($this->namespacePrefixesWithExcludedClasses as $namespacePrefix => $excludedClasses) {
$this->docBlockManipulator->changeUnderscoreType($node, $namespacePrefix, $excludedClasses);
}

if ($node instanceof Name || $node instanceof Identifier) {
return $this->processNameOrIdentifier($node);
}

return null;
}

/**
* @param Stmt[] $nodes
* @return Node[]
*/
public function afterTraverse(array $nodes): array
{
if ($this->newNamespace === null) {
return $nodes;
}

$namespaceNode = new Namespace_(new Name($this->newNamespace));
foreach ($nodes as $key => $node) {
if ($node instanceof Class_) {
$nodes = $this->classManipulator->insertBeforeAndFollowWithNewline($nodes, $namespaceNode, $key);

break;
}
}

$this->newNamespace = null;

return $nodes;
}

private function processName(Name $name): Name
{
$nodeName = $this->getName($name);

if ($nodeName !== null) {
$name->parts = explode('_', $nodeName);
}

return $name;
}

private function processIdentifier(Identifier $identifier): ?Identifier
{
$parentNode = $identifier->getAttribute(AttributeKey::PARENT_NODE);
if (! $parentNode instanceof Class_) {
return null;
}

$name = $this->getName($identifier);
if ($name === null) {
return null;
}

$newNameParts = explode('_', $name);
$lastNewNamePart = $newNameParts[count($newNameParts) - 1];

$namespaceParts = $newNameParts;
array_pop($namespaceParts);

$newNamespace = implode('\\', $namespaceParts);
if ($this->newNamespace !== null && $this->newNamespace !== $newNamespace) {
throw new ShouldNotHappenException('There cannot be 2 different namespaces in one file');
}

$this->newNamespace = $newNamespace;

$identifier->name = $lastNewNamePart;

return $identifier;
}

/**
* @param Name|Identifier $node
* @return Name|Identifier
*/
private function processNameOrIdentifier(Node $node): ?Node
{
// no name → skip
if ($node->toString() === '') {
return null;
}

foreach ($this->namespacePrefixesWithExcludedClasses as $namespacePrefix => $excludedClasses) {
if (! $this->isName($node, $namespacePrefix . '*')) {
continue;
}

if (is_array($excludedClasses) && $this->isNames($node, $excludedClasses)) {
return null;
}

if ($node instanceof Name) {
return $this->processName($node);
}

return $this->processIdentifier($node);
}

return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php declare(strict_types=1);

namespace Rector\CodeQuality\Tests\Rector\Array_\CallableThisArrayToAnonymousFunctionRector\Fixture;

final class ForeachToInArrayRector
{
public function refactor($node)
{
[, $comparedNode] = $node;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ private function resolveAlreadyImportedUses(Namespace_ $namespace): void
$class = $this->betterNodeFinder->findFirstInstanceOf($namespace->stmts, Class_::class);

// add class itself
if ($class) {
if ($class !== null) {
$className = $this->getName($class);
if ($className !== null) {
$this->importsInClassCollection->addImport($className);
Expand Down Expand Up @@ -334,7 +334,7 @@ private function resolveAlreadyUsedShortNames(Namespace_ $namespace): void
private function isCurrentNamespace(string $namespaceName, string $newUseStatement): bool
{
$afterCurrentNamespace = Strings::after($newUseStatement, $namespaceName . '\\');
if ($afterCurrentNamespace === false) {
if (! $afterCurrentNamespace) {
return false;
}

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

Rector\DeadCode\:
resource: '../src'
exclude: '../src/{Rector/**/*Rector.php,Data/*}'
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php declare(strict_types=1);

namespace Rector\DeadCode\Rector\ClassMethod\Data;
namespace Rector\DeadCode\Data;

use PhpParser\Node;
use PhpParser\Node\Expr\Variable;
Expand Down
30 changes: 30 additions & 0 deletions packages/DeadCode/src/FlowOfControlLocator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php declare(strict_types=1);

namespace Rector\DeadCode;

use PhpParser\Node;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\Expression;
use Rector\NodeTypeResolver\Node\AttributeKey;

final class FlowOfControlLocator
{
public function resolveNestingHashFromFunctionLike(FunctionLike $functionLike, Node $checkedNode): string
{
$nestingHash = '_';

$parentNode = $checkedNode;
while ($parentNode = $parentNode->getAttribute(AttributeKey::PARENT_NODE)) {
if ($parentNode instanceof Expression) {
continue;
}

$nestingHash .= spl_object_hash($parentNode);
if ($functionLike === $parentNode) {
return $nestingHash;
}
}

return $nestingHash;
}
}
Loading