Skip to content

Commit

Permalink
Merge pull request #618 from PHPCSStandards/php-8.4/update-for-exit-a…
Browse files Browse the repository at this point in the history
…s-function-call

PHP 8.4: updates for exit as function call
  • Loading branch information
jrfnl authored Aug 18, 2024
2 parents 482407c + 15deff1 commit 16645d7
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 7 deletions.
4 changes: 4 additions & 0 deletions PHPCSUtils/Tokens/Collections.php
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,10 @@ public static function functionCallTokens()
$tokens[\T_ANON_CLASS] = \T_ANON_CLASS;
$tokens += self::$ooHierarchyKeywords;

// As of PHP 8.4, exit()/die() should be treated as function call tokens.
// Sniffs using this collection should safeguard against use as a constant.
$tokens[\T_EXIT] = \T_EXIT;

return $tokens;
}

Expand Down
7 changes: 5 additions & 2 deletions PHPCSUtils/Utils/PassedParameters.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ final class PassedParameters
* a short array opener.
* - If passed a `T_ISSET` or `T_UNSET` stack pointer, it will detect whether those
* language constructs have "parameters".
* - If passed a `T_EXIT` stack pointer, it will treat it as a function call and detect whether
* it has been passed parameters. When the `T_EXIT` is used as a constant, the return value
* will be `false` (no parameters).
*
* @since 1.0.0
*
Expand Down Expand Up @@ -94,7 +97,7 @@ public static function hasParameters(File $phpcsFile, $stackPtr, $isShortArray =
throw OutOfBoundsStackPtr::create(2, '$stackPtr', $stackPtr);
}

$acceptedTokens = 'function call, array, isset or unset';
$acceptedTokens = 'function call, array, isset, unset or exit';
if (isset(Collections::parameterPassingTokens()[$tokens[$stackPtr]['code']]) === false) {
throw UnexpectedTokenType::create(2, '$stackPtr', $acceptedTokens, $tokens[$stackPtr]['type']);
}
Expand Down Expand Up @@ -131,7 +134,7 @@ public static function hasParameters(File $phpcsFile, $stackPtr, $isShortArray =
return true;
}

// Deal with function calls, long arrays, isset and unset.
// Deal with function calls, long arrays, isset, unset and exit/die.
// Next non-empty token should be the open parenthesis.
if ($tokens[$next]['code'] !== \T_OPEN_PARENTHESIS) {
return false;
Expand Down
1 change: 1 addition & 0 deletions Tests/Tokens/Collections/FunctionCallTokensTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public function testFunctionCallTokens()
\T_PARENT => \T_PARENT,
\T_SELF => \T_SELF,
\T_STATIC => \T_STATIC,
\T_EXIT => \T_EXIT,
];

$this->assertSame($expected, Collections::functionCallTokens());
Expand Down
1 change: 1 addition & 0 deletions Tests/Tokens/Collections/ParameterPassingTokensTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public function testParameterPassingTokens()
\T_PARENT => \T_PARENT,
\T_SELF => \T_SELF,
\T_STATIC => \T_STATIC,
\T_EXIT => \T_EXIT,
\T_ISSET => \T_ISSET,
\T_UNSET => \T_UNSET,
\T_ARRAY => \T_ARRAY,
Expand Down
3 changes: 3 additions & 0 deletions Tests/Utils/PassedParameters/GetParametersNamedTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ foo( label: $cond ? true : false, more: $cond ? CONSTANT_A : CONSTANT_B );
/* testTernaryWithFunctionCallsInThenElse */
echo $cond ? foo( label: $something ) : /* testTernaryWithFunctionCallsInElse */ bar( more: $something_else );

/* testExitWithNamedParam */
exit( status: 1 );

/* testCompileErrorNamedBeforePositional */
// Not the concern of PHPCSUtils. Should still be handled.
test(param: $bar, $foo);
Expand Down
14 changes: 14 additions & 0 deletions Tests/Utils/PassedParameters/GetParametersNamedTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,20 @@ public static function dataGetParameters()
],
],
],
'named-args-for-exit' => [
'testMarker' => '/* testExitWithNamedParam */',
'targetType' => \T_EXIT,
'expected' => [
'status' => [
'name' => 'status',
'name_token' => 3,
'start' => 5,
'end' => 7,
'raw' => '1',
],
],
],

