Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add constant FilesystemIterator::SKIP_DOTS when flags parameter is used #3215

Merged
merged 11 commits into from
Jan 18, 2023
15 changes: 14 additions & 1 deletion build/target-repository/docs/rector_rules_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@

- [Php81](#php81) (12)

- [Php82](#php82) (2)
- [Php82](#php82) (3)

- [Privatization](#privatization) (8)

Expand Down Expand Up @@ -6570,6 +6570,19 @@ Refactor Spatie enum method calls

## Php82

### FilesystemIteratorSkipDotsRector

Prior PHP 8.2 FilesystemIterator::SKIP_DOTS was always set and could not be removed, therefore FilesystemIterator::SKIP_DOTS is added in order to keep this behaviour.

- class: [`Rector\Php82\Rector\New_\FilesystemIteratorSkipDotsRector`](../rules/Php82/Rector/New_/FilesystemIteratorSkipDotsRector.php)

```diff
-new \FilesystemIterator(__DIR__, \FilesystemIterator::KEY_AS_FILENAME);
+new \FilesystemIterator(__DIR__, \FilesystemIterator::KEY_AS_FILENAME | \FilesystemIterator::SKIP_DOTS);
```

<br>
jawira marked this conversation as resolved.
Show resolved Hide resolved

### ReadOnlyClassRector

Decorate read-only class with `readonly` attribute
Expand Down
7 changes: 6 additions & 1 deletion config/set/php82.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
use Rector\Config\RectorConfig;
use Rector\Php82\Rector\Class_\ReadOnlyClassRector;
use Rector\Php82\Rector\FuncCall\Utf8DecodeEncodeToMbConvertEncodingRector;
use Rector\Php82\Rector\New_\FilesystemIteratorSkipDotsRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([ReadOnlyClassRector::class, Utf8DecodeEncodeToMbConvertEncodingRector::class]);
$rectorConfig->rules([
ReadOnlyClassRector::class,
Utf8DecodeEncodeToMbConvertEncodingRector::class,
FilesystemIteratorSkipDotsRector::class,
]);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots;

use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class FilesystemIteratorSkipDotsRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

/**
* @return Iterator<array<string>>
*/
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots\Fixture;

use FilesystemIterator;

final class AppendConstantToFlag
{
public function getIterator(): FilesystemIterator
{
return new FilesystemIterator(__DIR__, FilesystemIterator::KEY_AS_FILENAME);
}
}

?>
-----
<?php

namespace Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots\Fixture;

use FilesystemIterator;

final class AppendConstantToFlag
{
public function getIterator(): FilesystemIterator
{
return new FilesystemIterator(__DIR__, FilesystemIterator::KEY_AS_FILENAME | \FilesystemIterator::SKIP_DOTS);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots\Fixture;

use FilesystemIterator;

final class AppendConstantToMultipleFlags
{
public function getIterator(): FilesystemIterator
{
return new FilesystemIterator(__DIR__, FilesystemIterator::KEY_AS_FILENAME | FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::CURRENT_MODE_MASK);
}
}

?>
-----
<?php

namespace Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots\Fixture;

use FilesystemIterator;

final class AppendConstantToMultipleFlags
{
public function getIterator(): FilesystemIterator
{
return new FilesystemIterator(__DIR__, FilesystemIterator::KEY_AS_FILENAME | FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::CURRENT_MODE_MASK | \FilesystemIterator::SKIP_DOTS);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots\Fixture;

final class AppendConstantToMultipleFlagsWithFqn
{
public function getIterator(): \FilesystemIterator
{
return new \FilesystemIterator(__DIR__, \FilesystemIterator::KEY_AS_FILENAME | \FilesystemIterator::FOLLOW_SYMLINKS | \FilesystemIterator::CURRENT_MODE_MASK);
}
}

?>
-----
<?php

namespace Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots\Fixture;

final class AppendConstantToMultipleFlagsWithFqn
{
public function getIterator(): \FilesystemIterator
{
return new \FilesystemIterator(__DIR__, \FilesystemIterator::KEY_AS_FILENAME | \FilesystemIterator::FOLLOW_SYMLINKS | \FilesystemIterator::CURRENT_MODE_MASK | \FilesystemIterator::SKIP_DOTS);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
samsonasik marked this conversation as resolved.
Show resolved Hide resolved

namespace Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots\Fixture;

use FilesystemIterator;

final class ConstantPresent
samsonasik marked this conversation as resolved.
Show resolved Hide resolved
{
public function getIterator(): FilesystemIterator
{
return new FilesystemIterator(__DIR__, FilesystemIterator::SKIP_DOTS);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
samsonasik marked this conversation as resolved.
Show resolved Hide resolved

namespace Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots\Fixture;

use FilesystemIterator;

final class ConstantPresentInFlags
samsonasik marked this conversation as resolved.
Show resolved Hide resolved
{
public function getIterator(): FilesystemIterator
{
return new FilesystemIterator(__DIR__, FilesystemIterator::FOLLOW_SYMLINKS | FilesystemIterator::SKIP_DOTS | FilesystemIterator::KEY_AS_FILENAME);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
samsonasik marked this conversation as resolved.
Show resolved Hide resolved

namespace Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots\Fixture;

final class ConstantPresentWithFqn
samsonasik marked this conversation as resolved.
Show resolved Hide resolved
{
public function getIterator(): \FilesystemIterator
{
return new \FilesystemIterator(__DIR__, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::KEY_AS_FILENAME);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
samsonasik marked this conversation as resolved.
Show resolved Hide resolved

namespace Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots\Fixture;

use FilesystemIterator;

final class MaintainDefaultValue
samsonasik marked this conversation as resolved.
Show resolved Hide resolved
{
public function getIterator(): FilesystemIterator
{
return new FilesystemIterator(__DIR__);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Php82\Rector\New_\FilesystemIteratorSkipDotsRector;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(FilesystemIteratorSkipDotsRector::class);
};
95 changes: 95 additions & 0 deletions rules/Php82/Rector/New_/FilesystemIteratorSkipDotsRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

declare(strict_types=1);

namespace Rector\Php82\Rector\New_;

use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\BitwiseOr;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name\FullyQualified;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \Rector\Tests\Php82\Rector\New_\FilesystemIteratorSkipDots\FilesystemIteratorSkipDotsRectorTest
*/
class FilesystemIteratorSkipDotsRector extends AbstractRector implements MinPhpVersionInterface
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Prior PHP 8.2 FilesystemIterator::SKIP_DOTS was always set and could not be removed, therefore FilesystemIterator::SKIP_DOTS is added in order to keep this behaviour.',
[new CodeSample(
'new \\FilesystemIterator(__DIR__, \\FilesystemIterator::KEY_AS_FILENAME);',
'new \\FilesystemIterator(__DIR__, \\FilesystemIterator::KEY_AS_FILENAME | \\FilesystemIterator::SKIP_DOTS);'
),
]
);
}

public function getNodeTypes(): array
{
return [New_::class];
}

/**
* Add {@see \FilesystemIterator::SKIP_DOTS} to $node when required.
*
* @param New_ $node
*/
public function refactor(Node $node): ?New_
jawira marked this conversation as resolved.
Show resolved Hide resolved
{
if ($node->isFirstClassCallable()) {
return null;
}
if (!array_key_exists(1, $node->args)) {
return null;
}
$flags = $node->args[1]->value;
if ($this->isSkipDotsPresent($flags)) {
return null;
}
$skipDots = new ClassConstFetch(new FullyQualified('FilesystemIterator'), 'SKIP_DOTS');
$node->args[1] = new Arg(new BitwiseOr($flags, $skipDots));
return $node;
}

public function provideMinPhpVersion(): int
{
return PhpVersionFeature::FILESYSTEM_ITERATOR_SKIP_DOTS;
}

/**
* Is the constant {@see \FilesystemIterator::SKIP_DOTS} present within $node?
*/
private function isSkipDotsPresent(Expr $node): bool
jawira marked this conversation as resolved.
Show resolved Hide resolved
{
if ($this->isSkipDots($node)) {
return true;
}

while ($node instanceof BitwiseOr) {
if ($this->isSkipDots($node->right)) {
return true;
}
$node = $node->left;
}

return false;
}

/**
* Tells if $expr is equal to {@see \FilesystemIterator::SKIP_DOTS}.
*/
protected function isSkipDots(Expr $expr): bool
{
return ($expr instanceof ClassConstFetch) && (strval($expr->name) === 'SKIP_DOTS');
samsonasik marked this conversation as resolved.
Show resolved Hide resolved
}
}
6 changes: 2 additions & 4 deletions src/Application/ApplicationFileProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,8 @@ public function processFiles(array $files, Configuration $configuration): array
*/
public function configurePHPStanNodeScopeResolver(array $filePaths): void
{
$phpFilePaths = array_filter(
$filePaths,
static fn (string $filePath): bool => str_ends_with($filePath, '.php')
);
$phpFilter = static fn (string $filePath): bool => str_ends_with($filePath, '.php');
$phpFilePaths = array_filter($filePaths, $phpFilter);
$this->nodeScopeResolver->setAnalysedFiles($phpFilePaths);
}

Expand Down
6 changes: 6 additions & 0 deletions src/ValueObject/PhpVersionFeature.php
Original file line number Diff line number Diff line change
Expand Up @@ -607,4 +607,10 @@ final class PhpVersionFeature
* @var int
*/
public const DEPRECATE_UTF8_DECODE_ENCODE_FUNCTION = PhpVersion::PHP_82;

/**
* @see https://www.php.net/manual/en/filesystemiterator.construct
* @var int
*/
public const FILESYSTEM_ITERATOR_SKIP_DOTS = PhpVersion::PHP_82;
}