Skip to content

Commit

Permalink
Merge pull request #454 from PHPCSStandards/develop
Browse files Browse the repository at this point in the history
Release 1.0.3
  • Loading branch information
jrfnl committed Apr 13, 2023
2 parents e74812a + 772f8fa commit 73d7b07
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/update-phpcs-versionnr.yml
Expand Up @@ -68,7 +68,7 @@ jobs:
run: git status -vv --untracked=all

- name: Create pull request
uses: peter-evans/create-pull-request@v4
uses: peter-evans/create-pull-request@v5
with:
base: ${{ steps.branches.outputs.BASE }}
branch: ${{ steps.branches.outputs.PR_BRANCH }}
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Expand Up @@ -9,6 +9,23 @@ This projects adheres to [Keep a CHANGELOG](https://keepachangelog.com/) and use

_Nothing yet._

## [1.0.3] - 2023-04-13

### Changed

#### Other

* Various small housekeeping and maintenance updates.

### Fixed

#### Utils

* The `PassedParameters` class now allows for function calls to global functions called `self()`, `parent()` or `static()`. [#452]

[#452]: https://github.com/PHPCSStandards/PHPCSUtils/pull/452


## [1.0.2] - 2023-03-28

### Changed
Expand Down Expand Up @@ -805,6 +822,7 @@ This initial alpha release contains the following utility classes:


[Unreleased]: https://github.com/PHPCSStandards/PHPCSUtils/compare/stable...HEAD
[1.0.3]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.2...1.0.3
[1.0.2]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.1...1.0.2
[1.0.1]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.0...1.0.1
[1.0.0]: https://github.com/PHPCSStandards/PHPCSUtils/compare/1.0.0-rc1...1.0.0
Expand Down
13 changes: 9 additions & 4 deletions PHPCSUtils/Utils/PassedParameters.php
Expand Up @@ -47,13 +47,15 @@ final class PassedParameters
/**
* Checks if any parameters have been passed.
*
* - If passed a `T_STRING`, `T_NAME_FULLY_QUALIFIED`, `T_NAME_RELATIVE`, `T_NAME_QUALIFIED`
* - If passed a `T_STRING`, `T_NAME_FULLY_QUALIFIED`, `T_NAME_RELATIVE`, `T_NAME_QUALIFIED`,
* or `T_VARIABLE` stack pointer, it will treat it as a function call.
* If a `T_STRING` or `T_VARIABLE` which is *not* a function call is passed, the behaviour is
* undetermined.
* - If passed a `T_ANON_CLASS` stack pointer, it will accept it as a class instantiation.
* - If passed a `T_SELF`, `T_STATIC` or `T_PARENT` stack pointer, it will accept it as a
* class instantiation function call when used like `new self()`.
* class instantiation function call when used like `new self()` (with or without parenthesis).
* When these hierarchiecal keywords are not preceded by the `new` keyword, parenthesis
* will be required for the token to be accepted.
* - If passed a `T_ARRAY` or `T_OPEN_SHORT_ARRAY` stack pointer, it will detect
* whether the array has values or is empty.
* For purposes of backward-compatibility with older PHPCS versions, `T_OPEN_SQUARE_BRACKET`
Expand Down Expand Up @@ -89,9 +91,13 @@ public static function hasParameters(File $phpcsFile, $stackPtr, $isShortArray =
);
}

// Only accept self/static/parent if preceded by `new` or followed by an open parenthesis.
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if (isset(Collections::ooHierarchyKeywords()[$tokens[$stackPtr]['code']]) === true) {
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($tokens[$prev]['code'] !== \T_NEW) {
if ($tokens[$prev]['code'] !== \T_NEW
&& ($next !== false && $tokens[$next]['code'] !== \T_OPEN_PARENTHESIS)
) {
throw new RuntimeException(
'The hasParameters() method expects a function call, array, isset or unset token to be passed.'
);
Expand All @@ -107,7 +113,6 @@ public static function hasParameters(File $phpcsFile, $stackPtr, $isShortArray =
);
}

$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($next === false) {
return false;
}
Expand Down
3 changes: 3 additions & 0 deletions Tests/BackCompat/BCFile/GetDeclarationNameTest.inc
Expand Up @@ -85,6 +85,9 @@ enum Hoo : string
/* testBackedEnumNoSpaceBetweenNameAndColon */
enum Suit: int implements Colorful, CardGame {}

/* testFunctionReturnByRefWithReservedKeywordEach */
function &each() {}

/* testLiveCoding */
// Intentional parse error. This has to be the last test in the file.
function // Comment.
4 changes: 4 additions & 0 deletions Tests/BackCompat/BCFile/GetDeclarationNameTest.php
Expand Up @@ -192,6 +192,10 @@ public function dataGetDeclarationName()
'/* testBackedEnumNoSpaceBetweenNameAndColon */',
'Suit',
],
'function-return-by-reference-with-reserved-keyword-each' => [
'/* testFunctionReturnByRefWithReservedKeywordEach */',
'each',
],
];
}
}
6 changes: 6 additions & 0 deletions Tests/BackCompat/BCFile/GetMethodParametersTest.inc
Expand Up @@ -206,6 +206,12 @@ class ConstructorPropertyPromotionWithReadOnly {
public function __construct(public readonly ?int $promotedProp, ReadOnly private string|bool &$promotedToo) {}
}

