implement JS password scoring, td_stock approve logicc

This commit is contained in:
Thanakorn S
2026-04-30 15:14:42 +07:00
parent db5c47b6ca
commit ac4fd9a5ad
3147 changed files with 1298 additions and 343 deletions
@@ -0,0 +1,23 @@
<documentation title="Short Array Syntax">
<standard>
<![CDATA[
Short array syntax must be used to define arrays.
]]>
</standard>
<code_comparison>
<code title="Valid: Short form of array.">
<![CDATA[
$arr = <em>[</em>
'foo' => 'bar',
<em>]</em>;
]]>
</code>
<code title="Invalid: Long form of array.">
<![CDATA[
$arr = <em>array(</em>
'foo' => 'bar',
<em>)</em>;
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Long Array Syntax">
<standard>
<![CDATA[
Long array syntax must be used to define arrays.
]]>
</standard>
<code_comparison>
<code title="Valid: Long form of array.">
<![CDATA[
$arr = <em>array(</em>
'foo' => 'bar',
<em>)</em>;
]]>
</code>
<code title="Invalid: Short form of array.">
<![CDATA[
$arr = <em>[</em>
'foo' => 'bar',
<em>]</em>;
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,27 @@
<documentation title="Duplicate Class Names">
<standard>
<![CDATA[
Class and Interface names should be unique in a project. They should never be duplicated.
]]>
</standard>
<code_comparison>
<code title="Valid: A unique class name.">
<![CDATA[
class <em>Foo</em>
{
}
]]>
</code>
<code title="Invalid: A class duplicated (including across multiple files).">
<![CDATA[
class <em>Foo</em>
{
}
class <em>Foo</em>
{
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,36 @@
<documentation title="Opening Brace on Same Line">
<standard>
<![CDATA[
The opening brace of a class must be on the same line after the definition and must be the last thing on that line.
]]>
</standard>
<code_comparison>
<code title="Valid: Opening brace on the same line.">
<![CDATA[
class Foo <em>{</em>
}
]]>
</code>
<code title="Invalid: Opening brace on the next line.">
<![CDATA[
class Foo
<em>{</em>
}
]]>
</code>
</code_comparison>
<code_comparison>
<code title="Valid: Opening brace is the last thing on the line.">
<![CDATA[
class Foo <em>{</em>
}
]]>
</code>
<code title="Invalid: Opening brace not last thing on the line.">
<![CDATA[
class Foo {<em> // Start of class.</em>
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Assignment In Condition">
<standard>
<![CDATA[
Variable assignments should not be made within conditions.
]]>
</standard>
<code_comparison>
<code title="Valid: A variable comparison being executed within a condition.">
<![CDATA[
if (<em>$test === 'abc'</em>) {
// Code.
}
]]>
</code>
<code title="Invalid: A variable assignment being made within a condition.">
<![CDATA[
if (<em>$test = 'abc'</em>) {
// Code.
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Empty Statements">
<standard>
<![CDATA[
Control Structures must have at least one statement inside of the body.
]]>
</standard>
<code_comparison>
<code title="Valid: There is a statement inside the control structure.">
<![CDATA[
if ($test) {
$var = 1;
}
]]>
</code>
<code title="Invalid: The control structure has no statements.">
<![CDATA[
if ($test) {
<em>// do nothing</em>
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Condition-Only For Loops">
<standard>
<![CDATA[
For loops that have only a second expression (the condition) should be converted to while loops.
]]>
</standard>
<code_comparison>
<code title="Valid: A for loop is used with all three expressions.">
<![CDATA[
for (<em>$i = 0</em>; $i < 10; <em>$i++</em>) {
echo "{$i}\n";
}
]]>
</code>
<code title="Invalid: A for loop is used without a first or third expression.">
<![CDATA[
for (<em></em>;$test;<em></em>) {
$test = doSomething();
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,24 @@
<documentation title="For Loops With Function Calls in the Test">
<standard>
<![CDATA[
For loops should not call functions inside the test for the loop when they can be computed beforehand.
]]>
</standard>
<code_comparison>
<code title="Valid: A for loop that determines its end condition before the loop starts.">
<![CDATA[
<em>$end = count($foo);</em>
for ($i = 0; $i < $end; $i++) {
echo $foo[$i]."\n";
}
]]>
</code>
<code title="Invalid: A for loop that unnecessarily computes the same value on every iteration.">
<![CDATA[
for ($i = 0; $i < <em>count($foo)</em>; $i++) {
echo $foo[$i]."\n";
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,25 @@
<documentation title="Jumbled Incrementers">
<standard>
<![CDATA[
Incrementers in nested loops should use different variable names.
]]>
</standard>
<code_comparison>
<code title="Valid: Two different variables being used to increment.">
<![CDATA[
for ($i = 0; $i < 10; <em>$i++</em>) {
for ($j = 0; $j < 10; <em>$j++</em>) {
}
}
]]>
</code>
<code title="Invalid: Inner incrementer is the same variable name as the outer one.">
<![CDATA[
for ($i = 0; $i < 10; <em>$i++</em>) {
for ($j = 0; $j < 10; <em>$i++</em>) {
}
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,39 @@
<documentation title="Unconditional If Statements">
<standard>
<![CDATA[
If statements that are always evaluated should not be used.
]]>
</standard>
<code_comparison>
<code title="Valid: An if statement that only executes conditionally.">
<![CDATA[
if (<em>$test</em>) {
$var = 1;
}
]]>
</code>
<code title="Invalid: An if statement that is always performed.">
<![CDATA[
if (<em>true</em>) {
$var = 1;
}
]]>
</code>
</code_comparison>
<code_comparison>
<code title="Valid: An if statement that only executes conditionally.">
<![CDATA[
if (<em>$test</em>) {
$var = 1;
}
]]>
</code>
<code title="Invalid: An if statement that is never performed.">
<![CDATA[
if (<em>false</em>) {
$var = 1;
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,29 @@
<documentation title="Unnecessary Final Modifiers">
<standard>
<![CDATA[
Methods should not be declared final inside of classes that are declared final.
]]>
</standard>
<code_comparison>
<code title="Valid: A method in a final class is not marked final.">
<![CDATA[
final class Foo
{
public function bar()
{
}
}
]]>
</code>
<code title="Invalid: A method in a final class is also marked final.">
<![CDATA[
final class Foo
{
public <em>final</em> function bar()
{
}
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,25 @@
<documentation title="Unused function parameters">
<standard>
<![CDATA[
All parameters in a functions signature should be used within the function.
]]>
</standard>
<code_comparison>
<code title="Valid: All the parameters are used.">
<![CDATA[
function addThree($a, $b, $c)
{
return <em>$a + $b + $c</em>;
}
]]>
</code>
<code title="Invalid: One of the parameters is not being used.">
<![CDATA[
function addThree($a, $b, $c)
{
return <em>$a + $b</em>;
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,32 @@
<documentation title="Useless Overriding Methods">
<standard>
<![CDATA[
Methods should not be defined that only call the parent method.
]]>
</standard>
<code_comparison>
<code title="Valid: A method that extends functionality on a parent method.">
<![CDATA[
final class Foo
{
public function bar()
{
parent::bar();
<em>$this->doSomethingElse();</em>
}
}
]]>
</code>
<code title="Invalid: An overriding method that only calls the parent.">
<![CDATA[
final class Foo
{
public function bar()
{
<em>parent::bar();</em>
}
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,25 @@
<documentation title="Fixme Comments">
<standard>
<![CDATA[
FIXME Statements should be taken care of.
]]>
</standard>
<code_comparison>
<code title="Valid: A comment without a fixme.">
<![CDATA[
// <em>Handle strange case</em>
if ($test) {
$var = 1;
}
]]>
</code>
<code title="Invalid: A fixme comment.">
<![CDATA[
// <em>FIXME</em>: This needs to be fixed!
if ($test) {
$var = 1;
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,25 @@
<documentation title="Todo Comments">
<standard>
<![CDATA[
TODO Statements should be taken care of.
]]>
</standard>
<code_comparison>
<code title="Valid: A comment without a todo.">
<![CDATA[
// <em>Handle strange case</em>
if ($test) {
$var = 1;
}
]]>
</code>
<code title="Invalid: A todo comment.">
<![CDATA[
// <em>TODO</em>: This needs to be fixed!
if ($test) {
$var = 1;
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Disallow Yoda conditions">
<standard>
<![CDATA[
Yoda conditions are disallowed.
]]>
</standard>
<code_comparison>
<code title="Valid: value to be asserted must go on the right side of the comparison.">
<![CDATA[
if ($test === null) <em>{</em>
$var = 1;
<em>}</em>
]]>
</code>
<code title="Invalid: value to be asserted must not be on the left.">
<![CDATA[
if (null === $test) <em>{</em>
$var = 1;
<em>}</em>
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,22 @@
<documentation title="Inline Control Structures">
<standard>
<![CDATA[
Control Structures should use braces.
]]>
</standard>
<code_comparison>
<code title="Valid: Braces are used around the control structure.">
<![CDATA[
if ($test) <em>{</em>
$var = 1;
<em>}</em>
]]>
</code>
<code title="Invalid: No braces are used for the control structure..">
<![CDATA[
if ($test)
$var = 1;
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,19 @@
<documentation title="CSSLint">
<standard>
<![CDATA[
All css files should pass the basic csslint tests.
]]>
</standard>
<code_comparison>
<code title="Valid: Valid CSS Syntax is used.">
<![CDATA[
.foo: { width: 100<em></em>%; }
]]>
</code>
<code title="Invalid: The CSS has a typo in it.">
<![CDATA[
.foo: { width: 100<em> </em>%; }
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,19 @@
<documentation title="Closure Linter">
<standard>
<![CDATA[
All javascript files should pass basic Closure Linter tests.
]]>
</standard>
<code_comparison>
<code title="Valid: Valid JS Syntax is used.">
<![CDATA[
var foo = [1, 2<em></em>];
]]>
</code>
<code title="Invalid: Trailing comma in a javascript array.">
<![CDATA[
var foo = [1, 2<em>,</em>];
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,19 @@
<documentation title="JSHint">
<standard>
<![CDATA[
All javascript files should pass basic JSHint tests.
]]>
</standard>
<code_comparison>
<code title="Valid: Valid JS Syntax is used.">
<![CDATA[
<em>var</em> foo = 5;
]]>
</code>
<code title="Invalid: The Javascript is using an undefined variable.">
<![CDATA[
<em></em>foo = 5;
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="Byte Order Marks">
<standard>
<![CDATA[
Byte Order Marks that may corrupt your application should not be used. These include 0xefbbbf (UTF-8), 0xfeff (UTF-16 BE) and 0xfffe (UTF-16 LE).
]]>
</standard>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="End of File Newline">
<standard>
<![CDATA[
Files should end with a newline character.
]]>
</standard>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="No End of File Newline">
<standard>
<![CDATA[
Files should not end with a newline character.
]]>
</standard>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="Executable Files">
<standard>
<![CDATA[
Files should not be executable.
]]>
</standard>
</documentation>
@@ -0,0 +1,24 @@
<documentation title="Inline HTML">
<standard>
<![CDATA[
Files that contain php code should only have php code and should not have any "inline html".
]]>
</standard>
<code_comparison>
<code title="Valid: A php file with only php code in it.">
<![CDATA[
<?php
$foo = 'bar';
echo $foo . 'baz';
]]>
</code>
<code title="Invalid: A php file with html in it outside of the php tags.">
<![CDATA[
<em>some string here</em>
<?php
$foo = 'bar';
echo $foo . 'baz';
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="Line Endings">
<standard>
<![CDATA[
Unix-style line endings are preferred ("\n" instead of "\r\n").
]]>
</standard>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="Line Length">
<standard>
<![CDATA[
It is recommended to keep lines at approximately 80 characters long for better code readability.
]]>
</standard>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="Lowercased Filenames">
<standard>
<![CDATA[
Lowercase filenames are required.
]]>
</standard>
</documentation>
@@ -0,0 +1,29 @@
<documentation title="One Class Per File">
<standard>
<![CDATA[
There should only be one class defined in a file.
]]>
</standard>
<code_comparison>
<code title="Valid: Only one class in the file.">
<![CDATA[
<?php
<em>class Foo</em>
{
}
]]>
</code>
<code title="Invalid: Multiple classes defined in one file.">
<![CDATA[
<?php
<em>class Foo</em>
{
}
<em>class Bar</em>
{
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,29 @@
<documentation title="One Interface Per File">
<standard>
<![CDATA[
There should only be one interface defined in a file.
]]>
</standard>
<code_comparison>
<code title="Valid: Only one interface in the file.">
<![CDATA[
<?php
<em>interface Foo</em>
{
}
]]>
</code>
<code title="Invalid: Multiple interfaces defined in one file.">
<![CDATA[
<?php
<em>interface Foo</em>
{
}
<em>interface Bar</em>
{
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,29 @@
<documentation title="One Object Structure Per File">
<standard>
<![CDATA[
There should only be one class or interface or trait defined in a file.
]]>
</standard>
<code_comparison>
<code title="Valid: Only one object structure in the file.">
<![CDATA[
<?php
<em>trait Foo</em>
{
}
]]>
</code>
<code title="Invalid: Multiple object structures defined in one file.">
<![CDATA[
<?php
<em>trait Foo</em>
{
}
<em>class Bar</em>
{
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,29 @@
<documentation title="One Trait Per File">
<standard>
<![CDATA[
There should only be one trait defined in a file.
]]>
</standard>
<code_comparison>
<code title="Valid: Only one trait in the file.">
<![CDATA[
<?php
<em>trait Foo</em>
{
}
]]>
</code>
<code title="Invalid: Multiple traits defined in one file.">
<![CDATA[
<?php
<em>trait Foo</em>
{
}
<em>trait Bar</em>
{
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,20 @@
<documentation title="Multiple Statements On a Single Line">
<standard>
<![CDATA[
Multiple statements are not allowed on a single line.
]]>
</standard>
<code_comparison>
<code title="Valid: Two statements are spread out on two separate lines.">
<![CDATA[
$foo = 1;
$bar = 2;
]]>
</code>
<code title="Invalid: Two statements are combined onto one line.">
<![CDATA[
$foo = 1; $bar = 2;
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,56 @@
<documentation title="Aligning Blocks of Assignments">
<standard>
<![CDATA[
There should be one space on either side of an equals sign used to assign a value to a variable. In the case of a block of related assignments, more space may be inserted to promote readability.
]]>
</standard>
<code_comparison>
<code title="Equals signs aligned">
<![CDATA[
$shortVar <em>=</em> (1 + 2);
$veryLongVarName <em>=</em> 'string';
$var <em>=</em> foo($bar, $baz);
]]>
</code>
<code title="Not aligned; harder to read">
<![CDATA[
$shortVar <em>=</em> (1 + 2);
$veryLongVarName <em>=</em> 'string';
$var <em>=</em> foo($bar, $baz);
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
When using plus-equals, minus-equals etc. still ensure the equals signs are aligned to one space after the longest variable name.
]]>
</standard>
<code_comparison>
<code title="Equals signs aligned; only one space after longest var name">
<![CDATA[
$shortVar <em>+= </em>1;
$veryLongVarName<em> = </em>1;
]]>
</code>
<code title="Two spaces after longest var name">
<![CDATA[
$shortVar <em> += </em>1;
$veryLongVarName<em> = </em>1;
]]>
</code>
</code_comparison>
<code_comparison>
<code title="Equals signs aligned">
<![CDATA[
$shortVar <em> = </em>1;
$veryLongVarName<em> -= </em>1;
]]>
</code>
<code title="Equals signs not aligned">
<![CDATA[
$shortVar <em> = </em>1;
$veryLongVarName<em> -= </em>1;
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,19 @@
<documentation title="Space After Casts">
<standard>
<![CDATA[
Spaces are not allowed after casting operators.
]]>
</standard>
<code_comparison>
<code title="Valid: A cast operator is immediately before its value.">
<![CDATA[
$foo = (string)1;
]]>
</code>
<code title="Invalid: A cast operator is followed by whitespace.">
<![CDATA[
$foo = (string)<em> </em>1;
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,19 @@
<documentation title="Space After Casts">
<standard>
<![CDATA[
Exactly one space is allowed after a cast.
]]>
</standard>
<code_comparison>
<code title="Valid: A cast operator is followed by one space.">
<![CDATA[
$foo = (string)<em> </em>1;
]]>
</code>
<code title="Invalid: A cast operator is not followed by whitespace.">
<![CDATA[
$foo = (string)<em></em>1;
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,22 @@
<documentation title="Space After NOT operator">
<standard>
<![CDATA[
Exactly one space is allowed after the NOT operator.
]]>
</standard>
<code_comparison>
<code title="Valid: A NOT operator followed by one space.">
<![CDATA[
if (!<em> </em>$someVar || !<em> </em>$x instanceOf stdClass) {};
]]>
</code>
<code title="Invalid: A NOT operator not followed by whitespace or followed by too much whitespace.">
<![CDATA[
if (!<em></em>$someVar || !<em></em>$x instanceOf stdClass) {};
if (!<em> </em>$someVar || !<em>
</em>$x instanceOf stdClass) {};
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,31 @@
<documentation title="Call-Time Pass-By-Reference">
<standard>
<![CDATA[
Call-time pass-by-reference is not allowed. It should be declared in the function definition.
]]>
</standard>
<code_comparison>
<code title="Valid: Pass-by-reference is specified in the function definition.">
<![CDATA[
function foo(<em>&</em>$bar)
{
$bar++;
}
$baz = 1;
foo($baz);
]]>
</code>
<code title="Invalid: Pass-by-reference is done in the call to a function.">
<![CDATA[
function foo($bar)
{
$bar++;
}
$baz = 1;
foo(<em>&</em>$baz);
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,39 @@
<documentation title="Function Argument Spacing">
<standard>
<![CDATA[
Function arguments should have one space after a comma, and single spaces surrounding the equals sign for default values.
]]>
</standard>
<code_comparison>
<code title="Valid: Single spaces after a comma.">
<![CDATA[
function foo($bar,<em> </em>$baz)
{
}
]]>
</code>
<code title="Invalid: No spaces after a comma.">
<![CDATA[
function foo($bar,<em></em>$baz)
{
}
]]>
</code>
</code_comparison>
<code_comparison>
<code title="Valid: Single spaces around an equals sign in function declaration.">
<![CDATA[
function foo($bar, $baz<em> </em>=<em> </em>true)
{
}
]]>
</code>
<code title="Invalid: No spaces around an equals sign in function declaration.">
<![CDATA[
function foo($bar, $baz<em></em>=<em></em>true)
{
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,24 @@
<documentation title="Opening Brace in Function Declarations">
<standard>
<![CDATA[
Function declarations follow the "BSD/Allman style". The function brace is on the line following the function declaration and is indented to the same column as the start of the function declaration.
]]>
</standard>
<code_comparison>
<code title="Valid: brace on next line">
<![CDATA[
function fooFunction($arg1, $arg2 = '')
<em>{</em>
...
}
]]>
</code>
<code title="Invalid: brace on same line">
<![CDATA[
function fooFunction($arg1, $arg2 = '') <em>{</em>
...
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,24 @@
<documentation title="Opening Brace in Function Declarations">
<standard>
<![CDATA[
Function declarations follow the "Kernighan/Ritchie style". The function brace is on the same line as the function declaration. One space is required between the closing parenthesis and the brace.
]]>
</standard>
<code_comparison>
<code title="Valid: brace on same line">
<![CDATA[
function fooFunction($arg1, $arg2 = '')<em> {</em>
...
}
]]>
</code>
<code title="Invalid: brace on next line">
<![CDATA[
function fooFunction($arg1, $arg2 = '')
<em>{</em>
...
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="Cyclomatic Complexity">
<standard>
<![CDATA[
Functions should not have a cyclomatic complexity greater than 20, and should try to stay below a complexity of 10.
]]>
</standard>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="Nesting Level">
<standard>
<![CDATA[
Functions should not have a nesting level greater than 10, and should try to stay below 5.
]]>
</standard>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Abstract class name">
<standard>
<![CDATA[
Abstract class names must be prefixed with "Abstract", e.g. AbstractBar.
]]>
</standard>
<code_comparison>
<code title="Valid: ">
<![CDATA[
abstract class <em>AbstractBar</em>
{
}
]]>
</code>
<code title="Invalid: ">
<![CDATA[
abstract class <em>Bar</em>
{
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="camelCaps Function Names">
<standard>
<![CDATA[
Functions should use camelCaps format for their names. Only PHP's magic methods should use a double underscore prefix.
]]>
</standard>
<code_comparison>
<code title="Valid: A function in camelCaps format.">
<![CDATA[
function <em>doSomething</em>()
{
}
]]>
</code>
<code title="Invalid: A function in snake_case format.">
<![CDATA[
function <em>do_something</em>()
{
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,29 @@
<documentation title="Constructor name">
<standard>
<![CDATA[
Constructors should be named __construct, not after the class.
]]>
</standard>
<code_comparison>
<code title="Valid: The constructor is named __construct.">
<![CDATA[
class Foo
{
function <em>__construct</em>()
{
}
}
]]>
</code>
<code title="Invalid: The old style class name constructor is used.">
<![CDATA[
class Foo
{
function <em>Foo</em>()
{
}
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Interface name suffix">
<standard>
<![CDATA[
Interface names must be suffixed with "Interface", e.g. BarInterface.
]]>
</standard>
<code_comparison>
<code title="Valid: ">
<![CDATA[
interface <em>BarInterface</em>
{
}
]]>
</code>
<code title="Invalid: ">
<![CDATA[
interface <em>Bar</em>
{
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Trait name suffix">
<standard>
<![CDATA[
Trait names must be suffixed with "Trait", e.g. BarTrait.
]]>
</standard>
<code_comparison>
<code title="Valid: ">
<![CDATA[
trait <em>BarTrait</em>
{
}
]]>
</code>
<code title="Invalid: ">
<![CDATA[
trait <em>Bar</em>
{
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,29 @@
<documentation title="Constant Names">
<standard>
<![CDATA[
Constants should always be all-uppercase, with underscores to separate words.
]]>
</standard>
<code_comparison>
<code title="Valid: all uppercase">
<![CDATA[
define('<em>FOO_CONSTANT</em>', 'foo');
class FooClass
{
const <em>FOO_CONSTANT</em> = 'foo';
}
]]>
</code>
<code title="Invalid: mixed case">
<![CDATA[
define('<em>Foo_Constant</em>', 'foo');
class FooClass
{
const <em>foo_constant</em> = 'foo';
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="Backtick Operator">
<standard>
<![CDATA[
Disallow the use of the backtick operator for execution of shell commands.
]]>
</standard>
</documentation>
@@ -0,0 +1,22 @@
<documentation title="Opening Tag at Start of File">
<standard>
<![CDATA[
The opening php tag should be the first item in the file.
]]>
</standard>
<code_comparison>
<code title="Valid: A file starting with an opening php tag.">
<![CDATA[
<em></em><?php
echo 'Foo';
]]>
</code>
<code title="Invalid: A file with content before the opening php tag.">
<![CDATA[
<em>Beginning content</em>
<?php
echo 'Foo';
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,22 @@
<documentation title="Closing PHP Tags">
<standard>
<![CDATA[
All opening php tags should have a corresponding closing tag.
]]>
</standard>
<code_comparison>
<code title="Valid: A closing tag paired with it's opening tag.">
<![CDATA[
<em><?php</em>
echo 'Foo';
<em>?></em>
]]>
</code>
<code title="Invalid: No closing tag paired with the opening tag.">
<![CDATA[
<em><?php</em>
echo 'Foo';
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,19 @@
<documentation title="Deprecated Functions">
<standard>
<![CDATA[
Deprecated functions should not be used.
]]>
</standard>
<code_comparison>
<code title="Valid: A non-deprecated function is used.">
<![CDATA[
$foo = <em>explode</em>('a', $bar);
]]>
</code>
<code title="Invalid: A deprecated function is used.">
<![CDATA[
$foo = <em>split</em>('a', $bar);
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="Alternative PHP Code Tags">
<standard>
<![CDATA[
Always use <?php ?> to delimit PHP code, do not use the ASP <% %> style tags nor the <script language="php"></script> tags. This is the most portable way to include PHP code on differing operating systems and setups.
]]>
</standard>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="$_REQUEST Superglobal">
<standard>
<![CDATA[
$_REQUEST should never be used due to the ambiguity created as to where the data is coming from. Use $_POST, $_GET, or $_COOKIE instead.
]]>
</standard>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="PHP Code Tags">
<standard>
<![CDATA[
Always use <?php ?> to delimit PHP code, not the <? ?> shorthand. This is the most portable way to include PHP code on differing operating systems and setups.
]]>
</standard>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="Goto">
<standard>
<![CDATA[
Discourage the use of the PHP `goto` language construct.
]]>
</standard>
</documentation>
@@ -0,0 +1,19 @@
<documentation title="Forbidden Functions">
<standard>
<![CDATA[
The forbidden functions sizeof() and delete() should not be used.
]]>
</standard>
<code_comparison>
<code title="Valid: count() is used in place of sizeof().">
<![CDATA[
$foo = <em>count</em>($bar);
]]>
</code>
<code title="Invalid: sizeof() is used.">
<![CDATA[
$foo = <em>sizeof</em>($bar);
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Lowercase PHP Constants">
<standard>
<![CDATA[
The <em>true</em>, <em>false</em> and <em>null</em> constants must always be lowercase.
]]>
</standard>
<code_comparison>
<code title="Valid: lowercase constants">
<![CDATA[
if ($var === <em>false</em> || $var === <em>null</em>) {
$var = <em>true</em>;
}
]]>
</code>
<code title="Invalid: uppercase constants">
<![CDATA[
if ($var === <em>FALSE</em> || $var === <em>NULL</em>) {
$var = <em>TRUE</em>;
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,19 @@
<documentation title="Lowercase Keywords">
<standard>
<![CDATA[
All PHP keywords should be lowercase.
]]>
</standard>
<code_comparison>
<code title="Valid: Lowercase array keyword used.">
<![CDATA[
$foo = <em>array</em>();
]]>
</code>
<code title="Invalid: Non-lowercase array keyword used.">
<![CDATA[
$foo = <em>Array</em>();
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,38 @@
<documentation title="Lowercase PHP Types">
<standard>
<![CDATA[
All PHP types used for parameter type and return type declarations should be lowercase.
]]>
</standard>
<code_comparison>
<code title="Valid: Lowercase type declarations used.">
<![CDATA[
function myFunction(int $foo) : string {
}
]]>
</code>
<code title="Invalid: Non-lowercase type declarations used.">
<![CDATA[
function myFunction(<em>Int</em> $foo) : <em>STRING</em> {
}
]]>
</code>
</code_comparison>
<standard>
<![CDATA[
All PHP types used for type casting should be lowercase.
]]>
</standard>
<code_comparison>
<code title="Valid: Lowercase type used.">
<![CDATA[
$foo = (bool) $isValid;
]]>
</code>
<code title="Invalid: Non-lowercase type used.">
<![CDATA[
$foo = <em>(BOOL)</em> $isValid;
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Silenced Errors">
<standard>
<![CDATA[
Suppressing Errors is not allowed.
]]>
</standard>
<code_comparison>
<code title="Valid: isset() is used to verify that a variable exists before trying to use it.">
<![CDATA[
if (<em>isset($foo)</em> && $foo) {
echo "Hello\n";
}
]]>
</code>
<code title="Invalid: Errors are suppressed.">
<![CDATA[
if (<em>@</em>$foo) {
echo "Hello\n";
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="SAPI Usage">
<standard>
<![CDATA[
The PHP_SAPI constant should be used instead of php_sapi_name().
]]>
</standard>
<code_comparison>
<code title="Valid: PHP_SAPI is used.">
<![CDATA[
if (<em>PHP_SAPI</em> === 'cli') {
echo "Hello, CLI user.";
}
]]>
</code>
<code title="Invalid: php_sapi_name() is used.">
<![CDATA[
if (<em>php_sapi_name()</em> === 'cli') {
echo "Hello, CLI user.";
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Uppercase PHP Constants">
<standard>
<![CDATA[
The <em>true</em>, <em>false</em> and <em>null</em> constants must always be uppercase.
]]>
</standard>
<code_comparison>
<code title="Valid: uppercase constants">
<![CDATA[
if ($var === <em>FALSE</em> || $var === <em>NULL</em>) {
$var = <em>TRUE</em>;
}
]]>
</code>
<code title="Invalid: lowercase constants">
<![CDATA[
if ($var === <em>false</em> || $var === <em>null</em>) {
$var = <em>true</em>;
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,19 @@
<documentation title="Unnecessary String Concatenation">
<standard>
<![CDATA[
Strings should not be concatenated together.
]]>
</standard>
<code_comparison>
<code title="Valid: A string can be concatenated with an expression.">
<![CDATA[
echo '5 + 2 = ' . (5 + 2);
]]>
</code>
<code title="Invalid: Strings should not be concatenated together.">
<![CDATA[
echo 'Hello' . ' ' . 'World';
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="Subversion Properties">
<standard>
<![CDATA[
All php files in a subversion repository should have the svn:keywords property set to 'Author Id Revision' and the svn:eol-style property set to 'native'.
]]>
</standard>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Arbitrary Parentheses Spacing">
<standard>
<![CDATA[
Arbitrary sets of parentheses should have no spaces inside.
]]>
</standard>
<code_comparison>
<code title="Valid: no spaces on the inside of a set of arbitrary parentheses.">
<![CDATA[
$a = (null !== $extra);
]]>
</code>
<code title="Invalid: spaces or new lines on the inside of a set of arbitrary parentheses.">
<![CDATA[
$a = ( null !== $extra );
$a = (
null !== $extra
);
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="No Space Indentation">
<standard>
<![CDATA[
Tabs should be used for indentation instead of spaces.
]]>
</standard>
</documentation>
@@ -0,0 +1,7 @@
<documentation title="No Tab Indentation">
<standard>
<![CDATA[
Spaces should be used for indentation instead of tabs.
]]>
</standard>
</documentation>
@@ -0,0 +1,23 @@
<documentation title="Scope Indentation">
<standard>
<![CDATA[
Indentation for control structures, classes, and functions should be 4 spaces per level.
]]>
</standard>
<code_comparison>
<code title="Valid: 4 spaces are used to indent a control structure.">
<![CDATA[
if ($test) {
<em> </em>$var = 1;
}
]]>
</code>
<code title="Invalid: 8 spaces are used to indent a control structure.">
<![CDATA[
if ($test) {
<em> </em>$var = 1;
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,34 @@
<documentation title="Spacing After Spread Operator">
<standard>
<![CDATA[
There should be no space between the spread operator and the variable/function call it applies to.
]]>
</standard>
<code_comparison>
<code title="Valid: No space between the spread operator and the variable/function call it applies to.">
<![CDATA[
function foo(<em>&...$spread</em>) {
bar(<em>...$spread</em>);
bar(
[<em>...$foo</em>],
<em>...array_values($keyedArray)</em>
);
}
]]>
</code>
<code title="Invalid: space found between the spread operator and the variable/function call it applies to.">
<![CDATA[
function bar(<em>... </em>$spread) {
bar(<em>...
</em>$spread
);
bar(
[<em>... </em>$foo ],<em>.../*comment*/</em>array_values($keyedArray)
);
}
]]>
</code>
</code_comparison>
</documentation>
@@ -0,0 +1,177 @@
<?php
/**
* Ensures that array are indented one tab stop.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Arrays;
use PHP_CodeSniffer\Sniffs\AbstractArraySniff;
use PHP_CodeSniffer\Util\Tokens;
class ArrayIndentSniff extends AbstractArraySniff
{
/**
* The number of spaces each array key should be indented.
*
* @var integer
*/
public $indent = 4;
/**
* Processes a single-line array definition.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param int $arrayStart The token that starts the array definition.
* @param int $arrayEnd The token that ends the array definition.
* @param array $indices An array of token positions for the array keys,
* double arrows, and values.
*
* @return void
*/
public function processSingleLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices)
{
}//end processSingleLineArray()
/**
* Processes a multi-line array definition.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The current file being checked.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
* @param int $arrayStart The token that starts the array definition.
* @param int $arrayEnd The token that ends the array definition.
* @param array $indices An array of token positions for the array keys,
* double arrows, and values.
*
* @return void
*/
public function processMultiLineArray($phpcsFile, $stackPtr, $arrayStart, $arrayEnd, $indices)
{
$tokens = $phpcsFile->getTokens();
// Determine how far indented the entire array declaration should be.
$ignore = Tokens::$emptyTokens;
$ignore[] = T_DOUBLE_ARROW;
$ignore[] = T_COMMA;
$prev = $phpcsFile->findPrevious($ignore, ($stackPtr - 1), null, true);
$start = $phpcsFile->findStartOfStatement($prev);
$first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $start, true);
$baseIndent = ($tokens[$first]['column'] - 1);
$first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $stackPtr, true);
$startIndent = ($tokens[$first]['column'] - 1);
// If the open brace is not indented to at least to the level of the start
// of the statement, the sniff will conflict with other sniffs trying to
// check indent levels because it's not valid. But we don't enforce exactly
// how far indented it should be.
if ($startIndent < $baseIndent) {
$error = 'Array open brace not indented correctly; expected at least %s spaces but found %s';
$data = [
$baseIndent,
$startIndent,
];
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'OpenBraceIncorrect', $data);
if ($fix === true) {
$padding = str_repeat(' ', $baseIndent);
if ($startIndent === 0) {
$phpcsFile->fixer->addContentBefore($first, $padding);
} else {
$phpcsFile->fixer->replaceToken(($first - 1), $padding);
}
}
return;
}//end if
$expectedIndent = ($startIndent + $this->indent);
foreach ($indices as $index) {
if (isset($index['index_start']) === true) {
$start = $index['index_start'];
} else {
$start = $index['value_start'];
}
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($start - 1), null, true);
if ($tokens[$prev]['line'] === $tokens[$start]['line']) {
// This index isn't the only content on the line
// so we can't check indent rules.
continue;
}
$first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $start, true);
$foundIndent = ($tokens[$first]['column'] - 1);
if ($foundIndent === $expectedIndent) {
continue;
}
$error = 'Array key not indented correctly; expected %s spaces but found %s';
$data = [
$expectedIndent,
$foundIndent,
];
$fix = $phpcsFile->addFixableError($error, $first, 'KeyIncorrect', $data);
if ($fix === false) {
continue;
}
$padding = str_repeat(' ', $expectedIndent);
if ($foundIndent === 0) {
$phpcsFile->fixer->addContentBefore($first, $padding);
} else {
$phpcsFile->fixer->replaceToken(($first - 1), $padding);
}
}//end foreach
$prev = $phpcsFile->findPrevious(T_WHITESPACE, ($arrayEnd - 1), null, true);
if ($tokens[$prev]['line'] === $tokens[$arrayEnd]['line']) {
$error = 'Closing brace of array declaration must be on a new line';
$fix = $phpcsFile->addFixableError($error, $arrayEnd, 'CloseBraceNotNewLine');
if ($fix === true) {
$padding = $phpcsFile->eolChar.str_repeat(' ', $expectedIndent);
$phpcsFile->fixer->addContentBefore($arrayEnd, $padding);
}
return;
}
// The close brace must be indented one stop less.
$expectedIndent -= $this->indent;
$foundIndent = ($tokens[$arrayEnd]['column'] - 1);
if ($foundIndent === $expectedIndent) {
return;
}
$error = 'Array close brace not indented correctly; expected %s spaces but found %s';
$data = [
$expectedIndent,
$foundIndent,
];
$fix = $phpcsFile->addFixableError($error, $arrayEnd, 'CloseBraceIncorrect', $data);
if ($fix === false) {
return;
}
$padding = str_repeat(' ', $expectedIndent);
if ($foundIndent === 0) {
$phpcsFile->fixer->addContentBefore($arrayEnd, $padding);
} else {
$phpcsFile->fixer->replaceToken(($arrayEnd - 1), $padding);
}
}//end processMultiLineArray()
}//end class
@@ -0,0 +1,78 @@
<?php
/**
* Bans the use of the PHP long array syntax.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class DisallowLongArraySyntaxSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return int[]
*/
public function register()
{
return [T_ARRAY];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'no');
$error = 'Short array syntax must be used to define arrays';
if (isset($tokens[$stackPtr]['parenthesis_opener']) === false
|| isset($tokens[$stackPtr]['parenthesis_closer']) === false
) {
// Live coding/parse error, just show the error, don't try and fix it.
$phpcsFile->addError($error, $stackPtr, 'Found');
return;
}
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
if ($fix === true) {
$opener = $tokens[$stackPtr]['parenthesis_opener'];
$closer = $tokens[$stackPtr]['parenthesis_closer'];
$phpcsFile->fixer->beginChangeset();
if ($opener === null) {
$phpcsFile->fixer->replaceToken($stackPtr, '[]');
} else {
$phpcsFile->fixer->replaceToken($stackPtr, '');
$phpcsFile->fixer->replaceToken($opener, '[');
$phpcsFile->fixer->replaceToken($closer, ']');
}
$phpcsFile->fixer->endChangeset();
}
}//end process()
}//end class
@@ -0,0 +1,61 @@
<?php
/**
* Bans the use of the PHP short array syntax.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Arrays;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class DisallowShortArraySyntaxSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return int[]
*/
public function register()
{
return [T_OPEN_SHORT_ARRAY];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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)
{
$phpcsFile->recordMetric($stackPtr, 'Short array syntax used', 'yes');
$error = 'Short array syntax is not allowed';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
if ($fix === true) {
$tokens = $phpcsFile->getTokens();
$opener = $tokens[$stackPtr]['bracket_opener'];
$closer = $tokens[$stackPtr]['bracket_closer'];
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($opener, 'array(');
$phpcsFile->fixer->replaceToken($closer, ')');
$phpcsFile->fixer->endChangeset();
}
}//end process()
}//end class
@@ -0,0 +1,118 @@
<?php
/**
* Reports errors if the same class or interface name is used in multiple files.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Classes;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class DuplicateClassNameSniff implements Sniff
{
/**
* List of classes that have been found during checking.
*
* @var array
*/
protected $foundClasses = [];
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return int[]
*/
public function register()
{
return [T_OPEN_TAG];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$namespace = '';
$findTokens = [
T_CLASS,
T_INTERFACE,
T_TRAIT,
T_ENUM,
T_NAMESPACE,
T_CLOSE_TAG,
];
$stackPtr = $phpcsFile->findNext($findTokens, ($stackPtr + 1));
while ($stackPtr !== false) {
if ($tokens[$stackPtr]['code'] === T_CLOSE_TAG) {
// We can stop here. The sniff will continue from the next open
// tag when PHPCS reaches that token, if there is one.
return;
}
// Keep track of what namespace we are in.
if ($tokens[$stackPtr]['code'] === T_NAMESPACE) {
$nsEnd = $phpcsFile->findNext(
[
T_NS_SEPARATOR,
T_STRING,
T_WHITESPACE,
],
($stackPtr + 1),
null,
true
);
$namespace = trim($phpcsFile->getTokensAsString(($stackPtr + 1), ($nsEnd - $stackPtr - 1)));
$stackPtr = $nsEnd;
} else {
$nameToken = $phpcsFile->findNext(T_STRING, $stackPtr);
$name = $tokens[$nameToken]['content'];
if ($namespace !== '') {
$name = $namespace.'\\'.$name;
}
$compareName = strtolower($name);
if (isset($this->foundClasses[$compareName]) === true) {
$type = strtolower($tokens[$stackPtr]['content']);
$file = $this->foundClasses[$compareName]['file'];
$line = $this->foundClasses[$compareName]['line'];
$error = 'Duplicate %s name "%s" found; first defined in %s on line %s';
$data = [
$type,
$name,
$file,
$line,
];
$phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
} else {
$this->foundClasses[$compareName] = [
'file' => $phpcsFile->getFilename(),
'line' => $tokens[$stackPtr]['line'],
];
}
}//end if
$stackPtr = $phpcsFile->findNext($findTokens, ($stackPtr + 1));
}//end while
}//end process()
}//end class
@@ -0,0 +1,124 @@
<?php
/**
* Checks that the opening brace of a class/interface/trait is on the same line as the class declaration.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Classes;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class OpeningBraceSameLineSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [
T_CLASS,
T_INTERFACE,
T_TRAIT,
T_ENUM,
];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$scopeIdentifier = $phpcsFile->findNext(T_STRING, ($stackPtr + 1));
$errorData = [strtolower($tokens[$stackPtr]['content']).' '.$tokens[$scopeIdentifier]['content']];
if (isset($tokens[$stackPtr]['scope_opener']) === false) {
$error = 'Possible parse error: %s missing opening or closing brace';
$phpcsFile->addWarning($error, $stackPtr, 'MissingBrace', $errorData);
return;
}
$openingBrace = $tokens[$stackPtr]['scope_opener'];
// Is the brace on the same line as the class/interface/trait declaration ?
$lastClassLineToken = $phpcsFile->findPrevious(T_WHITESPACE, ($openingBrace - 1), $stackPtr, true);
$lastClassLine = $tokens[$lastClassLineToken]['line'];
$braceLine = $tokens[$openingBrace]['line'];
$lineDifference = ($braceLine - $lastClassLine);
if ($lineDifference > 0) {
$phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'new line');
$error = 'Opening brace should be on the same line as the declaration for %s';
$fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceOnNewLine', $errorData);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->addContent($lastClassLineToken, ' {');
$phpcsFile->fixer->replaceToken($openingBrace, '');
$phpcsFile->fixer->endChangeset();
}
} else {
$phpcsFile->recordMetric($stackPtr, 'Class opening brace placement', 'same line');
}
// Is the opening brace the last thing on the line ?
$next = $phpcsFile->findNext(T_WHITESPACE, ($openingBrace + 1), null, true);
if ($tokens[$next]['line'] === $tokens[$openingBrace]['line']) {
if ($next === $tokens[$stackPtr]['scope_closer']) {
// Ignore empty classes.
return;
}
$error = 'Opening brace must be the last content on the line';
$fix = $phpcsFile->addFixableError($error, $openingBrace, 'ContentAfterBrace');
if ($fix === true) {
$phpcsFile->fixer->addNewline($openingBrace);
}
}
// Only continue checking if the opening brace looks good.
if ($lineDifference > 0) {
return;
}
// Is there precisely one space before the opening brace ?
if ($tokens[($openingBrace - 1)]['code'] !== T_WHITESPACE) {
$length = 0;
} else if ($tokens[($openingBrace - 1)]['content'] === "\t") {
$length = '\t';
} else {
$length = $tokens[($openingBrace - 1)]['length'];
}
if ($length !== 1) {
$error = 'Expected 1 space before opening brace; found %s';
$data = [$length];
$fix = $phpcsFile->addFixableError($error, $openingBrace, 'SpaceBeforeBrace', $data);
if ($fix === true) {
if ($length === 0 || $length === '\t') {
$phpcsFile->fixer->addContentBefore($openingBrace, ' ');
} else {
$phpcsFile->fixer->replaceToken(($openingBrace - 1), ' ');
}
}
}
}//end process()
}//end class
@@ -0,0 +1,171 @@
<?php
/**
* Detects variable assignments being made within conditions.
*
* This is a typical code smell and more often than not a comparison was intended.
*
* Note: this sniff does not detect variable assignments in the conditional part of ternaries!
*
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
* @copyright 2017 Juliette Reinders Folmer. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class AssignmentInConditionSniff implements Sniff
{
/**
* Assignment tokens to trigger on.
*
* Set in the register() method.
*
* @var array
*/
protected $assignmentTokens = [];
/**
* The tokens that indicate the start of a condition.
*
* @var array
*/
protected $conditionStartTokens = [];
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return int[]
*/
public function register()
{
$this->assignmentTokens = Tokens::$assignmentTokens;
unset($this->assignmentTokens[T_DOUBLE_ARROW]);
$starters = Tokens::$booleanOperators;
$starters[T_SEMICOLON] = T_SEMICOLON;
$starters[T_OPEN_PARENTHESIS] = T_OPEN_PARENTHESIS;
$this->conditionStartTokens = $starters;
return [
T_IF,
T_ELSEIF,
T_FOR,
T_SWITCH,
T_CASE,
T_WHILE,
T_MATCH,
];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$token = $tokens[$stackPtr];
// Find the condition opener/closer.
if ($token['code'] === T_FOR) {
if (isset($token['parenthesis_opener'], $token['parenthesis_closer']) === false) {
return;
}
$semicolon = $phpcsFile->findNext(T_SEMICOLON, ($token['parenthesis_opener'] + 1), ($token['parenthesis_closer']));
if ($semicolon === false) {
return;
}
$opener = $semicolon;
$semicolon = $phpcsFile->findNext(T_SEMICOLON, ($opener + 1), ($token['parenthesis_closer']));
if ($semicolon === false) {
return;
}
$closer = $semicolon;
unset($semicolon);
} else if ($token['code'] === T_CASE) {
if (isset($token['scope_opener']) === false) {
return;
}
$opener = $stackPtr;
$closer = $token['scope_opener'];
} else {
if (isset($token['parenthesis_opener'], $token['parenthesis_closer']) === false) {
return;
}
$opener = $token['parenthesis_opener'];
$closer = $token['parenthesis_closer'];
}//end if
$startPos = $opener;
do {
$hasAssignment = $phpcsFile->findNext($this->assignmentTokens, ($startPos + 1), $closer);
if ($hasAssignment === false) {
return;
}
// Examine whether the left side is a variable.
$hasVariable = false;
$conditionStart = $startPos;
$altConditionStart = $phpcsFile->findPrevious($this->conditionStartTokens, ($hasAssignment - 1), $startPos);
if ($altConditionStart !== false) {
$conditionStart = $altConditionStart;
}
for ($i = $hasAssignment; $i > $conditionStart; $i--) {
if (isset(Tokens::$emptyTokens[$tokens[$i]['code']]) === true) {
continue;
}
// If this is a variable or array, we've seen all we need to see.
if ($tokens[$i]['code'] === T_VARIABLE || $tokens[$i]['code'] === T_CLOSE_SQUARE_BRACKET) {
$hasVariable = true;
break;
}
// If this is a function call or something, we are OK.
if ($tokens[$i]['code'] === T_CLOSE_PARENTHESIS) {
break;
}
}
if ($hasVariable === true) {
$errorCode = 'Found';
if ($token['code'] === T_WHILE) {
$errorCode = 'FoundInWhileCondition';
}
$phpcsFile->addWarning(
'Variable assignment found within a condition. Did you mean to do a comparison ?',
$hasAssignment,
$errorCode
);
}
$startPos = $hasAssignment;
} while ($startPos < $closer);
}//end process()
}//end class
@@ -0,0 +1,162 @@
<?php
/**
* Checks against empty PHP statements.
*
* - Check against two semi-colons with no executable code in between.
* - Check against an empty PHP open - close tag combination.
*
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
* @copyright 2017 Juliette Reinders Folmer. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class EmptyPHPStatementSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return int[]
*/
public function register()
{
return [
T_SEMICOLON,
T_CLOSE_TAG,
];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
switch ($tokens[$stackPtr]['type']) {
// Detect `something();;`.
case 'T_SEMICOLON':
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($prevNonEmpty === false) {
return;
}
if ($tokens[$prevNonEmpty]['code'] !== T_SEMICOLON
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
) {
if (isset($tokens[$prevNonEmpty]['scope_condition']) === false) {
return;
}
if ($tokens[$prevNonEmpty]['scope_opener'] !== $prevNonEmpty
&& $tokens[$prevNonEmpty]['code'] !== T_CLOSE_CURLY_BRACKET
) {
return;
}
$scopeOwner = $tokens[$tokens[$prevNonEmpty]['scope_condition']]['code'];
if ($scopeOwner === T_CLOSURE || $scopeOwner === T_ANON_CLASS || $scopeOwner === T_MATCH) {
return;
}
// Else, it's something like `if (foo) {};` and the semi-colon is not needed.
}
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
$nested = $tokens[$stackPtr]['nested_parenthesis'];
$lastCloser = array_pop($nested);
if (isset($tokens[$lastCloser]['parenthesis_owner']) === true
&& $tokens[$tokens[$lastCloser]['parenthesis_owner']]['code'] === T_FOR
) {
// Empty for() condition.
return;
}
}
$fix = $phpcsFile->addFixableWarning(
'Empty PHP statement detected: superfluous semi-colon.',
$stackPtr,
'SemicolonWithoutCodeDetected'
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
if ($tokens[$prevNonEmpty]['code'] === T_OPEN_TAG
|| $tokens[$prevNonEmpty]['code'] === T_OPEN_TAG_WITH_ECHO
) {
// Check for superfluous whitespace after the semi-colon which will be
// removed as the `<?php ` open tag token already contains whitespace,
// either a space or a new line.
if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
$replacement = str_replace(' ', '', $tokens[($stackPtr + 1)]['content']);
$phpcsFile->fixer->replaceToken(($stackPtr + 1), $replacement);
}
}
for ($i = $stackPtr; $i > $prevNonEmpty; $i--) {
if ($tokens[$i]['code'] !== T_SEMICOLON
&& $tokens[$i]['code'] !== T_WHITESPACE
) {
break;
}
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}//end if
break;
// Detect `<?php ? >`.
case 'T_CLOSE_TAG':
$prevNonEmpty = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($prevNonEmpty === false
|| ($tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO)
) {
return;
}
$fix = $phpcsFile->addFixableWarning(
'Empty PHP open/close tag combination detected.',
$prevNonEmpty,
'EmptyPHPOpenCloseTagsDetected'
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = $prevNonEmpty; $i <= $stackPtr; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
break;
default:
// Deliberately left empty.
break;
}//end switch
}//end process()
}//end class
@@ -0,0 +1,97 @@
<?php
/**
* This sniff class detected empty statement.
*
* This sniff implements the common algorithm for empty statement body detection.
* A body is considered as empty if it is completely empty or it only contains
* whitespace characters and/or comments.
*
* <code>
* stmt {
* // foo
* }
* stmt (conditions) {
* // foo
* }
* </code>
*
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2007-2014 Manuel Pichler. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class EmptyStatementSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return int[]
*/
public function register()
{
return [
T_TRY,
T_CATCH,
T_FINALLY,
T_DO,
T_ELSE,
T_ELSEIF,
T_FOR,
T_FOREACH,
T_IF,
T_SWITCH,
T_WHILE,
T_MATCH,
];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$token = $tokens[$stackPtr];
// Skip statements without a body.
if (isset($token['scope_opener']) === false) {
return;
}
$next = $phpcsFile->findNext(
Tokens::$emptyTokens,
($token['scope_opener'] + 1),
($token['scope_closer'] - 1),
true
);
if ($next !== false) {
return;
}
// Get token identifier.
$name = strtoupper($token['content']);
$error = 'Empty %s statement detected';
$phpcsFile->addError($error, $stackPtr, 'Detected'.ucfirst(strtolower($name)), [$name]);
}//end process()
}//end class
@@ -0,0 +1,91 @@
<?php
/**
* Detects for-loops that can be simplified to a while-loop.
*
* This rule is based on the PMD rule catalogue. Detects for-loops that can be
* simplified as a while-loop.
*
* <code>
* class Foo
* {
* public function bar($x)
* {
* for (;true;) true; // No Init or Update part, may as well be: while (true)
* }
* }
* </code>
*
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2014 Manuel Pichler. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class ForLoopShouldBeWhileLoopSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return int[]
*/
public function register()
{
return [T_FOR];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$token = $tokens[$stackPtr];
// Skip invalid statement.
if (isset($token['parenthesis_opener']) === false) {
return;
}
$next = ++$token['parenthesis_opener'];
$end = --$token['parenthesis_closer'];
$parts = [
0,
0,
0,
];
$index = 0;
for (; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
if ($code === T_SEMICOLON) {
++$index;
} else if (isset(Tokens::$emptyTokens[$code]) === false) {
++$parts[$index];
}
}
if ($parts[0] === 0 && $parts[2] === 0 && $parts[1] > 0) {
$error = 'This FOR loop can be simplified to a WHILE loop';
$phpcsFile->addWarning($error, $stackPtr, 'CanSimplify');
}
}//end process()
}//end class
@@ -0,0 +1,101 @@
<?php
/**
* Detects for-loops that use a function call in the test expression.
*
* This rule is based on the PMD rule catalogue. Detects for-loops that use a
* function call in the test expression.
*
* <code>
* class Foo
* {
* public function bar($x)
* {
* $a = array(1, 2, 3, 4);
* for ($i = 0; $i < count($a); $i++) {
* $a[$i] *= $i;
* }
* }
* }
* </code>
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2014 Manuel Pichler. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class ForLoopWithTestFunctionCallSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return int[]
*/
public function register()
{
return [T_FOR];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$token = $tokens[$stackPtr];
// Skip invalid statement.
if (isset($token['parenthesis_opener']) === false) {
return;
}
$next = ++$token['parenthesis_opener'];
$end = --$token['parenthesis_closer'];
$position = 0;
for (; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
if ($code === T_SEMICOLON) {
++$position;
}
if ($position < 1) {
continue;
} else if ($position > 1) {
break;
} else if ($code !== T_VARIABLE && $code !== T_STRING) {
continue;
}
// Find next non empty token, if it is a open curly brace we have a
// function call.
$index = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
if ($tokens[$index]['code'] === T_OPEN_PARENTHESIS) {
$error = 'Avoid function calls in a FOR loop test part';
$phpcsFile->addWarning($error, $stackPtr, 'NotAllowed');
break;
}
}//end for
}//end process()
}//end class
@@ -0,0 +1,134 @@
<?php
/**
* Detects incrementer jumbling in for loops.
*
* This rule is based on the PMD rule catalogue. The jumbling incrementer sniff
* detects the usage of one and the same incrementer into an outer and an inner
* loop. Even it is intended this is confusing code.
*
* <code>
* class Foo
* {
* public function bar($x)
* {
* for ($i = 0; $i < 10; $i++)
* {
* for ($k = 0; $k < 20; $i++)
* {
* echo 'Hello';
* }
* }
* }
* }
* </code>
*
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2014 Manuel Pichler. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class JumbledIncrementerSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return int[]
*/
public function register()
{
return [T_FOR];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$token = $tokens[$stackPtr];
// Skip for-loop without body.
if (isset($token['scope_opener']) === false) {
return;
}
// Find incrementors for outer loop.
$outer = $this->findIncrementers($tokens, $token);
// Skip if empty.
if (count($outer) === 0) {
return;
}
// Find nested for loops.
$start = ++$token['scope_opener'];
$end = --$token['scope_closer'];
for (; $start <= $end; ++$start) {
if ($tokens[$start]['code'] !== T_FOR) {
continue;
}
$inner = $this->findIncrementers($tokens, $tokens[$start]);
$diff = array_intersect($outer, $inner);
if (count($diff) !== 0) {
$error = 'Loop incrementor (%s) jumbling with inner loop';
$data = [join(', ', $diff)];
$phpcsFile->addWarning($error, $stackPtr, 'Found', $data);
}
}
}//end process()
/**
* Get all used variables in the incrementer part of a for statement.
*
* @param array(integer=>array) $tokens Array with all code sniffer tokens.
* @param array(string=>mixed) $token Current for loop token
*
* @return string[] List of all found incrementer variables.
*/
protected function findIncrementers(array $tokens, array $token)
{
// Skip invalid statement.
if (isset($token['parenthesis_opener']) === false) {
return [];
}
$start = ++$token['parenthesis_opener'];
$end = --$token['parenthesis_closer'];
$incrementers = [];
$semicolons = 0;
for ($next = $start; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
if ($code === T_SEMICOLON) {
++$semicolons;
} else if ($semicolons === 2 && $code === T_VARIABLE) {
$incrementers[] = $tokens[$next]['content'];
}
}
return $incrementers;
}//end findIncrementers()
}//end class
@@ -0,0 +1,93 @@
<?php
/**
* Detects unconditional if- and elseif-statements.
*
* This rule is based on the PMD rule catalogue. The Unconditional If Statement
* sniff detects statement conditions that are only set to one of the constant
* values <b>true</b> or <b>false</b>
*
* <code>
* class Foo
* {
* public function close()
* {
* if (true)
* {
* // ...
* }
* }
* }
* </code>
*
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2014 Manuel Pichler. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class UnconditionalIfStatementSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return int[]
*/
public function register()
{
return [
T_IF,
T_ELSEIF,
];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$token = $tokens[$stackPtr];
// Skip if statement without body.
if (isset($token['parenthesis_opener']) === false) {
return;
}
$next = ++$token['parenthesis_opener'];
$end = --$token['parenthesis_closer'];
$goodCondition = false;
for (; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
if (isset(Tokens::$emptyTokens[$code]) === true) {
continue;
} else if ($code !== T_TRUE && $code !== T_FALSE) {
$goodCondition = true;
}
}
if ($goodCondition === false) {
$error = 'Avoid IF statements that are always true or false';
$phpcsFile->addWarning($error, $stackPtr, 'Found');
}
}//end process()
}//end class
@@ -0,0 +1,85 @@
<?php
/**
* Detects unnecessary final modifiers inside of final classes.
*
* This rule is based on the PMD rule catalogue. The Unnecessary Final Modifier
* sniff detects the use of the final modifier inside of a final class which
* is unnecessary.
*
* <code>
* final class Foo
* {
* public final function bar()
* {
* }
* }
* </code>
*
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2014 Manuel Pichler. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class UnnecessaryFinalModifierSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return int[]
*/
public function register()
{
return [T_CLASS];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$token = $tokens[$stackPtr];
// Skip for-statements without body.
if (isset($token['scope_opener']) === false) {
return;
}
// Fetch previous token.
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
// Skip for non final class.
if ($prev === false || $tokens[$prev]['code'] !== T_FINAL) {
return;
}
$next = ++$token['scope_opener'];
$end = --$token['scope_closer'];
for (; $next <= $end; ++$next) {
if ($tokens[$next]['code'] === T_FINAL) {
$error = 'Unnecessary FINAL modifier in FINAL class';
$phpcsFile->addWarning($error, $next, 'Found');
}
}
}//end process()
}//end class
@@ -0,0 +1,265 @@
<?php
/**
* Checks for unused function parameters.
*
* This sniff checks that all function parameters are used in the function body.
* One exception is made for empty function bodies or function bodies that only
* contain comments. This could be useful for the classes that implement an
* interface that defines multiple methods but the implementation only needs some
* of them.
*
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2007-2014 Manuel Pichler. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class UnusedFunctionParameterSniff implements Sniff
{
/**
* The list of class type hints which will be ignored.
*
* @var array
*/
public $ignoreTypeHints = [];
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [
T_FUNCTION,
T_CLOSURE,
T_FN,
];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$token = $tokens[$stackPtr];
// Skip broken function declarations.
if (isset($token['scope_opener']) === false || isset($token['parenthesis_opener']) === false) {
return;
}
$errorCode = 'Found';
$implements = false;
$extends = false;
$classPtr = $phpcsFile->getCondition($stackPtr, T_CLASS);
if ($classPtr !== false) {
$implements = $phpcsFile->findImplementedInterfaceNames($classPtr);
$extends = $phpcsFile->findExtendedClassName($classPtr);
if ($extends !== false) {
$errorCode .= 'InExtendedClass';
} else if ($implements !== false) {
$errorCode .= 'InImplementedInterface';
}
}
$params = [];
$methodParams = $phpcsFile->getMethodParameters($stackPtr);
// Skip when no parameters found.
$methodParamsCount = count($methodParams);
if ($methodParamsCount === 0) {
return;
}
foreach ($methodParams as $param) {
if (isset($param['property_visibility']) === true) {
// Ignore constructor property promotion.
continue;
}
$params[$param['name']] = $stackPtr;
}
$next = ++$token['scope_opener'];
$end = --$token['scope_closer'];
// Check the end token for arrow functions as
// they can end at a content token due to not having
// a clearly defined closing token.
if ($token['code'] === T_FN) {
++$end;
}
$foundContent = false;
$validTokens = [
T_HEREDOC => T_HEREDOC,
T_NOWDOC => T_NOWDOC,
T_END_HEREDOC => T_END_HEREDOC,
T_END_NOWDOC => T_END_NOWDOC,
T_DOUBLE_QUOTED_STRING => T_DOUBLE_QUOTED_STRING,
];
$validTokens += Tokens::$emptyTokens;
for (; $next <= $end; ++$next) {
$token = $tokens[$next];
$code = $token['code'];
// Ignorable tokens.
if (isset(Tokens::$emptyTokens[$code]) === true) {
continue;
}
if ($foundContent === false) {
// A throw statement as the first content indicates an interface method.
if ($code === T_THROW && $implements !== false) {
return;
}
// A return statement as the first content indicates an interface method.
if ($code === T_RETURN) {
$tmp = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
if ($tmp === false && $implements !== false) {
return;
}
// There is a return.
if ($tokens[$tmp]['code'] === T_SEMICOLON && $implements !== false) {
return;
}
$tmp = $phpcsFile->findNext(Tokens::$emptyTokens, ($tmp + 1), null, true);
if ($tmp !== false && $tokens[$tmp]['code'] === T_SEMICOLON && $implements !== false) {
// There is a return <token>.
return;
}
}//end if
}//end if
$foundContent = true;
if ($code === T_VARIABLE && isset($params[$token['content']]) === true) {
unset($params[$token['content']]);
} else if ($code === T_DOLLAR) {
$nextToken = $phpcsFile->findNext(T_WHITESPACE, ($next + 1), null, true);
if ($tokens[$nextToken]['code'] === T_OPEN_CURLY_BRACKET) {
$nextToken = $phpcsFile->findNext(T_WHITESPACE, ($nextToken + 1), null, true);
if ($tokens[$nextToken]['code'] === T_STRING) {
$varContent = '$'.$tokens[$nextToken]['content'];
if (isset($params[$varContent]) === true) {
unset($params[$varContent]);
}
}
}
} else if ($code === T_DOUBLE_QUOTED_STRING
|| $code === T_START_HEREDOC
|| $code === T_START_NOWDOC
) {
// Tokenize strings that can contain variables.
// Make sure the string is re-joined if it occurs over multiple lines.
$content = $token['content'];
for ($i = ($next + 1); $i <= $end; $i++) {
if (isset($validTokens[$tokens[$i]['code']]) === true) {
$content .= $tokens[$i]['content'];
$next++;
} else {
break;
}
}
$stringTokens = token_get_all(sprintf('<?php %s;?>', $content));
foreach ($stringTokens as $stringPtr => $stringToken) {
if (is_array($stringToken) === false) {
continue;
}
$varContent = '';
if ($stringToken[0] === T_DOLLAR_OPEN_CURLY_BRACES) {
$varContent = '$'.$stringTokens[($stringPtr + 1)][1];
} else if ($stringToken[0] === T_VARIABLE) {
$varContent = $stringToken[1];
}
if ($varContent !== '' && isset($params[$varContent]) === true) {
unset($params[$varContent]);
}
}
}//end if
}//end for
if ($foundContent === true && count($params) > 0) {
$error = 'The method parameter %s is never used';
// If there is only one parameter and it is unused, no need for additional errorcode toggling logic.
if ($methodParamsCount === 1) {
foreach ($params as $paramName => $position) {
if (in_array($methodParams[0]['type_hint'], $this->ignoreTypeHints, true) === true) {
continue;
}
$data = [$paramName];
$phpcsFile->addWarning($error, $position, $errorCode, $data);
}
return;
}
$foundLastUsed = false;
$lastIndex = ($methodParamsCount - 1);
$errorInfo = [];
for ($i = $lastIndex; $i >= 0; --$i) {
if ($foundLastUsed !== false) {
if (isset($params[$methodParams[$i]['name']]) === true) {
$errorInfo[$methodParams[$i]['name']] = [
'position' => $params[$methodParams[$i]['name']],
'errorcode' => $errorCode.'BeforeLastUsed',
'typehint' => $methodParams[$i]['type_hint'],
];
}
} else {
if (isset($params[$methodParams[$i]['name']]) === false) {
$foundLastUsed = true;
} else {
$errorInfo[$methodParams[$i]['name']] = [
'position' => $params[$methodParams[$i]['name']],
'errorcode' => $errorCode.'AfterLastUsed',
'typehint' => $methodParams[$i]['type_hint'],
];
}
}
}//end for
if (count($errorInfo) > 0) {
$errorInfo = array_reverse($errorInfo);
foreach ($errorInfo as $paramName => $info) {
if (in_array($info['typehint'], $this->ignoreTypeHints, true) === true) {
continue;
}
$data = [$paramName];
$phpcsFile->addWarning($error, $info['position'], $info['errorcode'], $data);
}
}
}//end if
}//end process()
}//end class
@@ -0,0 +1,161 @@
<?php
/**
* Detects unnecessary overridden methods that simply call their parent.
*
* This rule is based on the PMD rule catalogue. The Useless Overriding Method
* sniff detects the use of methods that only call their parent class's method
* with the same name and arguments. These methods are not required.
*
* <code>
* class FooBar {
* public function __construct($a, $b) {
* parent::__construct($a, $b);
* }
* }
* </code>
*
* @author Manuel Pichler <mapi@manuel-pichler.de>
* @copyright 2007-2014 Manuel Pichler. All rights reserved.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class UselessOverridingMethodSniff implements Sniff
{
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return int[]
*/
public function register()
{
return [T_FUNCTION];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$token = $tokens[$stackPtr];
// Skip function without body.
if (isset($token['scope_opener']) === false) {
return;
}
// Get function name.
$methodName = $phpcsFile->getDeclarationName($stackPtr);
// Get all parameters from method signature.
$signature = [];
foreach ($phpcsFile->getMethodParameters($stackPtr) as $param) {
$signature[] = $param['name'];
}
$next = ++$token['scope_opener'];
$end = --$token['scope_closer'];
for (; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
if (isset(Tokens::$emptyTokens[$code]) === true) {
continue;
} else if ($code === T_RETURN) {
continue;
}
break;
}
// Any token except 'parent' indicates correct code.
if ($tokens[$next]['code'] !== T_PARENT) {
return;
}
// Find next non empty token index, should be double colon.
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
// Skip for invalid code.
if ($next === false || $tokens[$next]['code'] !== T_DOUBLE_COLON) {
return;
}
// Find next non empty token index, should be the function name.
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
// Skip for invalid code or other method.
if ($next === false || $tokens[$next]['content'] !== $methodName) {
return;
}
// Find next non empty token index, should be the open parenthesis.
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
// Skip for invalid code.
if ($next === false || $tokens[$next]['code'] !== T_OPEN_PARENTHESIS) {
return;
}
$parameters = [''];
$parenthesisCount = 1;
$count = count($tokens);
for (++$next; $next < $count; ++$next) {
$code = $tokens[$next]['code'];
if ($code === T_OPEN_PARENTHESIS) {
++$parenthesisCount;
} else if ($code === T_CLOSE_PARENTHESIS) {
--$parenthesisCount;
} else if ($parenthesisCount === 1 && $code === T_COMMA) {
$parameters[] = '';
} else if (isset(Tokens::$emptyTokens[$code]) === false) {
$parameters[(count($parameters) - 1)] .= $tokens[$next]['content'];
}
if ($parenthesisCount === 0) {
break;
}
}//end for
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($next + 1), null, true);
if ($next === false || $tokens[$next]['code'] !== T_SEMICOLON) {
return;
}
// Check rest of the scope.
for (++$next; $next <= $end; ++$next) {
$code = $tokens[$next]['code'];
// Skip for any other content.
if (isset(Tokens::$emptyTokens[$code]) === false) {
return;
}
}
$parameters = array_map('trim', $parameters);
$parameters = array_filter($parameters);
if (count($parameters) === count($signature) && $parameters === $signature) {
$phpcsFile->addWarning('Possible useless method overriding detected', $stackPtr, 'Found');
}
}//end process()
}//end class
@@ -0,0 +1,353 @@
<?php
/**
* Ensures doc blocks follow basic formatting.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Commenting;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class DocCommentSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = [
'PHP',
'JS',
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_DOC_COMMENT_OPEN_TAG];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
if (isset($tokens[$stackPtr]['comment_closer']) === false
|| ($tokens[$tokens[$stackPtr]['comment_closer']]['content'] === ''
&& $tokens[$stackPtr]['comment_closer'] === ($phpcsFile->numTokens - 1))
) {
// Don't process an unfinished comment during live coding.
return;
}
$commentStart = $stackPtr;
$commentEnd = $tokens[$stackPtr]['comment_closer'];
$empty = [
T_DOC_COMMENT_WHITESPACE,
T_DOC_COMMENT_STAR,
];
$short = $phpcsFile->findNext($empty, ($stackPtr + 1), $commentEnd, true);
if ($short === false) {
// No content at all.
$error = 'Doc comment is empty';
$phpcsFile->addError($error, $stackPtr, 'Empty');
return;
}
// The first line of the comment should just be the /** code.
if ($tokens[$short]['line'] === $tokens[$stackPtr]['line']) {
$error = 'The open comment tag must be the only content on the line';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'ContentAfterOpen');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->addNewline($stackPtr);
$phpcsFile->fixer->addContentBefore($short, '* ');
$phpcsFile->fixer->endChangeset();
}
}
// The last line of the comment should just be the */ code.
$prev = $phpcsFile->findPrevious($empty, ($commentEnd - 1), $stackPtr, true);
if ($tokens[$prev]['line'] === $tokens[$commentEnd]['line']) {
$error = 'The close comment tag must be the only content on the line';
$fix = $phpcsFile->addFixableError($error, $commentEnd, 'ContentBeforeClose');
if ($fix === true) {
$phpcsFile->fixer->addNewlineBefore($commentEnd);
}
}
// Check for additional blank lines at the end of the comment.
if ($tokens[$prev]['line'] < ($tokens[$commentEnd]['line'] - 1)) {
$error = 'Additional blank lines found at end of doc comment';
$fix = $phpcsFile->addFixableError($error, $commentEnd, 'SpacingAfter');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = ($prev + 1); $i < $commentEnd; $i++) {
if ($tokens[($i + 1)]['line'] === $tokens[$commentEnd]['line']) {
break;
}
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
}
// Check for a comment description.
if ($tokens[$short]['code'] !== T_DOC_COMMENT_STRING) {
$error = 'Missing short description in doc comment';
$phpcsFile->addError($error, $stackPtr, 'MissingShort');
} else {
// No extra newline before short description.
if ($tokens[$short]['line'] !== ($tokens[$stackPtr]['line'] + 1)) {
$error = 'Doc comment short description must be on the first line';
$fix = $phpcsFile->addFixableError($error, $short, 'SpacingBeforeShort');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = $stackPtr; $i < $short; $i++) {
if ($tokens[$i]['line'] === $tokens[$stackPtr]['line']) {
continue;
} else if ($tokens[$i]['line'] === $tokens[$short]['line']) {
break;
}
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
}
// Account for the fact that a short description might cover
// multiple lines.
$shortContent = $tokens[$short]['content'];
$shortEnd = $short;
for ($i = ($short + 1); $i < $commentEnd; $i++) {
if ($tokens[$i]['code'] === T_DOC_COMMENT_STRING) {
if ($tokens[$i]['line'] === ($tokens[$shortEnd]['line'] + 1)) {
$shortContent .= $tokens[$i]['content'];
$shortEnd = $i;
} else {
break;
}
}
}
if (preg_match('/^\p{Ll}/u', $shortContent) === 1) {
$error = 'Doc comment short description must start with a capital letter';
$phpcsFile->addError($error, $short, 'ShortNotCapital');
}
$long = $phpcsFile->findNext($empty, ($shortEnd + 1), ($commentEnd - 1), true);
if ($long !== false && $tokens[$long]['code'] === T_DOC_COMMENT_STRING) {
if ($tokens[$long]['line'] !== ($tokens[$shortEnd]['line'] + 2)) {
$error = 'There must be exactly one blank line between descriptions in a doc comment';
$fix = $phpcsFile->addFixableError($error, $long, 'SpacingBetween');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = ($shortEnd + 1); $i < $long; $i++) {
if ($tokens[$i]['line'] === $tokens[$shortEnd]['line']) {
continue;
} else if ($tokens[$i]['line'] === ($tokens[$long]['line'] - 1)) {
break;
}
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}
}
if (preg_match('/^\p{Ll}/u', $tokens[$long]['content']) === 1) {
$error = 'Doc comment long description must start with a capital letter';
$phpcsFile->addError($error, $long, 'LongNotCapital');
}
}//end if
}//end if
if (empty($tokens[$commentStart]['comment_tags']) === true) {
// No tags in the comment.
return;
}
$firstTag = $tokens[$commentStart]['comment_tags'][0];
$prev = $phpcsFile->findPrevious($empty, ($firstTag - 1), $stackPtr, true);
if ($tokens[$firstTag]['line'] !== ($tokens[$prev]['line'] + 2)
&& $tokens[$prev]['code'] !== T_DOC_COMMENT_OPEN_TAG
) {
$error = 'There must be exactly one blank line before the tags in a doc comment';
$fix = $phpcsFile->addFixableError($error, $firstTag, 'SpacingBeforeTags');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = ($prev + 1); $i < $firstTag; $i++) {
if ($tokens[$i]['line'] === $tokens[$firstTag]['line']) {
break;
}
$phpcsFile->fixer->replaceToken($i, '');
}
$indent = str_repeat(' ', $tokens[$stackPtr]['column']);
$phpcsFile->fixer->addContent($prev, $phpcsFile->eolChar.$indent.'*'.$phpcsFile->eolChar);
$phpcsFile->fixer->endChangeset();
}
}
// Break out the tags into groups and check alignment within each.
// A tag group is one where there are no blank lines between tags.
// The param tag group is special as it requires all @param tags to be inside.
$tagGroups = [];
$groupid = 0;
$paramGroupid = null;
foreach ($tokens[$commentStart]['comment_tags'] as $pos => $tag) {
if ($pos > 0) {
$prev = $phpcsFile->findPrevious(
T_DOC_COMMENT_STRING,
($tag - 1),
$tokens[$commentStart]['comment_tags'][($pos - 1)]
);
if ($prev === false) {
$prev = $tokens[$commentStart]['comment_tags'][($pos - 1)];
}
if ($tokens[$prev]['line'] !== ($tokens[$tag]['line'] - 1)) {
$groupid++;
}
}
if ($tokens[$tag]['content'] === '@param') {
if ($paramGroupid !== null
&& $paramGroupid !== $groupid
) {
$error = 'Parameter tags must be grouped together in a doc comment';
$phpcsFile->addError($error, $tag, 'ParamGroup');
}
if ($paramGroupid === null) {
$paramGroupid = $groupid;
}
}//end if
$tagGroups[$groupid][] = $tag;
}//end foreach
foreach ($tagGroups as $groupid => $group) {
$maxLength = 0;
$paddings = [];
foreach ($group as $pos => $tag) {
if ($paramGroupid === $groupid
&& $tokens[$tag]['content'] !== '@param'
) {
$error = 'Tag %s cannot be grouped with parameter tags in a doc comment';
$data = [$tokens[$tag]['content']];
$phpcsFile->addError($error, $tag, 'NonParamGroup', $data);
}
$tagLength = $tokens[$tag]['length'];
if ($tagLength > $maxLength) {
$maxLength = $tagLength;
}
// Check for a value. No value means no padding needed.
$string = $phpcsFile->findNext(T_DOC_COMMENT_STRING, $tag, $commentEnd);
if ($string !== false && $tokens[$string]['line'] === $tokens[$tag]['line']) {
$paddings[$tag] = $tokens[($tag + 1)]['length'];
}
}
// Check that there was single blank line after the tag block
// but account for a multi-line tag comments.
$lastTag = $group[$pos];
$next = $phpcsFile->findNext(T_DOC_COMMENT_TAG, ($lastTag + 3), $commentEnd);
if ($next !== false) {
$prev = $phpcsFile->findPrevious([T_DOC_COMMENT_TAG, T_DOC_COMMENT_STRING], ($next - 1), $commentStart);
if ($tokens[$next]['line'] !== ($tokens[$prev]['line'] + 2)) {
$error = 'There must be a single blank line after a tag group';
$fix = $phpcsFile->addFixableError($error, $lastTag, 'SpacingAfterTagGroup');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = ($prev + 1); $i < $next; $i++) {
if ($tokens[$i]['line'] === $tokens[$next]['line']) {
break;
}
$phpcsFile->fixer->replaceToken($i, '');
}
$indent = str_repeat(' ', $tokens[$stackPtr]['column']);
$phpcsFile->fixer->addContent($prev, $phpcsFile->eolChar.$indent.'*'.$phpcsFile->eolChar);
$phpcsFile->fixer->endChangeset();
}
}
}//end if
// Now check paddings.
foreach ($paddings as $tag => $padding) {
$required = ($maxLength - $tokens[$tag]['length'] + 1);
if ($padding !== $required) {
$error = 'Tag value for %s tag indented incorrectly; expected %s spaces but found %s';
$data = [
$tokens[$tag]['content'],
$required,
$padding,
];
$fix = $phpcsFile->addFixableError($error, ($tag + 1), 'TagValueIndent', $data);
if ($fix === true) {
$phpcsFile->fixer->replaceToken(($tag + 1), str_repeat(' ', $required));
}
}
}
}//end foreach
// If there is a param group, it needs to be first.
if ($paramGroupid !== null && $paramGroupid !== 0) {
$error = 'Parameter tags must be defined first in a doc comment';
$phpcsFile->addError($error, $tagGroups[$paramGroupid][0], 'ParamNotFirst');
}
$foundTags = [];
foreach ($tokens[$stackPtr]['comment_tags'] as $pos => $tag) {
$tagName = $tokens[$tag]['content'];
if (isset($foundTags[$tagName]) === true) {
$lastTag = $tokens[$stackPtr]['comment_tags'][($pos - 1)];
if ($tokens[$lastTag]['content'] !== $tagName) {
$error = 'Tags must be grouped together in a doc comment';
$phpcsFile->addError($error, $tag, 'TagsNotGrouped');
}
continue;
}
$foundTags[$tagName] = true;
}
}//end process()
}//end class
@@ -0,0 +1,78 @@
<?php
/**
* Warns about FIXME comments.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Sam Graham <php-codesniffer@illusori.co.uk>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Commenting;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class FixmeSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = [
'PHP',
'JS',
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array_diff(Tokens::$commentTokens, Tokens::$phpcsCommentTokens);
}//end register()
/**
* Processes this sniff, when one of its tokens is encountered.
*
* @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();
$content = $tokens[$stackPtr]['content'];
$matches = [];
preg_match('/(?:\A|[^\p{L}]+)fixme([^\p{L}]+(.*)|\Z)/ui', $content, $matches);
if (empty($matches) === false) {
// Clear whitespace and some common characters not required at
// the end of a fixme message to make the error more informative.
$type = 'CommentFound';
$fixmeMessage = trim($matches[1]);
$fixmeMessage = trim($fixmeMessage, '-:[](). ');
$error = 'Comment refers to a FIXME task';
$data = [$fixmeMessage];
if ($fixmeMessage !== '') {
$type = 'TaskFound';
$error .= ' "%s"';
}
$phpcsFile->addError($error, $stackPtr, $type, $data);
}
}//end process()
}//end class
@@ -0,0 +1,77 @@
<?php
/**
* Warns about TODO comments.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Commenting;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class TodoSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = [
'PHP',
'JS',
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return array_diff(Tokens::$commentTokens, Tokens::$phpcsCommentTokens);
}//end register()
/**
* Processes this sniff, when one of its tokens is encountered.
*
* @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();
$content = $tokens[$stackPtr]['content'];
$matches = [];
preg_match('/(?:\A|[^\p{L}]+)todo([^\p{L}]+(.*)|\Z)/ui', $content, $matches);
if (empty($matches) === false) {
// Clear whitespace and some common characters not required at
// the end of a to-do message to make the warning more informative.
$type = 'CommentFound';
$todoMessage = trim($matches[1]);
$todoMessage = trim($todoMessage, '-:[](). ');
$error = 'Comment refers to a TODO task';
$data = [$todoMessage];
if ($todoMessage !== '') {
$type = 'TaskFound';
$error .= ' "%s"';
}
$phpcsFile->addWarning($error, $stackPtr, $type, $data);
}
}//end process()
}//end class
@@ -0,0 +1,188 @@
<?php
/**
* Ban the use of Yoda conditions.
*
* @author Mponos George <gmponos@gmail.com>
* @author Mark Scherer <username@example.com>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\ControlStructures;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class DisallowYodaConditionsSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return Tokens::$comparisonTokens;
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
$previousIndex = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
$relevantTokens = [
T_CLOSE_SHORT_ARRAY,
T_CLOSE_PARENTHESIS,
T_TRUE,
T_FALSE,
T_NULL,
T_LNUMBER,
T_DNUMBER,
T_CONSTANT_ENCAPSED_STRING,
];
if ($previousIndex === false
|| in_array($tokens[$previousIndex]['code'], $relevantTokens, true) === false
) {
return;
}
if ($tokens[$previousIndex]['code'] === T_CLOSE_SHORT_ARRAY) {
$previousIndex = $tokens[$previousIndex]['bracket_opener'];
if ($this->isArrayStatic($phpcsFile, $previousIndex) === false) {
return;
}
}
$prevIndex = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($previousIndex - 1), null, true);
if ($prevIndex === false) {
return;
}
if (in_array($tokens[$prevIndex]['code'], Tokens::$arithmeticTokens, true) === true) {
return;
}
if ($tokens[$prevIndex]['code'] === T_STRING_CONCAT) {
return;
}
// Is it a parenthesis.
if ($tokens[$previousIndex]['code'] === T_CLOSE_PARENTHESIS) {
// Check what exists inside the parenthesis.
$closeParenthesisIndex = $phpcsFile->findPrevious(
Tokens::$emptyTokens,
($tokens[$previousIndex]['parenthesis_opener'] - 1),
null,
true
);
if ($closeParenthesisIndex === false || $tokens[$closeParenthesisIndex]['code'] !== T_ARRAY) {
if ($tokens[$closeParenthesisIndex]['code'] === T_STRING) {
return;
}
// If it is not an array check what is inside.
$found = $phpcsFile->findPrevious(
T_VARIABLE,
($previousIndex - 1),
$tokens[$previousIndex]['parenthesis_opener']
);
// If a variable exists, it is not Yoda.
if ($found !== false) {
return;
}
// If there is nothing inside the parenthesis, it it not a Yoda.
$opener = $tokens[$previousIndex]['parenthesis_opener'];
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($previousIndex - 1), ($opener + 1), true);
if ($prev === false) {
return;
}
} else if ($tokens[$closeParenthesisIndex]['code'] === T_ARRAY
&& $this->isArrayStatic($phpcsFile, $closeParenthesisIndex) === false
) {
return;
}//end if
}//end if
$phpcsFile->addError(
'Usage of Yoda conditions is not allowed; switch the expression order',
$stackPtr,
'Found'
);
}//end process()
/**
* Determines if an array is a static definition.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $arrayToken The position of the array token.
*
* @return bool
*/
public function isArrayStatic(File $phpcsFile, $arrayToken)
{
$tokens = $phpcsFile->getTokens();
$arrayEnd = null;
if ($tokens[$arrayToken]['code'] === T_OPEN_SHORT_ARRAY) {
$start = $arrayToken;
$end = $tokens[$arrayToken]['bracket_closer'];
} else if ($tokens[$arrayToken]['code'] === T_ARRAY) {
$start = $tokens[$arrayToken]['parenthesis_opener'];
$end = $tokens[$arrayToken]['parenthesis_closer'];
} else {
return true;
}
$staticTokens = Tokens::$emptyTokens;
$staticTokens += Tokens::$textStringTokens;
$staticTokens += Tokens::$assignmentTokens;
$staticTokens += Tokens::$equalityTokens;
$staticTokens += Tokens::$comparisonTokens;
$staticTokens += Tokens::$arithmeticTokens;
$staticTokens += Tokens::$operators;
$staticTokens += Tokens::$booleanOperators;
$staticTokens += Tokens::$castTokens;
$staticTokens += Tokens::$bracketTokens;
$staticTokens += [
T_DOUBLE_ARROW => T_DOUBLE_ARROW,
T_COMMA => T_COMMA,
T_TRUE => T_TRUE,
T_FALSE => T_FALSE,
];
for ($i = ($start + 1); $i < $end; $i++) {
if (isset($tokens[$i]['scope_closer']) === true) {
$i = $tokens[$i]['scope_closer'];
continue;
}
if (isset($staticTokens[$tokens[$i]['code']]) === false) {
return false;
}
}
return true;
}//end isArrayStatic()
}//end class
@@ -0,0 +1,381 @@
<?php
/**
* Verifies that inline control statements are not present.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\ControlStructures;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
class InlineControlStructureSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = [
'PHP',
'JS',
];
/**
* If true, an error will be thrown; otherwise a warning.
*
* @var boolean
*/
public $error = true;
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [
T_IF,
T_ELSE,
T_ELSEIF,
T_FOREACH,
T_WHILE,
T_DO,
T_SWITCH,
T_FOR,
];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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();
if (isset($tokens[$stackPtr]['scope_opener']) === true) {
$phpcsFile->recordMetric($stackPtr, 'Control structure defined inline', 'no');
return;
}
// Ignore the ELSE in ELSE IF. We'll process the IF part later.
if ($tokens[$stackPtr]['code'] === T_ELSE) {
$next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
if ($tokens[$next]['code'] === T_IF) {
return;
}
}
if ($tokens[$stackPtr]['code'] === T_WHILE || $tokens[$stackPtr]['code'] === T_FOR) {
// This could be from a DO WHILE, which doesn't have an opening brace or a while/for without body.
if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
$afterParensCloser = $phpcsFile->findNext(Tokens::$emptyTokens, ($tokens[$stackPtr]['parenthesis_closer'] + 1), null, true);
if ($afterParensCloser === false) {
// Live coding.
return;
}
if ($tokens[$afterParensCloser]['code'] === T_SEMICOLON) {
$phpcsFile->recordMetric($stackPtr, 'Control structure defined inline', 'no');
return;
}
}
// In Javascript DO WHILE loops without curly braces are legal. This
// is only valid if a single statement is present between the DO and
// the WHILE. We can detect this by checking only a single semicolon
// is present between them.
if ($tokens[$stackPtr]['code'] === T_WHILE && $phpcsFile->tokenizerType === 'JS') {
$lastDo = $phpcsFile->findPrevious(T_DO, ($stackPtr - 1));
$lastSemicolon = $phpcsFile->findPrevious(T_SEMICOLON, ($stackPtr - 1));
if ($lastDo !== false && $lastSemicolon !== false && $lastDo < $lastSemicolon) {
$precedingSemicolon = $phpcsFile->findPrevious(T_SEMICOLON, ($lastSemicolon - 1));
if ($precedingSemicolon === false || $precedingSemicolon < $lastDo) {
return;
}
}
}
}//end if
if (isset($tokens[$stackPtr]['parenthesis_opener'], $tokens[$stackPtr]['parenthesis_closer']) === false
&& $tokens[$stackPtr]['code'] !== T_ELSE
) {
if ($tokens[$stackPtr]['code'] !== T_DO) {
// Live coding or parse error.
return;
}
$nextWhile = $phpcsFile->findNext(T_WHILE, ($stackPtr + 1));
if ($nextWhile !== false
&& isset($tokens[$nextWhile]['parenthesis_opener'], $tokens[$nextWhile]['parenthesis_closer']) === false
) {
// Live coding or parse error.
return;
}
unset($nextWhile);
}
$start = $stackPtr;
if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
$start = $tokens[$stackPtr]['parenthesis_closer'];
}
$nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($start + 1), null, true);
if ($nextNonEmpty === false) {
// Live coding or parse error.
return;
}
if ($tokens[$nextNonEmpty]['code'] === T_OPEN_CURLY_BRACKET
|| $tokens[$nextNonEmpty]['code'] === T_COLON
) {
// T_CLOSE_CURLY_BRACKET missing, or alternative control structure with
// T_END... missing. Either live coding, parse error or end
// tag in short open tags and scan run with short_open_tag=Off.
// Bow out completely as any further detection will be unreliable
// and create incorrect fixes or cause fixer conflicts.
return ($phpcsFile->numTokens + 1);
}
unset($nextNonEmpty, $start);
// This is a control structure without an opening brace,
// so it is an inline statement.
if ($this->error === true) {
$fix = $phpcsFile->addFixableError('Inline control structures are not allowed', $stackPtr, 'NotAllowed');
} else {
$fix = $phpcsFile->addFixableWarning('Inline control structures are discouraged', $stackPtr, 'Discouraged');
}
$phpcsFile->recordMetric($stackPtr, 'Control structure defined inline', 'yes');
// Stop here if we are not fixing the error.
if ($fix !== true) {
return;
}
$phpcsFile->fixer->beginChangeset();
if (isset($tokens[$stackPtr]['parenthesis_closer']) === true) {
$closer = $tokens[$stackPtr]['parenthesis_closer'];
} else {
$closer = $stackPtr;
}
if ($tokens[($closer + 1)]['code'] === T_WHITESPACE
|| $tokens[($closer + 1)]['code'] === T_SEMICOLON
) {
$phpcsFile->fixer->addContent($closer, ' {');
} else {
$phpcsFile->fixer->addContent($closer, ' { ');
}
$fixableScopeOpeners = $this->register();
$lastNonEmpty = $closer;
for ($end = ($closer + 1); $end < $phpcsFile->numTokens; $end++) {
if ($tokens[$end]['code'] === T_SEMICOLON) {
break;
}
if ($tokens[$end]['code'] === T_CLOSE_TAG) {
$end = $lastNonEmpty;
break;
}
if (in_array($tokens[$end]['code'], $fixableScopeOpeners, true) === true
&& isset($tokens[$end]['scope_opener']) === false
) {
// The best way to fix nested inline scopes is middle-out.
// So skip this one. It will be detected and fixed on a future loop.
$phpcsFile->fixer->rollbackChangeset();
return;
}
if (isset($tokens[$end]['scope_opener']) === true) {
$type = $tokens[$end]['code'];
$end = $tokens[$end]['scope_closer'];
if ($type === T_DO
|| $type === T_IF || $type === T_ELSEIF
|| $type === T_TRY || $type === T_CATCH || $type === T_FINALLY
) {
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
if ($next === false) {
break;
}
$nextType = $tokens[$next]['code'];
// Let additional conditions loop and find their ending.
if (($type === T_IF
|| $type === T_ELSEIF)
&& ($nextType === T_ELSEIF
|| $nextType === T_ELSE)
) {
continue;
}
// Account for TRY... CATCH/FINALLY statements.
if (($type === T_TRY
|| $type === T_CATCH
|| $type === T_FINALLY)
&& ($nextType === T_CATCH
|| $nextType === T_FINALLY)
) {
continue;
}
// Account for DO... WHILE conditions.
if ($type === T_DO && $nextType === T_WHILE) {
$end = $phpcsFile->findNext(T_SEMICOLON, ($next + 1));
}
} else if ($type === T_CLOSURE) {
// There should be a semicolon after the closing brace.
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
if ($next !== false && $tokens[$next]['code'] === T_SEMICOLON) {
$end = $next;
}
}//end if
if ($tokens[$end]['code'] !== T_END_HEREDOC
&& $tokens[$end]['code'] !== T_END_NOWDOC
) {
break;
}
}//end if
if (isset($tokens[$end]['parenthesis_closer']) === true) {
$end = $tokens[$end]['parenthesis_closer'];
$lastNonEmpty = $end;
continue;
}
if ($tokens[$end]['code'] !== T_WHITESPACE) {
$lastNonEmpty = $end;
}
}//end for
if ($end === $phpcsFile->numTokens) {
$end = $lastNonEmpty;
}
$nextContent = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
if ($nextContent === false || $tokens[$nextContent]['line'] !== $tokens[$end]['line']) {
// Looks for completely empty statements.
$next = $phpcsFile->findNext(T_WHITESPACE, ($closer + 1), ($end + 1), true);
} else {
$next = ($end + 1);
$endLine = $end;
}
if ($next !== $end) {
if ($nextContent === false || $tokens[$nextContent]['line'] !== $tokens[$end]['line']) {
// Account for a comment on the end of the line.
for ($endLine = $end; $endLine < $phpcsFile->numTokens; $endLine++) {
if (isset($tokens[($endLine + 1)]) === false
|| $tokens[$endLine]['line'] !== $tokens[($endLine + 1)]['line']
) {
break;
}
}
if (isset(Tokens::$commentTokens[$tokens[$endLine]['code']]) === false
&& ($tokens[$endLine]['code'] !== T_WHITESPACE
|| isset(Tokens::$commentTokens[$tokens[($endLine - 1)]['code']]) === false)
) {
$endLine = $end;
}
}
if ($endLine !== $end) {
$endToken = $endLine;
$addedContent = '';
} else {
$endToken = $end;
$addedContent = $phpcsFile->eolChar;
if ($tokens[$end]['code'] !== T_SEMICOLON
&& $tokens[$end]['code'] !== T_CLOSE_CURLY_BRACKET
) {
$phpcsFile->fixer->addContent($end, '; ');
}
}
$next = $phpcsFile->findNext(T_WHITESPACE, ($endToken + 1), null, true);
if ($next !== false
&& ($tokens[$next]['code'] === T_ELSE
|| $tokens[$next]['code'] === T_ELSEIF)
) {
$phpcsFile->fixer->addContentBefore($next, '} ');
} else {
$indent = '';
for ($first = $stackPtr; $first > 0; $first--) {
if ($tokens[$first]['column'] === 1) {
break;
}
}
if ($tokens[$first]['code'] === T_WHITESPACE) {
$indent = $tokens[$first]['content'];
} else if ($tokens[$first]['code'] === T_INLINE_HTML
|| $tokens[$first]['code'] === T_OPEN_TAG
) {
$addedContent = '';
}
$addedContent .= $indent.'}';
if ($next !== false && $tokens[$endToken]['code'] === T_COMMENT) {
$addedContent .= $phpcsFile->eolChar;
}
$phpcsFile->fixer->addContent($endToken, $addedContent);
}//end if
} else {
if ($nextContent === false || $tokens[$nextContent]['line'] !== $tokens[$end]['line']) {
// Account for a comment on the end of the line.
for ($endLine = $end; $endLine < $phpcsFile->numTokens; $endLine++) {
if (isset($tokens[($endLine + 1)]) === false
|| $tokens[$endLine]['line'] !== $tokens[($endLine + 1)]['line']
) {
break;
}
}
if ($tokens[$endLine]['code'] !== T_COMMENT
&& ($tokens[$endLine]['code'] !== T_WHITESPACE
|| $tokens[($endLine - 1)]['code'] !== T_COMMENT)
) {
$endLine = $end;
}
}
if ($endLine !== $end) {
$phpcsFile->fixer->replaceToken($end, '');
$phpcsFile->fixer->addNewlineBefore($endLine);
$phpcsFile->fixer->addContent($endLine, '}');
} else {
$phpcsFile->fixer->replaceToken($end, '}');
}
}//end if
$phpcsFile->fixer->endChangeset();
}//end process()
}//end class
@@ -0,0 +1,96 @@
<?php
/**
* Runs csslint on the file.
*
* @author Roman Levishchenko <index.0h@gmail.com>
* @copyright 2013-2014 Roman Levishchenko
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Debug;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Common;
class CSSLintSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = ['CSS'];
/**
* Returns the token types that this sniff is interested in.
*
* @return int[]
*/
public function register()
{
return [T_OPEN_TAG];
}//end register()
/**
* Processes the tokens that this sniff is interested in.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where
* the token was found.
*
* @return void
*/
public function process(File $phpcsFile, $stackPtr)
{
$csslintPath = Config::getExecutablePath('csslint');
if ($csslintPath === null) {
return;
}
$fileName = $phpcsFile->getFilename();
$cmd = Common::escapeshellcmd($csslintPath).' '.escapeshellarg($fileName).' 2>&1';
exec($cmd, $output, $retval);
if (is_array($output) === false) {
return;
}
$count = count($output);
for ($i = 0; $i < $count; $i++) {
$matches = [];
$numMatches = preg_match(
'/(error|warning) at line (\d+)/',
$output[$i],
$matches
);
if ($numMatches === 0) {
continue;
}
$line = (int) $matches[2];
$message = 'csslint says: '.$output[($i + 1)];
// First line is message with error line and error code.
// Second is error message.
// Third is wrong line in file.
// Fourth is empty line.
$i += 4;
$phpcsFile->addWarningOnLine($message, $line, 'ExternalTool');
}//end for
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
}//end class
@@ -0,0 +1,117 @@
<?php
/**
* Runs gjslint on the file.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Debug;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Common;
class ClosureLinterSniff implements Sniff
{
/**
* A list of error codes that should show errors.
*
* All other error codes will show warnings.
*
* @var integer
*/
public $errorCodes = [];
/**
* A list of error codes to ignore.
*
* @var integer
*/
public $ignoreCodes = [];
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = ['JS'];
/**
* Returns the token types that this sniff is interested in.
*
* @return int[]
*/
public function register()
{
return [T_OPEN_TAG];
}//end register()
/**
* Processes the tokens that this sniff is interested in.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where
* the token was found.
*
* @return void
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jslint.js could not be run
*/
public function process(File $phpcsFile, $stackPtr)
{
$lintPath = Config::getExecutablePath('gjslint');
if ($lintPath === null) {
return;
}
$fileName = $phpcsFile->getFilename();
$lintPath = Common::escapeshellcmd($lintPath);
$cmd = $lintPath.' --nosummary --notime --unix_mode '.escapeshellarg($fileName);
exec($cmd, $output, $retval);
if (is_array($output) === false) {
return;
}
foreach ($output as $finding) {
$matches = [];
$numMatches = preg_match('/^(.*):([0-9]+):\(.*?([0-9]+)\)(.*)$/', $finding, $matches);
if ($numMatches === 0) {
continue;
}
// Skip error codes we are ignoring.
$code = $matches[3];
if (in_array($code, $this->ignoreCodes) === true) {
continue;
}
$line = (int) $matches[2];
$error = trim($matches[4]);
$message = 'gjslint says: (%s) %s';
$data = [
$code,
$error,
];
if (in_array($code, $this->errorCodes) === true) {
$phpcsFile->addErrorOnLine($message, $line, 'ExternalToolError', $data);
} else {
$phpcsFile->addWarningOnLine($message, $line, 'ExternalTool', $data);
}
}//end foreach
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
}//end class
@@ -0,0 +1,113 @@
<?php
/**
* Runs eslint on the file.
*
* @author Ryan McCue <ryan+gh@hmn.md>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Debug;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Common;
class ESLintSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = ['JS'];
/**
* ESLint configuration file path.
*
* @var string|null Path to eslintrc. Null to autodetect.
*/
public $configFile = null;
/**
* Returns the token types that this sniff is interested in.
*
* @return int[]
*/
public function register()
{
return [T_OPEN_TAG];
}//end register()
/**
* Processes the tokens that this sniff is interested in.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where
* the token was found.
*
* @return void
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jshint.js could not be run
*/
public function process(File $phpcsFile, $stackPtr)
{
$eslintPath = Config::getExecutablePath('eslint');
if ($eslintPath === null) {
return;
}
$filename = $phpcsFile->getFilename();
$configFile = $this->configFile;
if (empty($configFile) === true) {
// Attempt to autodetect.
$candidates = glob('.eslintrc{.js,.yaml,.yml,.json}', GLOB_BRACE);
if (empty($candidates) === false) {
$configFile = $candidates[0];
}
}
$eslintOptions = ['--format json'];
if (empty($configFile) === false) {
$eslintOptions[] = '--config '.escapeshellarg($configFile);
}
$cmd = Common::escapeshellcmd(escapeshellarg($eslintPath).' '.implode(' ', $eslintOptions).' '.escapeshellarg($filename));
// Execute!
exec($cmd, $stdout, $code);
if ($code <= 0) {
// No errors, continue.
return ($phpcsFile->numTokens + 1);
}
$data = json_decode(implode("\n", $stdout));
if (json_last_error() !== JSON_ERROR_NONE) {
// Ignore any errors.
return ($phpcsFile->numTokens + 1);
}
// Data is a list of files, but we only pass a single one.
$messages = $data[0]->messages;
foreach ($messages as $error) {
$message = 'eslint says: '.$error->message;
if (empty($error->fatal) === false || $error->severity === 2) {
$phpcsFile->addErrorOnLine($message, $error->line, 'ExternalTool');
} else {
$phpcsFile->addWarningOnLine($message, $error->line, 'ExternalTool');
}
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
}//end class
@@ -0,0 +1,95 @@
<?php
/**
* Runs jshint.js on the file.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @author Alexander Wei§ <aweisswa@gmx.de>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Debug;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Common;
class JSHintSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = ['JS'];
/**
* Returns the token types that this sniff is interested in.
*
* @return int[]
*/
public function register()
{
return [T_OPEN_TAG];
}//end register()
/**
* Processes the tokens that this sniff is interested in.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where
* the token was found.
*
* @return void
* @throws \PHP_CodeSniffer\Exceptions\RuntimeException If jshint.js could not be run
*/
public function process(File $phpcsFile, $stackPtr)
{
$rhinoPath = Config::getExecutablePath('rhino');
$jshintPath = Config::getExecutablePath('jshint');
if ($rhinoPath === null && $jshintPath === null) {
return;
}
$fileName = $phpcsFile->getFilename();
$jshintPath = Common::escapeshellcmd($jshintPath);
if ($rhinoPath !== null) {
$rhinoPath = Common::escapeshellcmd($rhinoPath);
$cmd = "$rhinoPath \"$jshintPath\" ".escapeshellarg($fileName);
exec($cmd, $output, $retval);
$regex = '`^(?P<error>.+)\(.+:(?P<line>[0-9]+).*:[0-9]+\)$`';
} else {
$cmd = "$jshintPath ".escapeshellarg($fileName);
exec($cmd, $output, $retval);
$regex = '`^(.+?): line (?P<line>[0-9]+), col [0-9]+, (?P<error>.+)$`';
}
if (is_array($output) === true) {
foreach ($output as $finding) {
$matches = [];
$numMatches = preg_match($regex, $finding, $matches);
if ($numMatches === 0) {
continue;
}
$line = (int) $matches['line'];
$message = 'jshint says: '.trim($matches['error']);
$phpcsFile->addWarningOnLine($message, $line, 'ExternalTool');
}
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
}//end class
@@ -0,0 +1,80 @@
<?php
/**
* A simple sniff for detecting a BOM definition that may corrupt application work.
*
* @author Piotr Karas <office@mediaself.pl>
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2010-2014 mediaSELF Sp. z o.o.
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class ByteOrderMarkSniff implements Sniff
{
/**
* List of supported BOM definitions.
*
* Use encoding names as keys and hex BOM representations as values.
*
* @var array
*/
protected $bomDefinitions = [
'UTF-8' => 'efbbbf',
'UTF-16 (BE)' => 'feff',
'UTF-16 (LE)' => 'fffe',
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_INLINE_HTML];
}//end register()
/**
* Processes this sniff, when one of its tokens is encountered.
*
* @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)
{
// The BOM will be the very first token in the file.
if ($stackPtr !== 0) {
return;
}
$tokens = $phpcsFile->getTokens();
foreach ($this->bomDefinitions as $bomName => $expectedBomHex) {
$bomByteLength = (strlen($expectedBomHex) / 2);
$htmlBomHex = bin2hex(substr($tokens[$stackPtr]['content'], 0, $bomByteLength));
if ($htmlBomHex === $expectedBomHex) {
$errorData = [$bomName];
$error = 'File contains %s byte order mark, which may corrupt your application';
$phpcsFile->addError($error, $stackPtr, 'Found', $errorData);
$phpcsFile->recordMetric($stackPtr, 'Using byte order mark', 'yes');
return;
}
}
$phpcsFile->recordMetric($stackPtr, 'Using byte order mark', 'no');
}//end process()
}//end class
@@ -0,0 +1,84 @@
<?php
/**
* Ensures the file ends with a newline character.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class EndFileNewlineSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = [
'PHP',
'JS',
'CSS',
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [
T_OPEN_TAG,
T_OPEN_TAG_WITH_ECHO,
];
}//end register()
/**
* Processes this sniff, when one of its tokens is encountered.
*
* @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 int
*/
public function process(File $phpcsFile, $stackPtr)
{
// Skip to the end of the file.
$tokens = $phpcsFile->getTokens();
$stackPtr = ($phpcsFile->numTokens - 1);
if ($tokens[$stackPtr]['content'] === '') {
$stackPtr--;
}
$eolCharLen = strlen($phpcsFile->eolChar);
$lastChars = substr($tokens[$stackPtr]['content'], ($eolCharLen * -1));
if ($lastChars !== $phpcsFile->eolChar) {
$phpcsFile->recordMetric($stackPtr, 'Newline at EOF', 'no');
$error = 'File must end with a newline character';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'NotFound');
if ($fix === true) {
$phpcsFile->fixer->addNewline($stackPtr);
}
} else {
$phpcsFile->recordMetric($stackPtr, 'Newline at EOF', 'yes');
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
}//end class
@@ -0,0 +1,91 @@
<?php
/**
* Ensures the file does not end with a newline character.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class EndFileNoNewlineSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = [
'PHP',
'JS',
'CSS',
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [
T_OPEN_TAG,
T_OPEN_TAG_WITH_ECHO,
];
}//end register()
/**
* Processes this sniff, when one of its tokens is encountered.
*
* @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 int
*/
public function process(File $phpcsFile, $stackPtr)
{
// Skip to the end of the file.
$tokens = $phpcsFile->getTokens();
$stackPtr = ($phpcsFile->numTokens - 1);
if ($tokens[$stackPtr]['content'] === '') {
--$stackPtr;
}
$eolCharLen = strlen($phpcsFile->eolChar);
$lastChars = substr($tokens[$stackPtr]['content'], ($eolCharLen * -1));
if ($lastChars === $phpcsFile->eolChar) {
$error = 'File must not end with a newline character';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
for ($i = $stackPtr; $i > 0; $i--) {
$newContent = rtrim($tokens[$i]['content'], $phpcsFile->eolChar);
$phpcsFile->fixer->replaceToken($i, $newContent);
if ($newContent !== '') {
break;
}
}
$phpcsFile->fixer->endChangeset();
}
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
}//end class
@@ -0,0 +1,62 @@
<?php
/**
* Tests that files are not executable.
*
* @author Matthew Peveler <matt.peveler@gmail.com>
* @copyright 2019 Matthew Peveler
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class ExecutableFileSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [
T_OPEN_TAG,
T_OPEN_TAG_WITH_ECHO,
];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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 int
*/
public function process(File $phpcsFile, $stackPtr)
{
$filename = $phpcsFile->getFilename();
if ($filename !== 'STDIN') {
$perms = fileperms($phpcsFile->getFilename());
if (($perms & 0x0040) !== 0 || ($perms & 0x0008) !== 0 || ($perms & 0x0001) !== 0) {
$error = 'A PHP file should not be executable; found file permissions set to %s';
$data = [substr(sprintf('%o', $perms), -4)];
$phpcsFile->addError($error, 0, 'Executable', $data);
}
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
}//end class
@@ -0,0 +1,79 @@
<?php
/**
* Ensures the whole file is PHP only, with no whitespace or inline HTML.
*
* @author Greg Sherwood <gsherwood@squiz.net>
* @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
class InlineHTMLSniff implements Sniff
{
/**
* List of supported BOM definitions.
*
* Use encoding names as keys and hex BOM representations as values.
*
* @var array
*/
protected $bomDefinitions = [
'UTF-8' => 'efbbbf',
'UTF-16 (BE)' => 'feff',
'UTF-16 (LE)' => 'fffe',
];
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_INLINE_HTML];
}//end register()
/**
* Processes this test, when one of its tokens is encountered.
*
* @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 int|null
*/
public function process(File $phpcsFile, $stackPtr)
{
// Allow a byte-order mark.
$tokens = $phpcsFile->getTokens();
foreach ($this->bomDefinitions as $bomName => $expectedBomHex) {
$bomByteLength = (strlen($expectedBomHex) / 2);
$htmlBomHex = bin2hex(substr($tokens[0]['content'], 0, $bomByteLength));
if ($htmlBomHex === $expectedBomHex && strlen($tokens[0]['content']) === $bomByteLength) {
return;
}
}
// Ignore shebang lines.
$tokens = $phpcsFile->getTokens();
if (substr($tokens[$stackPtr]['content'], 0, 2) === '#!') {
return;
}
$error = 'PHP files must only contain PHP code';
$phpcsFile->addError($error, $stackPtr, 'Found');
return $phpcsFile->numTokens;
}//end process()
}//end class

Some files were not shown because too many files have changed in this diff Show More