Skip to content

Commit c457da9

Browse files
authored
Merge pull request #586 from PHPCSStandards/develop
Release 1.0.11
2 parents f627b49 + d7667db commit c457da9

21 files changed

+579
-64
lines changed

.github/workflows/basics.yml

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,15 @@ jobs:
5555
# Bust the cache at least once a month - output format: YYYY-MM.
5656
custom-cache-suffix: $(date -u "+%Y-%m")
5757

58+
# Updating the lists can fail intermittently, typically after Microsoft has released a new package.
59+
# This should not be blocking for this job, so ignore any errors from this step.
60+
# Ref: https://github.com/dotnet/core/issues/4167
61+
- name: Update the available packages list
62+
continue-on-error: true
63+
run: sudo apt-get update
64+
5865
- name: Install xmllint
59-
run: |
60-
sudo apt-get update
61-
sudo apt-get install --no-install-recommends -y libxml2-utils
66+
run: sudo apt-get install --no-install-recommends -y libxml2-utils
6267

6368
# Show XML violations inline in the file diff.
6469
# @link https://github.com/marketplace/actions/xmllint-problem-matcher
@@ -118,24 +123,13 @@ jobs:
118123
- name: Checkout code
119124
uses: actions/checkout@v4
120125

121-
# This action also handles the caching of the dependencies.
122-
# https://github.com/actions/setup-node
123-
- name: Set up node and enable caching of dependencies
124-
uses: actions/setup-node@v4
125-
with:
126-
node-version: '16'
127-
128-
# @link https://github.com/DavidAnson/markdownlint-cli2
129-
# @link https://github.com/DavidAnson/markdownlint
130-
- name: Install Markdownlint CLI2
131-
run: npm install -g markdownlint-cli2
132-
133126
# @link https://github.com/marketplace/actions/problem-matcher-for-markdownlint-cli
134127
- name: Enable showing issue in PRs
135128
uses: xt0rted/markdownlint-problem-matcher@v3
136129

130+
# @link https://github.com/marketplace/actions/markdownlint-cli2-action
137131
- name: Check markdown with CLI2
138-
run: markdownlint-cli2
132+
uses: DavidAnson/markdownlint-cli2-action@v16
139133

140134
remark:
141135
name: 'QA Markdown'

.github/workflows/quicktest.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ jobs:
6464
if: ${{ matrix.phpcs_version != 'lowest' }}
6565
run: composer require squizlabs/php_codesniffer:"${{ matrix.phpcs_version }}" --no-update --no-scripts --no-interaction
6666

67+
- name: "Composer: use lock file when necessary"
68+
if: ${{ matrix.phpcs_version == 'lowest' }}
69+
run: composer config --unset lock
70+
6771
# Install dependencies and handle caching in one go.
6872
# @link https://github.com/marketplace/actions/install-php-dependencies-with-composer
6973
- name: Install Composer dependencies

.github/workflows/test.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,10 @@ jobs:
180180
- name: 'Composer: remove PHPCSDevCS'
181181
run: composer remove --dev --no-update phpcsstandards/phpcsdevcs
182182

183+
- name: "Composer: use lock file when necessary"
184+
if: ${{ matrix.phpcs_version == 'lowest' }}
185+
run: composer config --unset lock
186+
183187
# Install dependencies and handle caching in one go.
184188
# @link https://github.com/marketplace/actions/install-php-dependencies-with-composer
185189
- name: Install Composer dependencies - normal
@@ -308,6 +312,10 @@ jobs:
308312
if: ${{ matrix.phpcs_version != 'lowest' }}
309313
run: composer require squizlabs/php_codesniffer:"${{ matrix.phpcs_version }}" --no-update --no-scripts --no-interaction
310314

315+
- name: "Composer: use lock file when necessary"
316+
if: ${{ matrix.phpcs_version == 'lowest' }}
317+
run: composer config --unset lock
318+
311319
- name: Install Composer dependencies
312320
uses: "ramsey/composer-install@v3"
313321
with:

.github/workflows/update-docs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ jobs:
120120
retention-days: 5
121121

122122
- name: Setup GH Pages
123-
uses: actions/configure-pages@v4
123+
uses: actions/configure-pages@v5
124124

