Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions Universal/Docs/PHP/RequireExitDieParenthesesStandard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0"?>
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
title="Require Exit/Die Parentheses"
>
<standard>
<![CDATA[
Require the use of parentheses when calling `exit` or `die`, even if no argument is given.
]]>
</standard>
<code_comparison>
<code title="Valid: Calling exit/die with parentheses.">
<![CDATA[
die<em>()</em>;
exit<em>($status)</em>;
]]>
</code>
<code title="Invalid: Calling exit/die without parentheses.">
<![CDATA[
die<em></em>;
exit<em></em>;
]]>
</code>
</code_comparison>
</documentation>
85 changes: 85 additions & 0 deletions Universal/Sniffs/PHP/RequireExitDieParenthesesSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/

namespace PHPCSExtra\Universal\Sniffs\PHP;

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;

/**
* Require that `exit`/`die` always be called with parentheses, even if no argument is given.
*
* @since 1.5.0
*/
final class RequireExitDieParenthesesSniff implements Sniff
{

/**
* Name of the metric.
*
* @since 1.5.0
*
* @var string
*/
const METRIC_NAME = 'Exit/die with parentheses';

/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.5.0
*
* @return array<int|string>
*/
public function register()
{
return [\T_EXIT];
}

/**
* Processes this test, when one of its tokens is encountered.
*
* @since 1.5.0
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
if ($nextNonEmpty === false) {
// Live coding. Do not flag (yet).
return;
}

if ($tokens[$nextNonEmpty]['code'] === \T_OPEN_PARENTHESIS) {
// Parentheses found.
$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'yes');
return;
}

$phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'no');

$fix = $phpcsFile->addFixableError(
'Parentheses required when calling %s, even if no argument is given.',
$stackPtr,
'Missing',
[\strtolower(\ltrim($tokens[$stackPtr]['content'], '\\'))]
);

if ($fix === true) {
$phpcsFile->fixer->addContent($stackPtr, '()');
}
}
}
54 changes: 54 additions & 0 deletions Universal/Tests/PHP/RequireExitDieParenthesesUnitTest.1.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

/*
* Not our targets.
* These tests are mostly safeguards against tokenizer issues in PHPCS, at this moment (3.13.4/4.0.0), these wouldn't be tokenized as T_EXIT anyway.
*/
class Mirror {
const exit = 0;
const die = 1;

public $exit = 0;
protected $die = 1;

function exit() {}
function die() {}

function useMe() {
echo self::exit;
echo static::die;
echo parent::exit;
echo AnotherObject::die;
echo $this->exit;
echo $this->die;
echo $this->exit();
echo $this->die();
}
}

// Invalid PHP, but not our concern. Not our target.
namespace\Sub\exit;
Partially\Qualified\die;
\Fully\Qualified\exit;

/*
* OK.
*/
exit();
die();
Exit();
DIE();
\exit();
\die();
exit ($status);
\die /*comment*/ (10);

/*
* Bad.
*/
exit;
die;
EXIT;
Die;
\exit;
\die;
54 changes: 54 additions & 0 deletions Universal/Tests/PHP/RequireExitDieParenthesesUnitTest.1.inc.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

/*
* Not our targets.
* These tests are mostly safeguards against tokenizer issues in PHPCS, at this moment (3.13.4/4.0.0), these wouldn't be tokenized as T_EXIT anyway.
*/
class Mirror {
const exit = 0;
const die = 1;

public $exit = 0;
protected $die = 1;

function exit() {}
function die() {}

function useMe() {
echo self::exit;
echo static::die;
echo parent::exit;
echo AnotherObject::die;
echo $this->exit;
echo $this->die;
echo $this->exit();
echo $this->die();
}
}

// Invalid PHP, but not our concern. Not our target.
namespace\Sub\exit;
Partially\Qualified\die;
\Fully\Qualified\exit;

/*
* OK.
*/
exit();
die();
Exit();
DIE();
\exit();
\die();
exit ($status);
\die /*comment*/ (10);

/*
* Bad.
*/
exit();
die();
EXIT();
Die();
\exit();
\die();
6 changes: 6 additions & 0 deletions Universal/Tests/PHP/RequireExitDieParenthesesUnitTest.2.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?php

// Intentional parse error/live coding.
// This must be the only test in the file.
// This code sniff should **not** be flagged (yet).
exit
59 changes: 59 additions & 0 deletions Universal/Tests/PHP/RequireExitDieParenthesesUnitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
/**
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
*
* @package PHPCSExtra
* @copyright 2020 PHPCSExtra Contributors
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
* @link https://github.com/PHPCSStandards/PHPCSExtra
*/

namespace PHPCSExtra\Universal\Tests\PHP;

use PHP_CodeSniffer\Tests\Standards\AbstractSniffTestCase;

/**
* Unit test class for the RequireExitDieParentheses sniff.
*
* @covers PHPCSExtra\Universal\Sniffs\PHP\RequireExitDieParenthesesSniff
*
* @since 1.5.0
*/
final class RequireExitDieParenthesesUnitTest extends AbstractSniffTestCase
{

/**
* Returns the lines where errors should occur.
*
* @param string $testFile The name of the file being tested.
*
* @return array<int, int> Key is the line number, value is the number of expected errors.
*/
public function getErrorList($testFile = '')
{
switch ($testFile) {
case 'RequireExitDieParenthesesUnitTest.1.inc':
return [
49 => 1,
50 => 1,
51 => 1,
52 => 1,
53 => 1,
54 => 1,
];

default:
return [];
}
}

/**
* Returns the lines where warnings should occur.
*
* @return array<int, int> Key is the line number, value is the number of expected warnings.
*/
public function getWarningList()
{
return [];
}
}