Skip to content

Commit ce42676

Browse files
authored
Update for phpcs 4.0.0 (#356)
* Update linting config * Update github actions config * Update sniff to handle T_NAME... tokens for functions * Update composer versions * Bump min phpcs version to 3.5.7 * Remove defined guards for T_NAME... constants * Update github actions to raise min phpcs version to 3.5.7 * Remove unused isTokenInsideArrowFunctionDefinition
1 parent fb1275c commit ce42676

File tree

8 files changed

+205
-96
lines changed

8 files changed

+205
-96
lines changed

.github/workflows/csqa.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ on:
55
# Prevent the build from running when there are only irrelevant changes.
66
push:
77
paths-ignore:
8-
- '**.md'
8+
- "**.md"
99
pull_request:
1010
# Allow manually triggering the workflow.
1111
workflow_dispatch:
@@ -18,11 +18,11 @@ concurrency:
1818

1919
jobs:
2020
checkcs:
21-
name: 'Basic CS and QA checks'
21+
name: "Basic CS and QA checks"
2222
runs-on: ubuntu-latest
2323

2424
env:
25-
XMLLINT_INDENT: ' '
25+
XMLLINT_INDENT: " "
2626

2727
steps:
2828
- name: Checkout code
@@ -31,13 +31,13 @@ jobs:
3131
- name: Install PHP
3232
uses: shivammathur/setup-php@v2
3333
with:
34-
php-version: 'latest'
34+
php-version: "latest"
3535
coverage: none
3636
tools: cs2pr
3737

3838
# Using PHPCS `master` as an early detection system for bugs upstream.
39-
- name: 'Composer: adjust dependencies'
40-
run: composer require --no-update squizlabs/php_codesniffer:"dev-master"
39+
- name: "Composer: adjust dependencies"
40+
run: composer require --no-update squizlabs/php_codesniffer:"4.x-dev"
4141

4242
# Install dependencies and handle caching in one go.
4343
# @link https://github.com/marketplace/actions/install-php-dependencies-with-composer

.github/workflows/test.yml

Lines changed: 62 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -34,40 +34,60 @@ jobs:
3434
# - PHP 8.4 needs PHPCS 3.11.0+ to run without errors (though the errors don't affect this package).
3535
#
3636
# The matrix is set up so as not to duplicate the builds which are run for code coverage.
37-
php: ['5.5', '5.6', '7.0', '7.1', '7.2', '7.3']
38-
phpcs_version: ['3.5.6', 'dev-master']
37+
php: ["5.5", "5.6", "7.0", "7.1", "7.2", "7.3"]
38+
phpcs_version: ["3.5.7", "4.0.0", "4.x-dev"]
39+
40+
exclude:
41+
# PHPCS 4.x requires PHP 7.2+
42+
- php: "5.5"
43+
phpcs_version: "4.0.0"
44+
- php: "5.5"
45+
phpcs_version: "4.x-dev"
46+
- php: "5.6"
47+
phpcs_version: "4.0.0"
48+
- php: "5.6"
49+
phpcs_version: "4.x-dev"
50+
- php: "7.0"
51+
phpcs_version: "4.0.0"
52+
- php: "7.0"
53+
phpcs_version: "4.x-dev"
54+
- php: "7.1"
55+
phpcs_version: "4.0.0"
56+
- php: "7.1"
57+
phpcs_version: "4.x-dev"
3958

4059
include:
41-
# Make the matrix complete without duplicating builds run in code coverage.
42-
- php: '8.4'
43-
phpcs_version: '3.6.1'
44-
45-
- php: '8.3'
46-
phpcs_version: 'dev-master'
47-
- php: '8.3'
48-
phpcs_version: '3.6.1'
49-
50-
- php: '8.2'
51-
phpcs_version: 'dev-master'
52-
- php: '8.2'
53-
phpcs_version: '3.6.1'
54-
55-
- php: '8.1'
56-
phpcs_version: 'dev-master'
57-
- php: '8.1'
58-
phpcs_version: '3.6.1'
59-
60-
- php: '8.0'
61-
phpcs_version: 'dev-master'
62-
- php: '8.0'
63-
phpcs_version: '3.5.7'
64-
65-
- php: '7.4'
66-
phpcs_version: 'dev-master'
60+
- php: "8.4"
61+
phpcs_version: "4.0.0"
62+
- php: "8.4"
63+
phpcs_version: "3.6.1"
64+
65+
- php: "8.3"
66+
phpcs_version: "4.x-dev"
67+
- php: "8.3"
68+
phpcs_version: "3.6.1"
69+
70+
- php: "8.2"
71+
phpcs_version: "4.x-dev"
72+
- php: "8.2"
73+
phpcs_version: "3.6.1"
74+
75+
- php: "8.1"
76+
phpcs_version: "4.x-dev"
77+
- php: "8.1"
78+
phpcs_version: "3.6.1"
79+
80+
- php: "8.0"
81+
phpcs_version: "4.x-dev"
82+
- php: "8.0"
83+
phpcs_version: "3.5.7"
84+
85+
- php: "7.4"
86+
phpcs_version: "4.x-dev"
6787

6888
# Experimental builds.
69-
- php: '8.5' # Nightly.
70-
phpcs_version: 'dev-master'
89+
- php: "8.5" # Nightly.
90+
phpcs_version: "4.x-dev"
7191

7292
name: "Test: PHP ${{ matrix.php }} on PHPCS ${{ matrix.phpcs_version }}"
7393

@@ -82,7 +102,7 @@ jobs:
82102
run: |
83103
# On stable PHPCS versions, allow for PHP deprecation notices.
84104
# Unit tests don't need to fail on those for stable releases where those issues won't get fixed anymore.
85-
if [ "${{ matrix.phpcs_version }}" != "dev-master" ]; then
105+
if [ "${{ matrix.phpcs_version }}" != "4.x-dev" ]; then
86106
echo 'PHP_INI=error_reporting=E_ALL & ~E_DEPRECATED, display_errors=On, zend.assertions=1' >> $GITHUB_OUTPUT
87107
else
88108
echo 'PHP_INI=error_reporting=-1, display_errors=On, zend.assertions=1' >> $GITHUB_OUTPUT
@@ -95,7 +115,7 @@ jobs:
95115
ini-values: ${{ steps.set_ini.outputs.PHP_INI }}
96116
coverage: none
97117

98-
- name: 'Composer: adjust dependencies'
118+
- name: "Composer: adjust dependencies"
99119
run: |
100120
# Remove dev dependencies which are not compatible with all supported PHP versions.
101121
composer remove --dev --no-update sirbrillig/phpcs-import-detection phpstan/phpstan
@@ -146,15 +166,15 @@ jobs:
146166
strategy:
147167
matrix:
148168
include:
149-
- php: '8.4'
150-
phpcs_version: 'dev-master'
151-
- php: '7.4'
152-
phpcs_version: '3.5.6'
169+
- php: "8.4"
170+
phpcs_version: "4.x-dev"
171+
- php: "7.4"
172+
phpcs_version: "3.5.7"
153173

154-
- php: '5.4'
155-
phpcs_version: 'dev-master'
156-
- php: '5.4'
157-
phpcs_version: '3.5.6'
174+
- php: "7.2"
175+
phpcs_version: "4.x-dev"
176+
- php: "5.4"
177+
phpcs_version: "3.5.7"
158178

159179
name: "Coverage: PHP ${{ matrix.php }} on PHPCS ${{ matrix.phpcs_version }}"
160180

@@ -167,7 +187,7 @@ jobs:
167187
run: |
168188
# On stable PHPCS versions, allow for PHP deprecation notices.
169189
# Unit tests don't need to fail on those for stable releases where those issues won't get fixed anymore.
170-
if [ "${{ matrix.phpcs_version }}" != "dev-master" ]; then
190+
if [ "${{ matrix.phpcs_version }}" != "4.x-dev" ]; then
171191
echo 'PHP_INI=error_reporting=E_ALL & ~E_DEPRECATED, display_errors=On, zend.assertions=1' >> $GITHUB_OUTPUT
172192
else
173193
echo 'PHP_INI=error_reporting=-1, display_errors=On, zend.assertions=1' >> $GITHUB_OUTPUT
@@ -180,7 +200,7 @@ jobs:
180200
ini-values: ${{ steps.set_ini.outputs.PHP_INI }}
181201
coverage: xdebug
182202

183-
- name: 'Composer: adjust dependencies'
203+
- name: "Composer: adjust dependencies"
184204
run: |
185205
# Remove dev dependencies which are not compatible with all supported PHP/PHPCS versions.
186206
composer remove --dev --no-update phpcsstandards/phpcsdevcs sirbrillig/phpcs-import-detection phpstan/phpstan

VariableAnalysis/Lib/Helpers.php

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public static function findContainingOpeningBracket(File $phpcsFile, $stackPtr)
9393
$tokens = $phpcsFile->getTokens();
9494
if (isset($tokens[$stackPtr]['nested_parenthesis'])) {
9595
/**
96-
* @var array<int|string|null>
96+
* @var list<int|string>
9797
*/
9898
$openPtrs = array_keys($tokens[$stackPtr]['nested_parenthesis']);
9999
return (int)end($openPtrs);
@@ -319,8 +319,18 @@ public static function findFunctionCall(File $phpcsFile, $stackPtr)
319319
if (is_int($openPtr)) {
320320
// First non-whitespace thing and see if it's a T_STRING function name
321321
$functionPtr = $phpcsFile->findPrevious(Tokens::$emptyTokens, $openPtr - 1, null, true, null, true);
322-
if (is_int($functionPtr) && $tokens[$functionPtr]['code'] === T_STRING) {
323-
return $functionPtr;
322+
if (is_int($functionPtr)) {
323+
$functionTokenCode = $tokens[$functionPtr]['code'];
324+
// In PHPCS 4.x, function names can be T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, or T_NAME_RELATIVE
325+
$validFunctionTokens = [
326+
T_STRING,
327+
T_NAME_FULLY_QUALIFIED,
328+
T_NAME_QUALIFIED,
329+
T_NAME_RELATIVE,
330+
];
331+
if (in_array($functionTokenCode, $validFunctionTokens, true)) {
332+
return $functionPtr;
333+
}
324334
}
325335
}
326336
return null;
@@ -364,9 +374,6 @@ public static function findFunctionCallArguments(File $phpcsFile, $stackPtr)
364374
if (self::findContainingOpeningBracket($phpcsFile, $nextPtr) === $openPtr) {
365375
// Comma is at our level of brackets, it's an argument delimiter.
366376
$range = range($lastArgComma + 1, $nextPtr - 1);
367-
$range = array_filter($range, function ($element) {
368-
return is_int($element);
369-
});
370377
array_push($argPtrs, $range);
371378
$lastArgComma = $nextPtr;
372379
}
@@ -394,7 +401,8 @@ public static function getNextAssignPointer(File $phpcsFile, $stackPtr)
394401

395402
// Is the next non-whitespace an assignment?
396403
$nextPtr = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true, null, true);
397-
if (is_int($nextPtr)
404+
if (
405+
is_int($nextPtr)
398406
&& isset(Tokens::$assignmentTokens[$tokens[$nextPtr]['code']])
399407
// Ignore double arrow to prevent triggering on `foreach ( $array as $k => $v )`.
400408
&& $tokens[$nextPtr]['code'] !== T_DOUBLE_ARROW
@@ -548,6 +556,9 @@ public static function findVariableScopeExceptArrowFunctions(File $phpcsFile, $s
548556
T_DOUBLE_QUOTED_STRING,
549557
T_HEREDOC,
550558
T_STRING,
559+
T_NAME_FULLY_QUALIFIED,
560+
T_NAME_QUALIFIED,
561+
T_NAME_RELATIVE,
551562
];
552563
if (! in_array($tokens[$stackPtr]['code'], $allowedTypes, true)) {
553564
throw new \Exception("Cannot find variable scope for non-variable {$tokens[$stackPtr]['type']}");
@@ -618,24 +629,6 @@ private static function getStartOfTokenScope(File $phpcsFile, $stackPtr)
618629
return 0;
619630
}
620631

621-
/**
622-
* @param File $phpcsFile
623-
* @param int $stackPtr
624-
*
625-
* @return bool
626-
*/
627-
public static function isTokenInsideArrowFunctionDefinition(File $phpcsFile, $stackPtr)
628-
{
629-
$tokens = $phpcsFile->getTokens();
630-
$token = $tokens[$stackPtr];
631-
$openParenIndices = isset($token['nested_parenthesis']) ? $token['nested_parenthesis'] : [];
632-
if (empty($openParenIndices)) {
633-
return false;
634-
}
635-
$openParenPtr = $openParenIndices[0];
636-
return self::isArrowFunction($phpcsFile, $openParenPtr - 1);
637-
}
638-
639632
/**
640633
* @param File $phpcsFile
641634
* @param int $stackPtr
@@ -1290,7 +1283,7 @@ public static function getFunctionIndexForFunctionCallArgument(File $phpcsFile,
12901283
return null;
12911284
}
12921285
/**
1293-
* @var array<int|string|null>
1286+
* @var list<int|string>
12941287
*/
12951288
$startingParenthesis = array_keys($token['nested_parenthesis']);
12961289
$startOfArguments = end($startingParenthesis);
@@ -1681,9 +1674,27 @@ public static function getFunctionNameWithNamespace(File $phpcsFile, $stackPtr)
16811674
$startOfScope = self::findVariableScope($phpcsFile, $stackPtr);
16821675
$functionName = $tokens[$stackPtr]['content'];
16831676

1677+
// In PHPCS 4.x, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, and T_NAME_RELATIVE
1678+
// tokens already contain the full namespaced name, so we can return early.
1679+
if ($tokens[$stackPtr]['code'] === T_NAME_FULLY_QUALIFIED) {
1680+
return $functionName;
1681+
}
1682+
if ($tokens[$stackPtr]['code'] === T_NAME_QUALIFIED) {
1683+
return $functionName;
1684+
}
1685+
if ($tokens[$stackPtr]['code'] === T_NAME_RELATIVE) {
1686+
return $functionName;
1687+
}
1688+
16841689
// Move backwards from the token, collecting namespace separators and
16851690
// strings, until we encounter whitespace or something else.
1686-
$partOfNamespace = [T_NS_SEPARATOR, T_STRING];
1691+
$partOfNamespace = [
1692+
T_NS_SEPARATOR,
1693+
T_STRING,
1694+
T_NAME_QUALIFIED,
1695+
T_NAME_RELATIVE,
1696+
T_NAME_FULLY_QUALIFIED,
1697+
];
16871698
for ($i = $stackPtr - 1; $i > $startOfScope; $i--) {
16881699
if (! in_array($tokens[$i]['code'], $partOfNamespace, true)) {
16891700
break;
@@ -1708,6 +1719,15 @@ private static function isTokenPossiblyPartOfTypehint(File $phpcsFile, $stackPtr
17081719
if ($token['code'] === 'PHPCS_T_NULLABLE') {
17091720
return true;
17101721
}
1722+
if ($token['code'] === T_NAME_QUALIFIED) {
1723+
return true;
1724+
}
1725+
if ($token['code'] === T_NAME_RELATIVE) {
1726+
return true;
1727+
}
1728+
if ($token['code'] === T_NAME_FULLY_QUALIFIED) {
1729+
return true;
1730+
}
17111731
if ($token['code'] === T_NS_SEPARATOR) {
17121732
return true;
17131733
}

VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1503,7 +1503,34 @@ protected function processVariableAsPassByReferenceFunctionCall(File $phpcsFile,
15031503

15041504
// Is our function a known pass-by-reference function?
15051505
$functionName = $tokens[$functionPtr]['content'];
1506-
$refArgs = $this->getPassByReferenceFunction($functionName);
1506+
1507+
// In PHPCS 4.x, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, and T_NAME_RELATIVE
1508+
// tokens contain the full namespaced name. Extract just the base name for the
1509+
// first check so that 'my_function' in the config can match '\My\Namespace\my_function'.
1510+
$functionBaseName = $functionName;
1511+
if ($tokens[$functionPtr]['code'] === T_NAME_FULLY_QUALIFIED) {
1512+
$lastBackslashPos = strrpos($functionName, '\\');
1513+
if ($lastBackslashPos !== false) {
1514+
$functionBaseName = substr($functionName, $lastBackslashPos + 1);
1515+
}
1516+
} elseif ($tokens[$functionPtr]['code'] === T_NAME_QUALIFIED) {
1517+
$lastBackslashPos = strrpos($functionName, '\\');
1518+
if ($lastBackslashPos !== false) {
1519+
$functionBaseName = substr($functionName, $lastBackslashPos + 1);
1520+
}
1521+
} elseif ($tokens[$functionPtr]['code'] === T_NAME_RELATIVE) {
1522+
$lastBackslashPos = strrpos($functionName, '\\');
1523+
if ($lastBackslashPos !== false) {
1524+
$functionBaseName = substr($functionName, $lastBackslashPos + 1);
1525+
}
1526+
}
1527+
1528+
// Ensure we have a string (should always be true, but helps static analyzers).
1529+
if (! is_string($functionBaseName) || $functionBaseName === '') {
1530+
return false;
1531+
}
1532+
1533+
$refArgs = $this->getPassByReferenceFunction($functionBaseName);
15071534
if (! $refArgs) {
15081535
// Check again with the fully namespaced function name.
15091536
$functionName = Helpers::getFunctionNameWithNamespace($phpcsFile, $functionPtr);

composer.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,12 @@
5151
},
5252
"require": {
5353
"php": ">=5.4.0",
54-
"squizlabs/php_codesniffer": "^3.5.6"
54+
"squizlabs/php_codesniffer": "^3.5.7 || ^4.0.0"
5555
},
5656
"require-dev": {
5757
"phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0 || ^10.5.32 || ^11.3.3",
58-
"phpcsstandards/phpcsdevcs": "^1.1",
59-
"phpstan/phpstan": "^1.7",
58+
"phpstan/phpstan": "^1.7 || ^2.0",
6059
"dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0",
61-
"vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0"
60+
"vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0 || ^6.0 || ^7.0"
6261
}
6362
}

0 commit comments

Comments
 (0)