125125
- name: Build the GH Pages site with Jekyll
126126
uses: actions/jekyll-build-pages@v1

CHANGELOG.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,27 @@ This projects adheres to [Keep a CHANGELOG](https://keepachangelog.com/) and use
99

1010
_Nothing yet._
1111

12+
## [1.0.11] - 2024-04-24
13+
14+
### Changed
15+
16+
#### Other
17+
18+
* Various housekeeping and documentation improvements. Includes a contribution from [@fredden].
19+
20+
### Fixed
21+
22+
#### PHPCS BackCompat
23+
24+
* `BCFile::getMethodProperties()`: small performance improvement & more defensive coding, in line with same fix in PHPCS 3.9.2. [#573]
25+
26+
#### Utils
27+
28+
* `FunctionDeclarations::getProperties()`: small performance improvement & more defensive coding, in line with same fix in PHPCS 3.9.2. [#573]
29+
30+
[#573]: https://github.com/PHPCSStandards/PHPCSUtils/pull/573
31+
32+
1233
## [1.0.10] - 2024-03-18
1334

1435
### Changed
@@ -477,7 +498,7 @@ Please report any bugs/oversights you encounter!
477498
All properties have a replacement which should be used instead, in most cases this will be a method with the same name as the previously used property,
478499

479500
| Deprecated | Replacement | PR | Remarks |
480-
|---------------------------------------------------------------|------------------------------------------------------------------------------------------------------|----------------|------------------------------------------|
501+
| ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | -------------- | ---------------------------------------- |
481502
| `Collections::$alternativeControlStructureSyntaxTokens` | `Collections::alternativeControlStructureSyntaxes()` | [#311] | Mind the change in the name! |
482503
| `Collections::$alternativeControlStructureSyntaxCloserTokens` | `Collections::alternativeControlStructureSyntaxClosers()` | [#311] | Mind the change in the name! |
483504
| `Collections::$arrayTokens` | `Collections::arrayTokens()` | [#311] | |
@@ -510,7 +531,7 @@ All properties have a replacement which should be used instead, in most cases th
510531
Additionally, the following methods in the `Collections` class have been deprecated:
511532

512533
| Deprecated | Replacement | PR |
513-
|----------------------------------------------|--------------------------------------------|--------|
534+
| -------------------------------------------- | ------------------------------------------ | ------ |
514535
| `Collections::arrowFunctionTokensBC()` | Use the `T_FN` token instead. | [#347] |
515536
| `Collections::functionDeclarationTokensBC()` | `Collections::functionDeclarationTokens()` | [#347] |
516537
| `Collections::parameterTypeTokensBC()` | `Collections::parameterTypeTokens()` | [#347] |
@@ -984,6 +1005,7 @@ This initial alpha release contains the following utility classes:
9841005

9851006

9861007
[Unreleased]: https://github.com/PHPCSStandards/PHPCSUtils/compare/stable...HEAD
1008+
[1.0.11]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.10...1.0.11
9871009
[1.0.10]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.9...1.0.10
9881010
[1.0.9]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.8...1.0.9
9891011
[1.0.8]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.7...1.0.8

PHPCSUtils/BackCompat/BCFile.php

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
462462
*
463463
* Changelog for the PHPCS native function:
464464
* - Introduced in PHPCS 0.0.5.
465-
* - The upstream method has received no significant updates since PHPCS 3.9.0.
465+
* - PHPCS 3.9.2: skip over closure use statements. PHPCS #421.
466466
*
467467
* @see \PHP_CodeSniffer\Files\File::getMethodProperties() Original source.
468468
* @see \PHPCSUtils\Utils\FunctionDeclarations::getProperties() PHPCSUtils native improved version.
@@ -480,7 +480,147 @@ public static function getMethodParameters(File $phpcsFile, $stackPtr)
480480
*/
481481
public static function getMethodProperties(File $phpcsFile, $stackPtr)
482482
{
483-
return $phpcsFile->getMethodProperties($stackPtr);
483+
$tokens = $phpcsFile->getTokens();
484+
485+
if ($tokens[$stackPtr]['code'] !== T_FUNCTION
486+
&& $tokens[$stackPtr]['code'] !== T_CLOSURE
487+
&& $tokens[$stackPtr]['code'] !== T_FN
488+
) {
489+
throw new RuntimeException('$stackPtr must be of type T_FUNCTION or T_CLOSURE or T_FN');
490+
}
491+
492+
if ($tokens[$stackPtr]['code'] === T_FUNCTION) {
493+
$valid = [
494+
T_PUBLIC => T_PUBLIC,
495+
T_PRIVATE => T_PRIVATE,
496+
T_PROTECTED => T_PROTECTED,
497+
T_STATIC => T_STATIC,
498+
T_FINAL => T_FINAL,
499+
T_ABSTRACT => T_ABSTRACT,
500+
T_WHITESPACE => T_WHITESPACE,
501+
T_COMMENT => T_COMMENT,
502+
T_DOC_COMMENT => T_DOC_COMMENT,
503+
];
504+
} else {
505+
$valid = [
506+
T_STATIC => T_STATIC,
507+
T_WHITESPACE => T_WHITESPACE,
508+
T_COMMENT => T_COMMENT,
509+
T_DOC_COMMENT => T_DOC_COMMENT,
510+
];
511+
}
512+
513+
$scope = 'public';
514+
$scopeSpecified = false;
515+
$isAbstract = false;
516+
$isFinal = false;
517+
$isStatic = false;
518+
519+
for ($i = ($stackPtr - 1); $i > 0; $i--) {
520+
if (isset($valid[$tokens[$i]['code']]) === false) {
521+
break;
522+
}
523+
524+
switch ($tokens[$i]['code']) {
525+
case T_PUBLIC:
526+
$scope = 'public';
527+
$scopeSpecified = true;
528+
break;
529+
case T_PRIVATE:
530+
$scope = 'private';
531+
$scopeSpecified = true;
532+
break;
533+
case T_PROTECTED:
534+
$scope = 'protected';
535+
$scopeSpecified = true;
536+
break;
537+
case T_ABSTRACT:
538+
$isAbstract = true;
539+
break;
540+
case T_FINAL:
541+
$isFinal = true;
542+
break;
543+
case T_STATIC:
544+
$isStatic = true;
545+
break;
546+
}
547+
}
548+
549+
$returnType = '';
550+
$returnTypeToken = false;
551+
$returnTypeEndToken = false;
552+
$nullableReturnType = false;
553+
$hasBody = true;
554+
$returnTypeTokens = Collections::returnTypeTokens();
555+
556+
if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
557+
$scopeOpener = null;
558+
if (isset($tokens[$stackPtr]['scope_opener']) === true) {
559+
$scopeOpener = $tokens[$stackPtr]['scope_opener'];
560+
}
561+
562+
for ($i = $tokens[$stackPtr]['parenthesis_closer']; $i < $phpcsFile->numTokens; $i++) {
563+
if (($scopeOpener === null && $tokens[$i]['code'] === T_SEMICOLON)
564+
|| ($scopeOpener !== null && $i === $scopeOpener)
565+
) {
566+
// End of function definition.
567+
break;
568+
}
569+
570+
if ($tokens[$i]['code'] === T_USE) {
571+
// Skip over closure use statements.
572+
for ($j = ($i + 1); $j < $phpcsFile->numTokens && isset(Tokens::$emptyTokens[$tokens[$j]['code']]) === true; $j++);
573+
if ($tokens[$j]['code'] === T_OPEN_PARENTHESIS) {
574+
if (isset($tokens[$j]['parenthesis_closer']) === false) {
575+
// Live coding/parse error, stop parsing.
576+
break;
577+
}
578+
579+
$i = $tokens[$j]['parenthesis_closer'];
580+
continue;
581+
}
582+
}
583+
584+
if ($tokens[$i]['code'] === T_NULLABLE) {
585+
$nullableReturnType = true;
586+
}
587+
588+
if (isset($returnTypeTokens[$tokens[$i]['code']]) === true) {
589+
if ($returnTypeToken === false) {
590+
$returnTypeToken = $i;
591+
}
592+
593+
$returnType .= $tokens[$i]['content'];
594+
$returnTypeEndToken = $i;
595+
}
596+
}
597+
598+
if ($tokens[$stackPtr]['code'] === T_FN) {
599+
$bodyToken = T_FN_ARROW;
600+
} else {
601+
$bodyToken = T_OPEN_CURLY_BRACKET;
602+
}
603+
604+
$end = $phpcsFile->findNext([$bodyToken, T_SEMICOLON], $tokens[$stackPtr]['parenthesis_closer']);
605+
$hasBody = ($end !== false && $tokens[$end]['code'] === $bodyToken);
606+
}
607+
608+
if ($returnType !== '' && $nullableReturnType === true) {
609+
$returnType = '?' . $returnType;
610+
}
611+
612+
return [
613+
'scope' => $scope,
614+
'scope_specified' => $scopeSpecified,
615+
'return_type' => $returnType,
616+
'return_type_token' => $returnTypeToken,
617+
'return_type_end_token' => $returnTypeEndToken,
618+
'nullable_return_type' => $nullableReturnType,
619+
'is_abstract' => $isAbstract,
620+
'is_final' => $isFinal,
621+
'is_static' => $isStatic,
622+
'has_body' => $hasBody,
623+
];
484624
}
485625

486626
/**

PHPCSUtils/Utils/FunctionDeclarations.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,25 @@ public static function getProperties(File $phpcsFile, $stackPtr)
270270
break;
271271
}
272272

273+
if ($tokens[$i]['code'] === \T_USE) {
274+
// Skip over closure use statements.
275+
for (
276+
$j = ($i + 1);
277+
$j < $phpcsFile->numTokens && isset(Tokens::$emptyTokens[$tokens[$j]['code']]) === true;
278+
$j++
279+
);
280+
281+
if ($tokens[$j]['code'] === \T_OPEN_PARENTHESIS) {
282+
if (isset($tokens[$j]['parenthesis_closer']) === false) {
283+
// Live coding/parse error, stop parsing.
284+
break;
285+
}
286+
287+
$i = $tokens[$j]['parenthesis_closer'];
288+
continue;
289+
}
290+
}
291+
273292
if ($tokens[$i]['code'] === \T_NULLABLE) {
274293
$nullableReturnType = true;
275294
}

PHPCSUtils/Utils/NamingConventions.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,6 @@ public static function isEqual($nameA, $nameB)
111111
}
112112

113113
// Comparing via strcasecmp will only compare ASCII letters case-insensitively.
114-
return (strcasecmp($nameA, $nameB) === 0);
114+
return (\strcasecmp($nameA, $nameB) === 0);
115115
}
116116
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
/* testParseError */
4+
// Intentional parse error.
5+
$incompleteUse = function(int $a, string $b = '') use(&$import,
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
/**
3+
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
4+
*
5+
* @package PHPCSUtils
6+
* @copyright 2019-2020 PHPCSUtils Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCSStandards/PHPCSUtils
9+
*/
10+
11+
namespace PHPCSUtils\Tests\BackCompat\BCFile;
12+
13+
use PHPCSUtils\BackCompat\BCFile;
14+
use PHPCSUtils\TestUtils\UtilityMethodTestCase;
15+
use PHPCSUtils\Tokens\Collections;
16+
17+
/**
18+
* Tests for the \PHPCSUtils\BackCompat\BCFile::getMethodProperties method.
19+
*
20+
* @covers \PHPCSUtils\BackCompat\BCFile::getMethodProperties
21+
*
22+
* @group functiondeclarations
23+
*
24+
* @since 1.0.11
25+
*/
26+
final class GetMethodPropertiesParseError1Test extends UtilityMethodTestCase
27+
{
28+
29+
/**
30+
* Test handling of closure declarations with an incomplete use clause.
31+
*
32+
* @return void
33+
*/
34+
public function testParseError()
35+
{
36+
$target = $this->getTargetToken('/* testParseError */', Collections::functionDeclarationTokens());
37+
$result = BCFile::getMethodProperties(self::$phpcsFile, $target);
38+
39+
$expected = [
40+
'scope' => 'public',
41+
'scope_specified' => false,
42+
'return_type' => '',
43+
'return_type_token' => false,
44+
'return_type_end_token' => false,
45+
'nullable_return_type' => false,
46+
'is_abstract' => false,
47+
'is_final' => false,
48+
'is_static' => false,
49+
'has_body' => false,
50+
];
51+
52+
$this->assertSame($expected, $result);
53+
}
54+
}

0 commit comments

Comments
 (0)