class ConstructorPropertyPromotionWithReadOnlyNoTypeDeclaration {
/* testPHP81ConstructorPropertyPromotionWithReadOnlyNoTypeDeclaration */
// Intentional fatal error. Readonly properties MUST be typed.
public function __construct(public readonly $promotedProp, ReadOnly private &$promotedToo) {}
}

/* testPHP8ConstructorPropertyPromotionGlobalFunction */
// Intentional fatal error. Property promotion not allowed in non-constructor, but that's not the concern of this method.
function globalFunction(private $x) {}
Expand Down
51 changes: 51 additions & 0 deletions Tests/BackCompat/BCFile/GetMethodParametersTest.php
Expand Up @@ -1961,6 +1961,57 @@ public function testPHP81ConstructorPropertyPromotionWithReadOnly()
$this->getMethodParametersTestHelper('/* ' . __FUNCTION__ . ' */', $expected);
}

/**
* Verify recognition of PHP8 constructor with property promotion using PHP 8.1 readonly keyword
* without a property type.
*
* @return void
*/
public function testPHP81ConstructorPropertyPromotionWithReadOnlyNoTypeDeclaration()
{
$expected = [];
$expected[0] = [
'token' => 8, // Offset from the T_FUNCTION token.
'name' => '$promotedProp',
'content' => 'public readonly $promotedProp',
'has_attributes' => false,
'pass_by_reference' => false,
'reference_token' => false,
'variable_length' => false,
'variadic_token' => false,
'type_hint' => '',
'type_hint_token' => false,
'type_hint_end_token' => false,
'nullable_type' => false,
'property_visibility' => 'public',
'visibility_token' => 4, // Offset from the T_FUNCTION token.
'property_readonly' => true,
'readonly_token' => 6, // Offset from the T_FUNCTION token.
'comma_token' => 9,
];
$expected[1] = [
'token' => 16, // Offset from the T_FUNCTION token.
'name' => '$promotedToo',
'content' => 'ReadOnly private &$promotedToo',
'has_attributes' => false,
'pass_by_reference' => true,
'reference_token' => 15, // Offset from the T_FUNCTION token.
'variable_length' => false,
'variadic_token' => false,
'type_hint' => '',
'type_hint_token' => false,
'type_hint_end_token' => false,
'nullable_type' => false,
'property_visibility' => 'private',
'visibility_token' => 13, // Offset from the T_FUNCTION token.
'property_readonly' => true,
'readonly_token' => 11, // Offset from the T_FUNCTION token.
'comma_token' => false,
];

$this->getMethodParametersTestHelper('/* ' . __FUNCTION__ . ' */', $expected);
}

