Skip to content

Commit c3c5741

Browse files
authored
Merge pull request #327 from PHPCSStandards/feature/forbid-fqn-true-false-null
✨ New Universal.PHP.NoFQNTrueFalseNull sniff
2 parents cf28c84 + 53fa0b6 commit c3c5741

5 files changed

+227
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0"?>
2+
<documentation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="https://phpcsstandards.github.io/PHPCSDevTools/phpcsdocs.xsd"
4+
title="No FQN True False or Null"
5+
>
6+
<standard>
7+
<![CDATA[
8+
Forbids using `true`, `false` and `null` as fully qualified constants.
9+
]]>
10+
</standard>
11+
<code_comparison>
12+
<code title="Valid: Using true/false/null without namespace separator prefix.">
13+
<![CDATA[
14+
$a = <em>null</em>;
15+
16+
if ($something === <em>false</em>) {}
17+
]]>
18+
</code>
19+
<code title="Invalid: Using true/false/null as fully qualified constants.">
20+
<![CDATA[
21+
$a = <em>\</em>null;
22+
23+
if ($something === <em>\</em>false) {}
24+
]]>
25+
</code>
26+
</code_comparison>
27+
</documentation>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
/**
3+
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
4+
*
5+
* @package PHPCSExtra
6+
* @copyright 2020 PHPCSExtra Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCSStandards/PHPCSExtra
9+
*/
10+
11+
namespace PHPCSExtra\Universal\Sniffs\PHP;
12+
13+
use PHP_CodeSniffer\Files\File;
14+
use PHP_CodeSniffer\Sniffs\Sniff;
15+
use PHP_CodeSniffer\Util\Tokens;
16+
17+
/**
18+
* Forbids the use of `true`/`false`/`null` as fully qualified constants.
19+
*
20+
* @since 1.3.0
21+
*/
22+
final class NoFQNTrueFalseNullSniff implements Sniff
23+
{
24+
25+
/**
26+
* Registers the tokens that this sniff wants to listen for.
27+
*
28+
* @since 1.3.0
29+
*
30+
* @return array<int|string>
31+
*/
32+
public function register()
33+
{
34+
return [
35+
// PHP < 8.0.
36+
\T_TRUE,
37+
\T_FALSE,
38+
\T_NULL,
39+
40+
// PHP >= 8.0.
41+
\T_STRING,
42+
];
43+
}
44+
45+
/**
46+
* Processes this test, when one of its tokens is encountered.
47+
*
48+
* @since 1.3.0
49+
*
50+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
51+
* @param int $stackPtr The position of the current token
52+
* in the stack passed in $tokens.
53+
*
54+
* @return void
55+
*/
56+
public function process(File $phpcsFile, $stackPtr)
57+
{
58+
$tokens = $phpcsFile->getTokens();
59+
$content = $tokens[$stackPtr]['content'];
60+
$contentLC = \strtolower($content);
61+
62+
if ($contentLC !== 'true' && $contentLC !== 'false' && $contentLC !== 'null') {
63+
return;
64+
}
65+
66+
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
67+
if ($tokens[$prev]['code'] !== \T_NS_SEPARATOR) {
68+
return;
69+
}
70+
71+
$prevPrev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($prev - 1), null, true);
72+
if ($tokens[$prevPrev]['code'] === \T_STRING || $tokens[$prevPrev]['code'] === \T_NAMESPACE) {
73+
return;
74+
}
75+
76+
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true);
77+
if ($tokens[$next]['code'] === \T_NS_SEPARATOR) {
78+
return;
79+
}
80+
81+
$fix = $phpcsFile->addFixableError(
82+
'The special PHP constant "%s" should not be fully qualified.',
83+
$prev,
84+
'Found',
85+
[$contentLC]
86+
);
87+
88+
if ($fix === true) {
89+
$phpcsFile->fixer->replaceToken($prev, '');
90+
}
91+
}
92+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
$thisIsFine = true;
4+
$thisIsFine = False;
5+
$thisIsFine = NULL;
6+
7+
// Namespaced names should be left alone.
8+
$fullyQualified = \Fully\True\Qualified;
9+
$nsRelative = namespace\Null;
10+
$partiallyQualified = False\Something\Something;
11+
12+
/* These should all be flagged */
13+
$notOkay = \true;
14+
$notOkay = \false;
15+
$notOkay = \ null;
16+
17+
$notOkay = \TRUE;
18+
$notOkay = \/*comment*/False;
19+
$notOkay = \nULl;
20+
21+
class UseInTypes {
22+
const MyClass|\TRUE CONSTANT_NAME = new MyClass;
23+
24+
public readonly \False|float|\null $property;
25+
26+
public function foo(string|\false $name) : \NULL|int {}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
$thisIsFine = true;
4+
$thisIsFine = False;
5+
$thisIsFine = NULL;
6+
7+
// Namespaced names should be left alone.
8+
$fullyQualified = \Fully\True\Qualified;
9+
$nsRelative = namespace\Null;
10+
$partiallyQualified = False\Something\Something;
11+
12+
/* These should all be flagged */
13+
$notOkay = true;
14+
$notOkay = false;
15+
$notOkay = null;
16+
17+
$notOkay = TRUE;
18+
$notOkay = /*comment*/False;
19+
$notOkay = nULl;
20+
21+
class UseInTypes {
22+
const MyClass|TRUE CONSTANT_NAME = new MyClass;
23+
24+
public readonly False|float|null $property;
25+
26+
public function foo(string|false $name) : NULL|int {}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
/**
3+
* PHPCSExtra, a collection of sniffs and standards for use with PHP_CodeSniffer.
4+
*
5+
* @package PHPCSExtra
6+
* @copyright 2020 PHPCSExtra Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCSStandards/PHPCSExtra
9+
*/
10+
11+
namespace PHPCSExtra\Universal\Tests\PHP;
12+
13+
use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest;
14+
15+
/**
16+
* Unit test class for the NoFQNTrueFalseNull sniff.
17+
*
18+
* @covers PHPCSExtra\Universal\Sniffs\PHP\NoFQNTrueFalseNullSniff
19+
*
20+
* @since 1.3.0
21+
*/
22+
final class NoFQNTrueFalseNullUnitTest extends AbstractSniffUnitTest
23+
{
24+
25+
/**
26+
* Returns the lines where errors should occur.
27+
*
28+
* @return array<int, int> Key is the line number, value is the number of expected errors.
29+
*/
30+
public function getErrorList()
31+
{
32+
return [
33+
13 => 1,
34+
14 => 1,
35+
15 => 1,
36+
17 => 1,
37+
18 => 1,
38+
19 => 1,
39+
22 => 1,
40+
24 => 2,
41+
26 => 2,
42+
];
43+
}
44+
45+
/**
46+
* Returns the lines where warnings should occur.
47+
*
48+
* @return array<int, int> Key is the line number, value is the number of expected warnings.
49+
*/
50+
public function getWarningList()
51+
{
52+
return [];
53+
}
54+
}

0 commit comments

Comments
 (0)