-
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from PHPCSStandards/feature/new-arrays-mixedar…
…raykeytypes-sniff New `Universal.Arrays.MixedArrayKeyTypes` sniff
- Loading branch information
Showing
4 changed files
with
303 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<documentation title="Mixed Array Key Types"> | ||
<standard> | ||
<![CDATA[ | ||
In an array where the items have keys, all items should either have a numeric key assigned or a string key. A mix of numeric and string keys is not allowed. | ||
]]> | ||
</standard> | ||
<code_comparison> | ||
<code title="Valid: Arrays with either numeric keys or string keys."> | ||
<![CDATA[ | ||
$args = array( | ||
<em>'foo'</em> => 22, | ||
<em>'bar'</em> => 25, | ||
); | ||
$args = array( | ||
<em>0</em> => 22, | ||
<em>1</em> => 25, | ||
); | ||
]]> | ||
</code> | ||
<code title="Invalid: Arrays with a mix of numeric and string keys."> | ||
<![CDATA[ | ||
$args = array( | ||
'foo' => 22, | ||
25, | ||
); | ||
$args = array( | ||
'foo' => 22, | ||
12 => 25, | ||
); | ||
]]> | ||
</code> | ||
</code_comparison> | ||
</documentation> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
<?php | ||
/** | ||
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer. | ||
* | ||
* @package PHPCSExtra | ||
* @copyright 2020 PHPCSExtra Contributors | ||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3 | ||
* @link https://github.com/PHPCSStandards/PHPCSExtra | ||
*/ | ||
|
||
namespace PHPCSExtra\Universal\Sniffs\Arrays; | ||
|
||
use PHP_CodeSniffer\Files\File; | ||
use PHP_CodeSniffer\Util\Tokens; | ||
use PHPCSUtils\AbstractSniffs\AbstractArrayDeclarationSniff; | ||
|
||
/** | ||
* Forbid arrays which contain both array items with numeric keys as well as array items with string keys. | ||
* | ||
* @since 1.0.0 | ||
*/ | ||
class MixedArrayKeyTypesSniff extends AbstractArrayDeclarationSniff | ||
{ | ||
|
||
/** | ||
* Whether a string key was encountered. | ||
* | ||
* @var bool | ||
*/ | ||
private $seenStringKey = false; | ||
|
||
/** | ||
* Whether a numeric key was encountered. | ||
* | ||
* @var bool | ||
*/ | ||
private $seenNumericKey = false; | ||
|
||
/** | ||
* Process the array declaration. | ||
* | ||
* @since 1.0.0 | ||
* | ||
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the | ||
* token was found. | ||
* | ||
* @return void | ||
*/ | ||
public function processArray(File $phpcsFile) | ||
{ | ||
// Reset properties before processing this array. | ||
$this->seenStringKey = false; | ||
$this->seenNumericKey = false; | ||
|
||
parent::processArray($phpcsFile); | ||
} | ||
|
||
/** | ||
* Process the tokens in an array key. | ||
* | ||
* @since 1.0.0 | ||
* | ||
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the | ||
* token was found. | ||
* @param int $startPtr The stack pointer to the first token in the "key" part of | ||
* an array item. | ||
* @param int $endPtr The stack pointer to the last token in the "key" part of | ||
* an array item. | ||
* @param int $itemNr Which item in the array is being handled. | ||
* | ||
* @return void | ||
*/ | ||
public function processKey(File $phpcsFile, $startPtr, $endPtr, $itemNr) | ||
{ | ||
$key = $this->getActualArrayKey($phpcsFile, $startPtr, $endPtr); | ||
if (isset($key) === false) { | ||
// Key could not be determined. | ||
return; | ||
} | ||
|
||
$integerKey = \is_int($key); | ||
|
||
// Handle integer key. | ||
if ($integerKey === true) { | ||
if ($this->seenStringKey === false) { | ||
if ($this->seenNumericKey !== false) { | ||
// Already seen a numeric key before. | ||
return; | ||
} | ||
|
||
$this->seenNumericKey = true; | ||
return; | ||
} | ||
|
||
// Ok, so we've seen a string key before and now see an explicit numeric key. | ||
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true); | ||
$phpcsFile->addError( | ||
'Arrays should have either numeric keys or string keys. Explicit numeric key detected,' | ||
. ' while all previous keys in this array were string keys.', | ||
$firstNonEmpty, | ||
'ExplicitNumericKey' | ||
); | ||
|
||
// Stop the loop. | ||
return true; | ||
} | ||
|
||
// Handle string key. | ||
if ($this->seenNumericKey === false) { | ||
if ($this->seenStringKey !== false) { | ||
// Already seen a string key before. | ||
return; | ||
} | ||
|
||
$this->seenStringKey = true; | ||
return; | ||
} | ||
|
||
// Ok, so we've seen a numeric key before and now see a string key. | ||
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true); | ||
$phpcsFile->addError( | ||
'Arrays should have either numeric keys or string keys. String key detected,' | ||
. ' while all previous keys in this array were integer based keys.', | ||
$firstNonEmpty, | ||
'StringKey' | ||
); | ||
|
||
// Stop the loop. | ||
return true; | ||
} | ||
|
||
/** | ||
* Process an array item without an array key. | ||
* | ||
* @since 1.0.0 | ||
* | ||
* @param \PHP_CodeSniffer\Files\File $phpcsFile The PHP_CodeSniffer file where the | ||
* token was found. | ||
* @param int $startPtr The stack pointer to the first token in the array item, | ||
* which in this case will be the first token of the array | ||
* value part of the array item. | ||
* @param int $itemNr Which item in the array is being handled. | ||
* | ||
* @return void | ||
*/ | ||
public function processNoKey(File $phpcsFile, $startPtr, $itemNr) | ||
{ | ||
if ($this->seenStringKey === false) { | ||
if ($this->seenNumericKey !== false) { | ||
// Already seen a numeric key before. | ||
return; | ||
} | ||
|
||
$this->seenNumericKey = true; | ||
return; | ||
} | ||
|
||
// Ok, so we've seen a string key before and now see an implicit numeric key. | ||
$firstNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, $startPtr, null, true); | ||
$phpcsFile->addError( | ||
'Arrays should have either numeric keys or string keys. Implicit numeric key detected,' | ||
. ' while all previous keys in this array were string keys.', | ||
$firstNonEmpty, | ||
'ImplicitNumericKey' | ||
); | ||
|
||
// Stop the loop. | ||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
// OK: Array without keys. | ||
$array = [1, 2]; | ||
|
||
// OK: All items have numeric keys. | ||
$array = array( | ||
1 => 'a', | ||
2 => 'b', | ||
3 => 'c', | ||
4 => 'd', | ||
); | ||
|
||
// OK: All items have numeric keys. | ||
$array = array( | ||
'a', | ||
2 => 'b', | ||
'3' => 'c', | ||
4 => 'd', | ||
); | ||
|
||
// OK: All items have string keys. | ||
$array = array( | ||
'a' => 'a', | ||
'b' => 'b', | ||
'c' => 'c', | ||
'd' => 'd', | ||
); | ||
|
||
// Mixed numeric first. | ||
$array = [ | ||
12 => 'numeric key', | ||
'value', | ||
'string' => 'string key', // Error. | ||
]; | ||
|
||
// Mixed string first. | ||
$array = [ | ||
'stringA' => 'string key', | ||
'stringB' => 'string key', | ||
12 => 'numeric key', // Error. | ||
]; | ||
|
||
// Mixed string first, implicit numeric. | ||
$array = [ | ||
'stringA' => 'string key', | ||
'numeric key', // Error. | ||
'stringB' => 'string key', | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
<?php | ||
/** | ||
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer. | ||
* | ||
* @package PHPCSExtra | ||
* @copyright 2020 PHPCSExtra Contributors | ||
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3 | ||
* @link https://github.com/PHPCSStandards/PHPCSExtra | ||
*/ | ||
|
||
namespace PHPCSExtra\Universal\Tests\Arrays; | ||
|
||
use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest; | ||
|
||
/** | ||
* Unit test class for the MixedArrayKeyTypes sniff. | ||
* | ||
* @covers PHPCSExtra\Universal\Sniffs\Arrays\MixedArrayKeyTypesSniff | ||
* | ||
* @since 1.0.0 | ||
*/ | ||
class MixedArrayKeyTypesUnitTest extends AbstractSniffUnitTest | ||
{ | ||
|
||
/** | ||
* Returns the lines where errors should occur. | ||
* | ||
* @return array <int line number> => <int number of errors> | ||
*/ | ||
public function getErrorList() | ||
{ | ||
return [ | ||
34 => 1, | ||
41 => 1, | ||
47 => 1, | ||
]; | ||
} | ||
|
||
/** | ||
* Returns the lines where warnings should occur. | ||
* | ||
* @return array <int line number> => <int number of warnings> | ||
*/ | ||
public function getWarningList() | ||
{ | ||
return []; | ||
} | ||
} |