Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Universal.Operators.DisallowLogicalAndOr sniff #52

Merged
merged 1 commit into from
Apr 16, 2020
Merged
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
26 changes: 26 additions & 0 deletions Universal/Docs/Operators/DisallowLogicalAndOrStandard.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<documentation title="Disallow Logical And Or">
<standard>
<![CDATA[
Using the logical "and" and "or" operators is not allowed.

The logical "and" and "or" operators have a significantly lower operator precedence than their boolean "&&" and "||" counter-parts, which often leads to unexpected bugs.
The boolean "&&" and "||" operators are nearly always the better choice.
]]>
</standard>
<code_comparison>
<code title="Valid: Using boolean operators.">
<![CDATA[
if (isset($var) <em>&&</em> $var > 10) {}

if (empty($var) <em>||</em> $var < 0) {}
]]>
</code>
<code title="Invalid: Using logical operators.">
<![CDATA[
if (isset($var) <em>and</em> $var > 10) {}

if (empty($var) <em>or</em> $var < 0) {}
]]>
</code>
</code_comparison>
</documentation>
103 changes: 103 additions & 0 deletions Universal/Sniffs/Operators/DisallowLogicalAndOrSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<?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\Operators;

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

/**
* Enforce the use of the boolean `&&` and `||` operators instead of the logical `and`/`or` operators.
*
* Note: as the {@link https://www.php.net/manual/en/language.operators.precedence.php operator precedence}
* of the logical operators is significantly lower than the operator precedence of boolean operators,
* this sniff does not contain an auto-fixer.
*
* @since 1.0.0
*/
class DisallowLogicalAndOrSniff implements Sniff
{

/**
* The tokens this sniff records metrics for.
*
* @since 1.0.0
*
* @var array
*/
private $metricType = [
\T_LOGICAL_AND => 'logical (and/or)',
\T_LOGICAL_OR => 'logical (and/or)',
\T_BOOLEAN_AND => 'boolean (&&/||)',
\T_BOOLEAN_OR => 'boolean (&&/||)',
];

/**
* The tokens this sniff targets with error code and replacements.
*
* @since 1.0.0
*
* @var array
*/
private $targetTokenInfo = [
\T_LOGICAL_AND => [
'error_code' => 'LogicalAnd',
'replacement' => '&&',
],
\T_LOGICAL_OR => [
'error_code' => 'LogicalOr',
'replacement' => '||',
],
];

/**
* Returns an array of tokens this test wants to listen for.
*
* @since 1.0.0
*
* @return array
*/
public function register()
{
return \array_keys($this->metricType);
}

/**
* 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();
$tokenCode = $tokens[$stackPtr]['code'];

$phpcsFile->recordMetric($stackPtr, 'Type of and/or operator used', $this->metricType[$tokenCode]);

if (isset($this->targetTokenInfo[$tokenCode]) === false) {
// Already using boolean operator.
return;
}

$error = 'Using logical operators is not allowed. Expected: "%s"; Found: "%s"';
$data = [
$this->targetTokenInfo[$tokenCode]['replacement'],
$tokens[ $stackPtr ]['content'],
];

$phpcsFile->addError($error, $stackPtr, $this->targetTokenInfo[$tokenCode]['error_code'], $data);
}
}
19 changes: 19 additions & 0 deletions Universal/Tests/Operators/DisallowLogicalAndOrUnitTest.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

/*
* This is ok.
*/
if ( true && $true ) {
echo 'True';
} elseif ($true || false) {
echo 'False';
}

/*
* Using Logical operators
*/
if ( true and $true ) {
echo 'True';
} elseif ($true or true) {
echo 'False';
}
47 changes: 47 additions & 0 deletions Universal/Tests/Operators/DisallowLogicalAndOrUnitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?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\Operators;

use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;

/**
* Unit test class for the DisallowLogicalAndOr sniff.
*
* @covers PHPCSExtra\Universal\Sniffs\Operators\DisallowLogicalAndOrSniff
*
* @since 1.0.0
*/
class DisallowLogicalAndOrUnitTest extends AbstractSniffUnitTest
{

/**
* Returns the lines where errors should occur.
*
* @return array<int, int>
*/
public function getErrorList()
{
return [
15 => 1,
17 => 1,
];
}

/**
* Returns the lines where warnings should occur.
*
* @return array<int, int>
*/
public function getWarningList()
{
return [];
}
}