-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new sniff that checks the declare statements
- Loading branch information
Showing
4 changed files
with
850 additions
and
0 deletions.
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
Universal/Docs/DeclareStatements/DeclareStatementsStyleStandard.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?xml version="1.0"?> | ||
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd" | ||
title="Declare Statements Style" | ||
> | ||
<standard> | ||
<![CDATA[ | ||
Declare statements should, by default, be written without curly braces/alternative syntax. | ||
]]> | ||
</standard> | ||
<code_comparison> | ||
<code title="Valid: Declare statement written without curly braces."> | ||
<![CDATA[ | ||
declare(strict_types=1); | ||
]]> | ||
</code> | ||
<code title="Invalid: Declare statement written with curly braces or alternative syntax."> | ||
<![CDATA[ | ||
declare(encoding='ISO-8859-1', ticks=1) { | ||
// Code. | ||
} | ||
declare(encoding='ISO-8859-1', ticks=10): | ||
declare(ticks=1): | ||
// Code. | ||
enddeclare; | ||
enddeclare; | ||
]]> | ||
</code> | ||
</code_comparison> | ||
<standard> | ||
<![CDATA[ | ||
strict_types declaration directive mustn't be written using curly braces, or with alternative syntax. | ||
]]> | ||
</standard> | ||
<code_comparison> | ||
<code title="Valid: strict_types written without curly braces."> | ||
<![CDATA[ | ||
declare(strict_types=1); | ||
]]> | ||
</code> | ||
<code title="Invalid: strict_types written using alternative syntax/curly braces."> | ||
<![CDATA[ | ||
declare(strict_types=1, ticks=1) { | ||
// Code. | ||
} | ||
]]> | ||
</code> | ||
</code_comparison> | ||
</documentation> |
241 changes: 241 additions & 0 deletions
241
Universal/Sniffs/DeclareStatements/DeclareStatementsStyleSniff.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
<?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\DeclareStatements; | ||
|
||
use PHP_CodeSniffer\Files\File; | ||
use PHP_CodeSniffer\Sniffs\Sniff; | ||
use PHP_CodeSniffer\Util\Tokens; | ||
use PHPCSUtils\Utils\TextStrings; | ||
|
||
/** | ||
* Checks the style of the declare statement | ||
* | ||
* `declare` directive can be written in different styles: | ||
* 1. Applied to the rest of the file, usually written at the top of a file like `declare(strict_types=1);` | ||
* 2. Applied to a limited scope using curly braces (the exception to this rule is the `strict_types` directive) | ||
* 3. Applied to a limited scope using alternative control structure syntax | ||
* | ||
* You can also have multiple directives written inside the `declare` directive. | ||
* This sniff will check the style of the `declare` directive - by default applied to all directives, | ||
* written without braces. | ||
* | ||
* You can modify the sniff by changing the `declareStyle` option to require or disallow braces, | ||
* and require or allow the style to only one of the two directives (`ticks` and `encoding`). | ||
* | ||
* @since 1.0.0 | ||
*/ | ||
class DeclareStatementsStyleSniff implements Sniff | ||
{ | ||
|
||
/** | ||
* Whether to require curly brace style or disallow it | ||
* when using declare statements. | ||
* | ||
* Possible values: requireBraces, disallowBraces. | ||
* | ||
* Default is disallowBraces. | ||
* | ||
* @var string | ||
*/ | ||
public $declareStyle = 'disallowBraces'; | ||
|
||
/** | ||
* Which declare to enforce the brace style for. | ||
* If not explicitly defined, the brace style will be applied | ||
* to all declare directive. | ||
* | ||
* The exception is the strict_types directive which cannot be | ||
* written using curly braces. | ||
* | ||
* Possible values: ticks, encoding. | ||
* | ||
* @var array | ||
*/ | ||
public $directiveType = []; | ||
|
||
/** | ||
* Allowed declare directives. | ||
* | ||
* @var array | ||
*/ | ||
private $allowedDirectives = [ | ||
'strict_types', | ||
'ticks', | ||
'encoding', | ||
]; | ||
|
||
/** | ||
* Returns an array of tokens this test wants to listen for. | ||
* | ||
* @since 1.0.0 | ||
* | ||
* @return array | ||
*/ | ||
public function register() | ||
{ | ||
return [ | ||
T_DECLARE | ||
]; | ||
} | ||
|
||
/** | ||
* Processes this test, when one of its tokens is encountered. | ||
* | ||
* @since 1.0.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(); | ||
|
||
$openParenPtr = $tokens[$stackPtr]['parenthesis_opener']; | ||
$closeParenPtr = $tokens[$stackPtr]['parenthesis_closer']; | ||
|
||
if (isset($openParenPtr, $closeParenPtr) === false) { | ||
// Parse error or live coding, bow out. | ||
return; | ||
} | ||
|
||
$directiveStrings = []; | ||
// Get the next string and check if it's an allowed directive. | ||
// Find all the directive strings inside the declare statement. | ||
for ($i = $openParenPtr; $i <= $closeParenPtr; $i++) { | ||
if ($tokens[$i]['code'] === \T_STRING) { | ||
$directiveStrings[] = $tokens[$i]['content']; | ||
} | ||
} | ||
|
||
foreach ($directiveStrings as $directiveString) { | ||
if (!in_array($directiveString, $this->allowedDirectives, true)) { | ||
$phpcsFile->addError( | ||
sprintf( | ||
'Declare directives can be one of: %1$s. "%2$s" found.', | ||
implode(', ', $this->allowedDirectives), | ||
$directiveString | ||
), | ||
$stackPtr, | ||
'WrongDeclareDirective' | ||
); | ||
return; | ||
} | ||
unset($directiveString); | ||
} | ||
|
||
// Curly braces. | ||
$hasScopeOpenerCloser = isset($tokens[$stackPtr]['scope_opener']); | ||
|
||
// If strict types is defined using curly brace, throw error. | ||
if ($hasScopeOpenerCloser !== false && in_array('strict_types', $directiveStrings, true)) { | ||
$phpcsFile->addError( | ||
sprintf( | ||
'strict_types declaration must not use block mode. Opening brace found on line %d', | ||
$tokens[$stackPtr]['line'] | ||
), | ||
$stackPtr, | ||
'Forbidden' | ||
); | ||
return; | ||
} | ||
|
||
// Fix for the case when the code is between the curly braces for the strict_types. | ||
$codePtr = $phpcsFile->findNext(Tokens::$emptyTokens, ($closeParenPtr + 1), null, true); | ||
|
||
/* If the code pointer is not one of: | ||
* \T_SEMICOLON, \T_CLOSE_TAG or \T_OPEN_CURLY_BRACKET, \T_COLON | ||
* throw an error. | ||
*/ | ||
if (!in_array($tokens[$codePtr]['code'], [\T_SEMICOLON, \T_CLOSE_TAG, \T_OPEN_CURLY_BRACKET, \T_COLON], true)) { | ||
$phpcsFile->addError( | ||
'Unexpected code found after opening the declare statement without closing it.', | ||
$stackPtr, | ||
'UnexpectedCodeFound' | ||
); | ||
return; | ||
} | ||
|
||
if ($this->declareStyle === 'requireBraces' && | ||
$hasScopeOpenerCloser === false && | ||
in_array('strict_types', $directiveStrings, true) | ||
) { | ||
$phpcsFile->addError( | ||
'strict_types declaration is not compatible with requireBraces option.', | ||
$stackPtr, | ||
'IncompatibleCurlyBracesRequirement' | ||
); | ||
return; | ||
} | ||
|
||
if ($this->declareStyle === 'disallowBraces' && $hasScopeOpenerCloser !== false) { | ||
$phpcsFile->addError('Declare statement found using curly braces', $stackPtr, 'DisallowedCurlyBraces'); | ||
return; | ||
} | ||
|
||
if (in_array('ticks', $this->directiveType, true)) { | ||
if ($this->declareStyle === 'requireBraces' && | ||
$hasScopeOpenerCloser === false && | ||
in_array('ticks', $directiveStrings, true) | ||
) { | ||
$phpcsFile->addError( | ||
'Declare statement for found without curly braces', | ||
$stackPtr, | ||
'MissingCurlyBracesTicks' | ||
); | ||
return; | ||
} | ||
|
||
if ($this->declareStyle === 'disallowBraces' && $hasScopeOpenerCloser !== false) { | ||
$phpcsFile->addError( | ||
'Declare statement found using curly braces', | ||
$stackPtr, | ||
'DisallowedCurlyBracesTicks' | ||
); | ||
return; | ||
} | ||
} | ||
|
||
if (in_array('encoding', $this->directiveType, true)) { | ||
if ($this->declareStyle === 'requireBraces' && | ||
$hasScopeOpenerCloser === false && | ||
in_array('encoding', $directiveStrings, true) | ||
) { | ||
$phpcsFile->addError( | ||
'Declare statement found without curly braces', | ||
$stackPtr, | ||
'MissingCurlyBracesEncoding' | ||
); | ||
return; | ||
} | ||
|
||
if ($this->declareStyle === 'disallowBraces' && $hasScopeOpenerCloser !== false) { | ||
$phpcsFile->addError( | ||
'Declare statement found using curly braces', | ||
$stackPtr, | ||
'DisallowedCurlyBracesEncoding' | ||
); | ||
return; | ||
} | ||
} | ||
|
||
if ($this->declareStyle === 'requireBraces' && | ||
$hasScopeOpenerCloser === false && | ||
empty($this->directiveType) && | ||
!in_array('strict_types', $directiveStrings, true) | ||
) { | ||
$phpcsFile->addError('Declare statement found without curly braces', $stackPtr, 'MissingCurlyBraces'); | ||
return; | ||
} | ||
} | ||
} |
Oops, something went wrong.