Skip to content

Commit 54308f7

Browse files
authored
Add IssetToArrayKeyExistsFixer (#690)
1 parent f29f91b commit 54308f7

21 files changed

+236
-19
lines changed

.php-cs-fixer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
if ($fixer instanceof \PhpCsFixer\Fixer\DeprecatedFixerInterface) {
1919
continue;
2020
}
21-
if (!isset($rules[$fixer->getName()])) {
21+
if (!array_key_exists($fixer->getName(), $rules)) {
2222
$rules[$fixer->getName()] = true;
2323
}
2424
}

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# CHANGELOG for PHP CS Fixer: custom fixers
22

33
## v3.6.0
4+
- Add IssetToArrayKeyExistsFixer
45
- Add PhpdocVarAnnotationToAssertFixer
56

67
## v3.5.0

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![Latest stable version](https://img.shields.io/packagist/v/kubawerlos/php-cs-fixer-custom-fixers.svg?label=current%20version)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers)
44
[![PHP version](https://img.shields.io/packagist/php-v/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://php.net)
55
[![License](https://img.shields.io/github/license/kubawerlos/php-cs-fixer-custom-fixers.svg)](LICENSE)
6-
![Tests](https://img.shields.io/badge/tests-3167-brightgreen.svg)
6+
![Tests](https://img.shields.io/badge/tests-3199-brightgreen.svg)
77
[![Downloads](https://img.shields.io/packagist/dt/kubawerlos/php-cs-fixer-custom-fixers.svg)](https://packagist.org/packages/kubawerlos/php-cs-fixer-custom-fixers)
88

99
[![CI Status](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/workflows/CI/badge.svg?branch=main&event=push)](https://github.com/kubawerlos/php-cs-fixer-custom-fixers/actions)
@@ -135,6 +135,17 @@ Classes defined internally by extension or core must be referenced with the corr
135135
+$foo = new stdClass();
136136
```
137137

138+
#### IssetToArrayKeyExistsFixer
139+
Function `array_key_exists` must be used over `isset` when possible.
140+
*Risky: when array is not defined, is multi-dimensional or behaviour is relying on the null value.*
141+
```diff
142+
<?php
143+
-if (isset($array[$key])) {
144+
+if (array_key_exists($key, $array)) {
145+
echo $array[$key];
146+
}
147+
```
148+
138149
#### MultilineCommentOpeningClosingAloneFixer
139150
Multiline comments or PHPDocs must contain an opening and closing line with no additional content.
140151
```diff

src/Analyzer/Analysis/ConstructorAnalysis.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ private function getDuplicatesIndices(array $array): array
166166
$duplicates = [];
167167
$values = [];
168168
foreach ($array as $key => $value) {
169-
if (isset($values[$value])) {
169+
if (\array_key_exists($value, $values)) {
170170
$duplicates[$values[$value]] = $values[$value];
171171
$duplicates[$key] = $key;
172172
}

src/Analyzer/DataProviderAnalyzer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public function getDataProviders(Tokens $tokens, int $startIndex, int $endIndex)
5353
$dataProviderAnalyses = [];
5454
foreach ($dataProviders as $dataProviderName => $dataProviderUsages) {
5555
$lowercaseDataProviderName = \strtolower($dataProviderName);
56-
if (!isset($methods[$lowercaseDataProviderName])) {
56+
if (!\array_key_exists($lowercaseDataProviderName, $methods)) {
5757
continue;
5858
}
5959
$dataProviderAnalyses[$methods[$lowercaseDataProviderName]] = new DataProviderAnalysis(

src/Analyzer/FunctionAnalyzer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public static function getFunctionArguments(Tokens $tokens, int $index): array
6767
*/
6868
private static function getArgumentsRange(Tokens $tokens, int $index): ?array
6969
{
70-
if (!$tokens[$index]->isGivenKind(\T_STRING)) {
70+
if (!$tokens[$index]->isGivenKind([\T_ISSET, \T_STRING])) {
7171
throw new \InvalidArgumentException(\sprintf('Index %d is not a function.', $index));
7272
}
7373

src/Fixer/CommentedOutFunctionFixer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public function getConfigurationDefinition(): FixerConfigurationResolverInterfac
5656
*/
5757
public function configure(?array $configuration = null): void
5858
{
59-
if (isset($configuration['functions'])) {
59+
if (\is_array($configuration) && \array_key_exists('functions', $configuration)) {
6060
$elements = $configuration['functions'];
6161
$this->functions = $elements;
6262
}

src/Fixer/DataProviderNameFixer.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,11 @@ public function configure(?array $configuration = null): void
7676
/** @var array<string> $configuration */
7777
$configuration = $configuration ?? [];
7878

79-
if (isset($configuration['prefix'])) {
79+
if (\array_key_exists('prefix', $configuration)) {
8080
$this->prefix = $configuration['prefix'];
8181
}
8282

83-
if (isset($configuration['suffix'])) {
83+
if (\array_key_exists('suffix', $configuration)) {
8484
$this->suffix = $configuration['suffix'];
8585
}
8686
}

src/Fixer/InternalClassCasingFixer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ private function getCorrectCase(string $className): string
120120

121121
$lowercaseClassName = \strtolower($className);
122122

123-
if (!isset($classes[$lowercaseClassName])) {
123+
if (!\array_key_exists($lowercaseClassName, $classes)) {
124124
return $className;
125125
}
126126

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<?php declare(strict_types=1);
2+
3+
/*
4+
* This file is part of PHP CS Fixer: custom fixers.
5+
*
6+
* (c) 2018 Kuba Werłos
7+
*
8+
* For the full copyright and license information, please view
9+
* the LICENSE file that was distributed with this source code.
10+
*/
11+
12+
namespace PhpCsFixerCustomFixers\Fixer;
13+
14+
use PhpCsFixer\FixerDefinition\CodeSample;
15+
use PhpCsFixer\FixerDefinition\FixerDefinition;
16+
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
17+
use PhpCsFixer\Tokenizer\Token;
18+
use PhpCsFixer\Tokenizer\Tokens;
19+
use PhpCsFixerCustomFixers\Analyzer\FunctionAnalyzer;
20+
21+
final class IssetToArrayKeyExistsFixer extends AbstractFixer
22+
{
23+
public function getDefinition(): FixerDefinitionInterface
24+
{
25+
return new FixerDefinition(
26+
'Function `array_key_exists` must be used over `isset` when possible.',
27+
[
28+
new CodeSample(
29+
'<?php
30+
if (isset($array[$key])) {
31+
echo $array[$key];
32+
}
33+
'
34+
),
35+
],
36+
null,
37+
'when array is not defined, is multi-dimensional or behaviour is relying on the null value'
38+
);
39+
}
40+
41+
/**
42+
* Must run before NativeFunctionInvocationFixer.
43+
*/
44+
public function getPriority(): int
45+
{
46+
return 2;
47+
}
48+
49+
public function isCandidate(Tokens $tokens): bool
50+
{
51+
return $tokens->isTokenKindFound(\T_ISSET);
52+
}
53+
54+
public function isRisky(): bool
55+
{
56+
return true;
57+
}
58+
59+
public function fix(\SplFileInfo $file, Tokens $tokens): void
60+
{
61+
for ($index = $tokens->count() - 1; $index > 0; $index--) {
62+
if (!$tokens[$index]->isGivenKind(\T_ISSET)) {
63+
continue;
64+
}
65+
66+
if (\count(FunctionAnalyzer::getFunctionArguments($tokens, $index)) !== 1) {
67+
continue;
68+
}
69+
70+
$openParenthesis = $tokens->getNextMeaningfulToken($index);
71+
\assert(\is_int($openParenthesis));
72+
73+
$closeParenthesis = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesis);
74+
75+
$closeBrackets = $tokens->getPrevMeaningfulToken($closeParenthesis);
76+
\assert(\is_int($closeBrackets));
77+
if (!$tokens[$closeBrackets]->equals(']')) {
78+
continue;
79+
}
80+
81+
$openBrackets = $tokens->findBlockStart(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $closeBrackets);
82+
83+
$keyStartIndex = $tokens->getNextMeaningfulToken($openBrackets);
84+
\assert(\is_int($keyStartIndex));
85+
$keyEndIndex = $tokens->getPrevMeaningfulToken($closeBrackets);
86+
87+
$keyTokens = [];
88+
for ($i = $keyStartIndex; $i <= $keyEndIndex; $i++) {
89+
if ($tokens[$i]->equals('')) {
90+
continue;
91+
}
92+
$keyTokens[] = $tokens[$i];
93+
}
94+
$keyTokens[] = new Token(',');
95+
$keyTokens[] = new Token([\T_WHITESPACE, ' ']);
96+
97+
$tokens->clearRange($openBrackets, $closeBrackets);
98+
$tokens->insertAt($openParenthesis + 1, $keyTokens);
99+
$tokens[$index] = new Token([\T_STRING, 'array_key_exists']);
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)