/**
* Verify behaviour when a non-constructor function uses PHP 8 property promotion syntax.
*
Expand Down
9 changes: 9 additions & 0 deletions Tests/Utils/ObjectDeclarations/GetNameDiffTest.inc
Expand Up @@ -12,6 +12,15 @@ interface switch{ // Intentional parse error.
public function someFunction();
}

/* testFunctionReturnByRefWithReservedKeywordParent */
function &parent() {}

/* testFunctionReturnByRefWithReservedKeywordSelf */
function &self() {}

/* testFunctionReturnByRefWithReservedKeywordStatic */
function &static() {}

/* testLiveCoding */
// Intentional parse error. Redundancy testing.
abstract class
12 changes: 12 additions & 0 deletions Tests/Utils/ObjectDeclarations/GetNameDiffTest.php
Expand Up @@ -117,6 +117,18 @@ public function dataGetName()
'testMarker' => '/* testInvalidInterfaceName */',
'expected' => 'switch',
],
'function-return-by-reference-with-reserved-keyword-parent' => [
'/* testFunctionReturnByRefWithReservedKeywordParent */',
'parent',
],
'function-return-by-reference-with-reserved-keyword-self' => [
'/* testFunctionReturnByRefWithReservedKeywordSelf */',
'self',
],
'function-return-by-reference-with-reserved-keyword-static' => [
'/* testFunctionReturnByRefWithReservedKeywordStatic */',
'static',
],
];
}
}
9 changes: 9 additions & 0 deletions Tests/Utils/PassedParameters/HasParametersTest.inc
Expand Up @@ -74,6 +74,15 @@ class HierarchyKeywordsAsMethodNames {
/* testHasParamsFunctionCall8 */
$a = $this->parent(true);
}

public function callGlobalFunctionsUsingKeywords() {
/* testHasParamsFunctionCall9 */
$a = self(true);
/* testHasParamsFunctionCall10 */
$a = static(true);
/* testHasParamsFunctionCall11 */
$a = parent(true);
}
}

/* testNoParamsFunctionCallFullyQualified */
Expand Down
29 changes: 24 additions & 5 deletions Tests/Utils/PassedParameters/HasParametersTest.php
Expand Up @@ -124,11 +124,11 @@ public function testNotAShortArray()
*
* @dataProvider dataHasParameters
*
* @param string $testMarker The comment which prefaces the target token in the test file.
* @param int|string $targetType The type of token to look for.
* @param bool $expected Whether or not the function/array has parameters/values.
* @param string $targetContent Optional. The content of the target token to find.
* Defaults to null (ignore content).
* @param string $testMarker The comment which prefaces the target token in the test file.
* @param int|string|array $targetType The type(s) of token to look for.
* @param bool $expected Whether or not the function/array has parameters/values.
* @param string $targetContent Optional. The content of the target token to find.
* Defaults to null (ignore content).
*
* @return void
*/
Expand Down Expand Up @@ -231,6 +231,25 @@ public function dataHasParameters()
'expected' => true,
'targetContent' => 'parent',
],
'has-params-function-call-9-self-as-global-function-name' => [
'testMarker' => '/* testHasParamsFunctionCall9 */',
'targetType' => [\T_STRING, \T_SELF],
'expected' => true,
'targetContent' => 'self',
],
// Parse error in PHP, but not our concern.
'has-params-function-call-10-static-as-global-function-name' => [
'testMarker' => '/* testHasParamsFunctionCall10 */',
'targetType' => [\T_STRING, \T_STATIC],
'expected' => true,
'targetContent' => 'static',
],
'has-params-function-call-11-parent-as-global-function-name' => [
'testMarker' => '/* testHasParamsFunctionCall11 */',
'targetType' => [\T_STRING, \T_PARENT],
'expected' => true,
'targetContent' => 'parent',
],

'no-params-function-call-fully-qualified' => [
'testMarker' => '/* testNoParamsFunctionCallFullyQualified */',
Expand Down

0 comments on commit 73d7b07

Please sign in to comment.