Skip to content

Commit a48958f

Browse files
committed
Add new sniff to ban the use of compact() function
1 parent ed8e00d commit a48958f

6 files changed

+313
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
3+
/**
4+
* Bans the use of compact() function
5+
*
6+
* @author Paweł Bogut <[email protected]>
7+
* @copyright 2006-2023 Squiz Pty Ltd (ABN 77 084 670 600)
8+
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
9+
*/
10+
11+
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Arrays;
12+
13+
use PHP_CodeSniffer\Files\File;
14+
use PHP_CodeSniffer\Sniffs\Sniff;
15+
use PHP_CodeSniffer\Util\Tokens;
16+
17+
class DisallowCompactArrayBuilderSniff implements Sniff
18+
{
19+
20+
21+
/**
22+
* Registers the tokens that this sniff wants to listen for.
23+
*
24+
* @return int[]
25+
*/
26+
public function register()
27+
{
28+
return [T_STRING];
29+
30+
}//end register()
31+
32+
33+
/**
34+
* Processes this test, when one of its tokens is encountered.
35+
*
36+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
37+
* @param int $stackPtr The position of the current token
38+
* in the stack passed in $tokens.
39+
*
40+
* @return void
41+
*/
42+
public function process(File $phpcsFile, $stackPtr)
43+
{
44+
$tokens = $phpcsFile->getTokens();
45+
46+
$content = $tokens[$stackPtr]['content'];
47+
48+
if ($content !== 'compact') {
49+
return;
50+
}
51+
52+
// Make sure this is a function call.
53+
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
54+
if ($next === false || $tokens[$next]['code'] !== T_OPEN_PARENTHESIS) {
55+
// Not a function call.
56+
return;
57+
}
58+
59+
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
60+
61+
// Make sure its not method of an object or class.
62+
if ($tokens[$prev]['code'] === T_OBJECT_OPERATOR || $tokens[$prev]['code'] === T_DOUBLE_COLON) {
63+
// Not a builtin function call.
64+
return;
65+
}
66+
67+
$error = 'Array must not be created with compact() function';
68+
69+
$fix = true;
70+
$toExpand = [];
71+
$openPtr = $next;
72+
$closePtr = null;
73+
$validVarNameRegExp = '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/';
74+
// Find all params in compact() function call, and check if it is fixable.
75+
while (($next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true)) !== false) {
76+
if ($tokens[$next]['code'] === T_CONSTANT_ENCAPSED_STRING) {
77+
$variableName = substr($tokens[$next]['content'], 1, -1);
78+
$isValid = preg_match($validVarNameRegExp, $variableName);
79+
80+
if ($isValid === false || $isValid === 0) {
81+
$fix = false;
82+
break;
83+
}
84+
85+
$toExpand[] = $next;
86+
continue;
87+
}
88+
89+
if ($tokens[$next]['code'] === T_CLOSE_PARENTHESIS) {
90+
$closePtr = $next;
91+
break;
92+
}
93+
94+
if ($tokens[$next]['code'] !== T_COMMA) {
95+
$fix = false;
96+
}
97+
}//end while
98+
99+
if ($closePtr === false) {
100+
$fix = false;
101+
}
102+
103+
if ($fix === false) {
104+
$phpcsFile->addError($error, $stackPtr, 'Found');
105+
return;
106+
}
107+
108+
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
109+
110+
if ($fix === true) {
111+
$phpcsFile->fixer->beginChangeset();
112+
113+
$phpcsFile->fixer->replaceToken($stackPtr, '');
114+
$phpcsFile->fixer->replaceToken($openPtr, '[');
115+
$phpcsFile->fixer->replaceToken($closePtr, ']');
116+
117+
foreach ($toExpand as $ptr) {
118+
$variableName = substr($tokens[$ptr]['content'], 1, -1);
119+
$phpcsFile->fixer->replaceToken(
120+
$ptr,
121+
$tokens[$ptr]['content'].' => $'.$variableName
122+
);
123+
}
124+
125+
$phpcsFile->fixer->endChangeset();
126+
}//end if
127+
128+
}//end process()
129+
130+
131+
}//end class
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
$var = compact();
3+
$var = array(1,2,3);
4+
$var = compact('a','b','c');
5+
echo $var[1];
6+
$foo = compact($var[1],$var[2]);
7+
$foo = compact(
8+
'a',
9+
"b",
10+
'c'
11+
);
12+
$var = compact/*comment*/('a', 'b', "c");
13+
14+
$var = compact(['aa', 'bb' => 'cc']);
15+
16+
$var = compact(array('aa', 'bb' => 'cc'));
17+
18+
$ver = $foo->compact('a', 'b');
19+
20+
function foo($compact) {}
21+
22+
$compact = function ($a, $b, $c) use ($foo): array {};
23+
$compact('a', 'b', 'c');
24+
25+
view('some.view', compact("a", 'b', 'c'));
26+
view('some.view', compact(
27+
'a',
28+
'b',
29+
'c'
30+
));
31+
32+
$var = compact('aa', 'invalid-var.name');
33+
$var = Bazz::compact('a', 'b');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
$var = [];
3+
$var = array(1,2,3);
4+
$var = ['a' => $a,'b' => $b,'c' => $c];
5+
echo $var[1];
6+
$foo = compact($var[1],$var[2]);
7+
$foo = [
8+
'a' => $a,
9+
"b" => $b,
10+
'c' => $c
11+
];
12+
$var = /*comment*/['a' => $a, 'b' => $b, "c" => $c];
13+
14+
$var = compact(['aa', 'bb' => 'cc']);
15+
16+
$var = compact(array('aa', 'bb' => 'cc'));
17+
18+
$ver = $foo->compact('a', 'b');
19+
20+
function foo($compact) {}
21+
22+
$compact = function ($a, $b, $c) use ($foo): array {};
23+
$compact('a', 'b', 'c');
24+
25+
view('some.view', ["a" => $a, 'b' => $b, 'c' => $c]);
26+
view('some.view', [
27+
'a' => $a,
28+
'b' => $b,
29+
'c' => $c
30+
]);
31+
32+
$var = compact('aa', 'invalid-var.name');
33+
$var = Bazz::compact('a', 'b');
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
$var = compact('s', 'b');
3+
4+
// The following function has a simulated git conflict for testing.
5+
// This is not a merge conflict - it is a valid test case to test handling of
6+
// compact function call without associated closer.
7+
// Please do not remove.
8+
function test()
9+
{
10+
$a = 'a';
11+
$b = 'b';
12+
13+
$arr = compact(
14+
'a',
15+
<<<<<<< HEAD
16+
'b'
17+
=======
18+
'c'
19+
>>>>>>> master
20+
);
21+
}
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
$var = ['s' => $s, 'b' => $b];
3+
4+
// The following function has a simulated git conflict for testing.
5+
// This is not a merge conflict - it is a valid test case to test handling of
6+
// compact function call without associated closer.
7+
// Please do not remove.
8+
function test()
9+
{
10+
$a = 'a';
11+
$b = 'b';
12+
13+
$arr = compact(
14+
'a',
15+
<<<<<<< HEAD
16+
'b'
17+
=======
18+
'c'
19+
>>>>>>> master
20+
);
21+
}
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
/**
4+
* Unit test class for the DisallowCompactArrayBuilder sniff.
5+
*
6+
* @author Paweł Bogut <[email protected]>
7+
* @copyright 2006-2023 Squiz Pty Ltd (ABN 77 084 670 600)
8+
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
9+
*/
10+
11+
namespace PHP_CodeSniffer\Standards\Generic\Tests\Arrays;
12+
13+
use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
14+
15+
class DisallowCompactArrayBuilderUnitTest extends AbstractSniffUnitTest
16+
{
17+
18+
19+
/**
20+
* Returns the lines where errors should occur.
21+
*
22+
* The key of the array should represent the line number and the value
23+
* should represent the number of errors that should occur on that line.
24+
*
25+
* @param string $testFile The name of the file being tested.
26+
*
27+
* @return array<int, int>
28+
*/
29+
public function getErrorList($testFile='')
30+
{
31+
switch ($testFile) {
32+
case 'DisallowCompactArrayBuilderUnitTest.1.inc':
33+
return [
34+
2 => 1,
35+
4 => 1,
36+
6 => 1,
37+
7 => 1,
38+
12 => 1,
39+
14 => 1,
40+
16 => 1,
41+
25 => 1,
42+
26 => 1,
43+
32 => 1,
44+
];
45+
case 'DisallowCompactArrayBuilderUnitTest.2.inc':
46+
return [
47+
2 => 1,
48+
13 => 1,
49+
];
50+
default:
51+
return [];
52+
}//end switch
53+
54+
}//end getErrorList()
55+
56+
57+
/**
58+
* Returns the lines where warnings should occur.
59+
*
60+
* The key of the array should represent the line number and the value
61+
* should represent the number of warnings that should occur on that line.
62+
*
63+
* @return array<int, int>
64+
*/
65+
public function getWarningList()
66+
{
67+
return [];
68+
69+
}//end getWarningList()
70+
71+
72+
}//end class

0 commit comments

Comments
 (0)