'named-args-compile-error-named-before-positional' => [
'testMarker' => '/* testCompileErrorNamedBeforePositional */',
'targetType' => \T_STRING,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function testHasParametersDontSkipShortArrayCheck($testMarker, $targetTyp
if ($expectException === true) {
$this->expectException('PHPCSUtils\Exceptions\UnexpectedTokenType');
$this->expectExceptionMessage(
'Argument #2 ($stackPtr) must be of type function call, array, isset or unset;'
'Argument #2 ($stackPtr) must be of type function call, array, isset, unset or exit;'
);
}

Expand Down Expand Up @@ -96,7 +96,7 @@ public function testGetParametersSkipShortArrayCheck($testMarker, $targetType, $
if ($targetType === \T_OPEN_SQUARE_BRACKET) {
$this->expectException('PHPCSUtils\Exceptions\UnexpectedTokenType');
$this->expectExceptionMessage(
'Argument #2 ($stackPtr) must be of type function call, array, isset or unset;'
'Argument #2 ($stackPtr) must be of type function call, array, isset, unset or exit;'
);
}

Expand Down
3 changes: 3 additions & 0 deletions Tests/Utils/PassedParameters/GetParametersTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ if ( isset(
/* testUnset */
unset( $variable, $object->property, static::$property, $array[$name], );

/* testDie */
die( $status );

/* testAnonClass */
$anon = new class( $param1, $param2 ) {
public function __construct($param1, $param2) {}
Expand Down
12 changes: 12 additions & 0 deletions Tests/Utils/PassedParameters/GetParametersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,18 @@ public function test( $foo, $bar ) {
],
],
],
'die' => [
'testMarker' => '/* testDie */',
'targetType' => \T_EXIT,
'expected' => [
1 => [
'start' => 2,
'end' => 4,
'raw' => '$status',
],
],
],

'anon-class' => [
'testMarker' => '/* testAnonClass */',
'targetType' => \T_ANON_CLASS,
Expand Down
19 changes: 19 additions & 0 deletions Tests/Utils/PassedParameters/HasParametersTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ class Foo {
/* testShortListNotShortArray */
[ $a, $b ] = $array;

/* testExitAsConstant */
exit;

/* testDieAsConstant */
die;


// Function calls: no parameters.

/* testNoParamsFunctionCall1 */
Expand Down Expand Up @@ -163,6 +170,18 @@ unset(

);

/* testNoParamsExit */
exit();

/* testHasParamsExit */
exit( $code );

/* testNoParamsDie */
die( /*comment*/ );

/* testHasParamsDie */
die( $status );

/* testNoParamsNoParensAnonClass */
$anon = new class extends FooBar {};

Expand Down
38 changes: 35 additions & 3 deletions Tests/Utils/PassedParameters/HasParametersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public function testNotAnAcceptedTokenException()
{
$this->expectException('PHPCSUtils\Exceptions\UnexpectedTokenType');
$this->expectExceptionMessage(
'Argument #2 ($stackPtr) must be of type function call, array, isset or unset;'
'Argument #2 ($stackPtr) must be of type function call, array, isset, unset or exit;'
);

$interface = $this->getTargetToken('/* testNotAnAcceptedToken */', \T_INTERFACE);
Expand All @@ -83,7 +83,7 @@ public function testNotACallToConstructor($testMarker, $targetType)
{
$this->expectException('PHPCSUtils\Exceptions\UnexpectedTokenType');
$this->expectExceptionMessage(
'Argument #2 ($stackPtr) must be of type function call, array, isset or unset;'
'Argument #2 ($stackPtr) must be of type function call, array, isset, unset or exit;'
);

$self = $this->getTargetToken($testMarker, $targetType);
Expand Down Expand Up @@ -124,7 +124,7 @@ public function testNotAShortArray()
{
$this->expectException('PHPCSUtils\Exceptions\UnexpectedTokenType');
$this->expectExceptionMessage(
'Argument #2 ($stackPtr) must be of type function call, array, isset or unset;'
'Argument #2 ($stackPtr) must be of type function call, array, isset, unset or exit;'
);

$self = $this->getTargetToken(
Expand Down Expand Up @@ -387,6 +387,38 @@ public static function dataHasParameters()
'expected' => true,
],

// Exit/die.
'exit as a constant' => [
'testMarker' => '/* testExitAsConstant */',
'targetType' => \T_EXIT,
'expected' => false,
],
'die as a constant' => [
'testMarker' => '/* testDieAsConstant */',
'targetType' => \T_EXIT,
'expected' => false,
],
'no-params-exit' => [
'testMarker' => '/* testNoParamsExit */',
'targetType' => \T_EXIT,
'expected' => false,
],
'has-params-exit' => [
'testMarker' => '/* testHasParamsExit */',
'targetType' => \T_EXIT,
'expected' => true,
],
'no-params-die' => [
'testMarker' => '/* testNoParamsDie */',
'targetType' => \T_EXIT,
'expected' => false,
],
'has-params-die' => [
'testMarker' => '/* testHasParamsDie */',
'targetType' => \T_EXIT,
'expected' => true,
],

// Anonymous class instantiation.
'no-params-no-parens-anon-class' => [
'testMarker' => '/* testNoParamsNoParensAnonClass */',
Expand Down

0 comments on commit 16645d7

Please sign in to comment.