more updates yay

This commit is contained in:
Wyatt Miller
2018-05-21 12:29:14 -04:00
parent 168f2f2a54
commit 2a39739af0
1105 changed files with 115947 additions and 34 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,28 @@
<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 title="Invalid: Opening brace not last thing on the line.">
<![CDATA[
class Foo {<em> // Start of class.</em>
}
]]>
</code>
</code_comparison>
</documentation>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,25 @@
<documentation title="Todo 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,7 @@
<documentation title="End of File Newline">
<standard>
<![CDATA[
Files should end with a newline character.
]]>
</standard>
</documentation>

View File

@ -0,0 +1,7 @@
<documentation title="No End of File Newline">
<standard>
<![CDATA[
Files should not end with a newline character.
]]>
</standard>
</documentation>

View File

@ -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>

View File

@ -0,0 +1,7 @@
<documentation title="Line Endings">
<standard>
<![CDATA[
Unix-style line endings are preferred ("\n" instead of "\r\n").
]]>
</standard>
</documentation>

View File

@ -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>

View File

@ -0,0 +1,7 @@
<documentation title="Lowercased Filenames">
<standard>
<![CDATA[
Lowercase filenames are required.
]]>
</standard>
</documentation>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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, $quux);
]]>
</code>
<code title="Not aligned; harder to read">
<![CDATA[
$shortVar <em>=</em> (1 + 2);
$veryLongVarName <em>=</em> 'string';
$var <em>=</em> foo($bar, $baz, $quux);
]]>
</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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,7 @@
<documentation title="Backtick Operator">
<standard>
<![CDATA[
Disallow the use of the backtick operator for execution of shell commands.
]]>
</standard>
</documentation>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,7 @@
<documentation title="Goto">
<standard>
<![CDATA[
Discourage the use of the PHP `goto` language construct.
]]>
</standard>
</documentation>

View File

@ -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>

View File

@ -0,0 +1,23 @@
<documentation title="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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,23 @@
<documentation title="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>

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,7 @@
<documentation title="No Space Indentation">
<standard>
<![CDATA[
Tabs should be used for indentation instead of spaces.
]]>
</standard>
</documentation>

View File

@ -0,0 +1,7 @@
<documentation title="No Tab Indentation">
<standard>
<![CDATA[
Spaces should be used for indentation instead of tabs.
]]>
</standard>
</documentation>

View File

@ -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>

View File

@ -0,0 +1,144 @@
<?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\Files\File;
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();
$first = $phpcsFile->findFirstOnLine(T_WHITESPACE, $stackPtr, true);
$expectedIndent = ($tokens[$first]['column'] - 1 + $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

View File

@ -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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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

View File

@ -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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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

View File

@ -0,0 +1,116 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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_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

View File

@ -0,0 +1,123 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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,
];
}//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 = strlen($tokens[($openingBrace - 1)]['content']);
}
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

View File

@ -0,0 +1,166 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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,
];
}//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) {
$phpcsFile->addWarning(
'Variable assignment found within a condition. Did you mean to do a comparison ?',
$hasAssignment,
'Found'
);
}
$startPos = $hasAssignment;
} while ($startPos < $closer);
}//end process()
}//end class

View File

@ -0,0 +1,96 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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,
];
}//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

View File

@ -0,0 +1,91 @@
<?php
/**
* Detects for-loops that can be simplified to a while-loop.
*
* This rule is based on the PMD rule catalog. 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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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

View File

@ -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 catalog. 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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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

View File

@ -0,0 +1,134 @@
<?php
/**
* Detects incrementer jumbling in for loops.
*
* This rule is based on the PMD rule catalog. 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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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

View File

@ -0,0 +1,93 @@
<?php
/**
* Detects unconditional if- and elseif-statements.
*
* This rule is based on the PMD rule catalog. 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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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 for-loop 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

View File

@ -0,0 +1,85 @@
<?php
/**
* Detects unnecessary final modifiers inside of final classes.
*
* This rule is based on the PMD rule catalog. 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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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

View File

@ -0,0 +1,176 @@
<?php
/**
* Checks the 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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
class UnusedFunctionParameterSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [
T_FUNCTION,
T_CLOSURE,
];
}//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;
}
$params = [];
foreach ($phpcsFile->getMethodParameters($stackPtr) as $param) {
$params[$param['name']] = $stackPtr;
}
$next = ++$token['scope_opener'];
$end = --$token['scope_closer'];
$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) {
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) {
return;
}
// There is a return.
if ($tokens[$tmp]['code'] === T_SEMICOLON) {
return;
}
$tmp = $phpcsFile->findNext(Tokens::$emptyTokens, ($tmp + 1), null, true);
if ($tmp !== false && $tokens[$tmp]['code'] === T_SEMICOLON) {
// 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) {
foreach ($params as $paramName => $position) {
$error = 'The method parameter %s is never used';
$data = [$paramName];
$phpcsFile->addWarning($error, $position, 'Found', $data);
}
}
}//end process()
}//end class

View File

@ -0,0 +1,161 @@
<?php
/**
* Detects unnecessary overridden methods that simply call their parent.
*
* This rule is based on the PMD rule catalog. The Useless Overriding Method
* sniff detects the use of methods that only call their parent classes'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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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

View File

@ -0,0 +1,348 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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');
return;
}
// 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
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)) {
$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
&& empty($tagGroups[$groupid]) === false)
|| ($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;
}
} else if ($groupid === $paramGroupid) {
$error = 'Tag cannot be grouped with parameter tags in a doc comment';
$phpcsFile->addError($error, $tag, 'NonParamGroup');
}//end if
$tagGroups[$groupid][] = $tag;
}//end foreach
foreach ($tagGroups as $group) {
$maxLength = 0;
$paddings = [];
foreach ($group as $pos => $tag) {
$tagLength = strlen($tokens[$tag]['content']);
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] = strlen($tokens[($tag + 1)]['content']);
}
}
// 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 - strlen($tokens[$tag]['content']) + 1);
if ($padding !== $required) {
$error = 'Tag value indented incorrectly; expected %s spaces but found %s';
$data = [
$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

View File

@ -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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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

View File

@ -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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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

View File

@ -0,0 +1,302 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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) {
// This could be from a DO WHILE, which doesn't have an opening brace.
$lastContent = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($tokens[$lastContent]['code'] === T_CLOSE_CURLY_BRACKET) {
$brace = $tokens[$lastContent];
if (isset($brace['scope_condition']) === true) {
$condition = $tokens[$brace['scope_condition']];
if ($condition['code'] === T_DO) {
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 ($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
// 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
&& 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) {
$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 DO... WHILE conditions.
if ($type === T_DO && $nextType === T_WHILE) {
$end = $phpcsFile->findNext(T_SEMICOLON, ($next + 1));
}
// Account for TRY... CATCH statements.
if ($type === T_TRY && $nextType === T_CATCH) {
$end = $tokens[$next]['scope_closer'];
}
}//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;
}
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($end + 1), null, true);
if ($next === false || $tokens[$next]['line'] !== $tokens[$end]['line']) {
// Looks for completely empty statements.
$next = $phpcsFile->findNext(T_WHITESPACE, ($closer + 1), ($end + 1), true);
// 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) {
$endLine = $end;
}
} else {
$next = ($end + 1);
$endLine = $end;
}
if ($next !== $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 ($first === 1
|| $tokens[($first - 1)]['line'] !== $tokens[$first]['line']
) {
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 ($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

View File

@ -0,0 +1,95 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Config;
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 = 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

View File

@ -0,0 +1,116 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Config;
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 = 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

View File

@ -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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Config;
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 = 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

View File

@ -0,0 +1,86 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Config;
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();
$rhinoPath = escapeshellcmd($rhinoPath);
$jshintPath = escapeshellcmd($jshintPath);
$cmd = "$rhinoPath \"$jshintPath\" ".escapeshellarg($fileName);
exec($cmd, $output, $retval);
if (is_array($output) === true) {
foreach ($output as $finding) {
$matches = [];
$numMatches = preg_match('/^(.+)\(.+:([0-9]+).*:[0-9]+\)$/', $finding, $matches);
if ($numMatches === 0) {
continue;
}
$line = (int) $matches[2];
$message = 'jshint says: '.trim($matches[1]);
$phpcsFile->addWarningOnLine($message, $line, 'ExternalTool');
}
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
}//end class

View File

@ -0,0 +1,80 @@
<?php
/**
* A simple sniff for detecting BOMs 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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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

View File

@ -0,0 +1,81 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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];
}//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

View File

@ -0,0 +1,78 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
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];
}//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) {
$newContent = substr($tokens[$stackPtr]['content'], 0, ($eolCharLen * -1));
$phpcsFile->fixer->replaceToken($stackPtr, $newContent);
}
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
}//end class

View File

@ -0,0 +1,56 @@
<?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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class InlineHTMLSniff implements Sniff
{
/**
* 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 void
*/
public function process(File $phpcsFile, $stackPtr)
{
// 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

View File

@ -0,0 +1,136 @@
<?php
/**
* Checks that end of line characters are correct.
*
* @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\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class LineEndingsSniff implements Sniff
{
/**
* A list of tokenizers this sniff supports.
*
* @var array
*/
public $supportedTokenizers = [
'PHP',
'JS',
'CSS',
];
/**
* The valid EOL character.
*
* @var string
*/
public $eolChar = '\n';
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_OPEN_TAG];
}//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)
{
$found = $phpcsFile->eolChar;
$found = str_replace("\n", '\n', $found);
$found = str_replace("\r", '\r', $found);
$phpcsFile->recordMetric($stackPtr, 'EOL char', $found);
if ($found === $this->eolChar) {
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}
// Check for single line files without an EOL. This is a very special
// case and the EOL char is set to \n when this happens.
if ($found === '\n') {
$tokens = $phpcsFile->getTokens();
$lastToken = ($phpcsFile->numTokens - 1);
if ($tokens[$lastToken]['line'] === 1
&& $tokens[$lastToken]['content'] !== "\n"
) {
return;
}
}
$error = 'End of line character is invalid; expected "%s" but found "%s"';
$expected = $this->eolChar;
$expected = str_replace("\n", '\n', $expected);
$expected = str_replace("\r", '\r', $expected);
$data = [
$expected,
$found,
];
// Errors are always reported on line 1, no matter where the first PHP tag is.
$fix = $phpcsFile->addFixableError($error, 0, 'InvalidEOLChar', $data);
if ($fix === true) {
$tokens = $phpcsFile->getTokens();
switch ($this->eolChar) {
case '\n':
$eolChar = "\n";
break;
case '\r':
$eolChar = "\r";
break;
case '\r\n':
$eolChar = "\r\n";
break;
default:
$eolChar = $this->eolChar;
break;
}
for ($i = 0; $i < $phpcsFile->numTokens; $i++) {
if (isset($tokens[($i + 1)]) === false
|| $tokens[($i + 1)]['line'] > $tokens[$i]['line']
) {
// Token is the last on a line.
if (isset($tokens[$i]['orig_content']) === true) {
$tokenContent = $tokens[$i]['orig_content'];
} else {
$tokenContent = $tokens[$i]['content'];
}
$newContent = rtrim($tokenContent, "\r\n");
$newContent .= $eolChar;
$phpcsFile->fixer->replaceToken($i, $newContent);
}
}
}//end if
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
}//end class

View File

@ -0,0 +1,187 @@
<?php
/**
* Checks the length of all lines in a file.
*
* Checks all lines in the file, and throws warnings if they are over 80
* characters in length and errors if they are over 100. Both these
* figures can be changed in a ruleset.xml 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\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
class LineLengthSniff implements Sniff
{
/**
* The limit that the length of a line should not exceed.
*
* @var integer
*/
public $lineLimit = 80;
/**
* The limit that the length of a line must not exceed.
*
* Set to zero (0) to disable.
*
* @var integer
*/
public $absoluteLineLimit = 100;
/**
* Whether or not to ignore comment lines.
*
* @var boolean
*/
public $ignoreComments = false;
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
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 int
*/
public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
for ($i = 1; $i < $phpcsFile->numTokens; $i++) {
if ($tokens[$i]['column'] === 1) {
$this->checkLineLength($phpcsFile, $tokens, $i);
}
}
$this->checkLineLength($phpcsFile, $tokens, $i);
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
/**
* Checks if a line is too long.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param array $tokens The token stack.
* @param int $stackPtr The first token on the next line.
*
* @return null|false
*/
protected function checkLineLength($phpcsFile, $tokens, $stackPtr)
{
// The passed token is the first on the line.
$stackPtr--;
if ($tokens[$stackPtr]['column'] === 1
&& $tokens[$stackPtr]['length'] === 0
) {
// Blank line.
return;
}
if ($tokens[$stackPtr]['column'] !== 1
&& $tokens[$stackPtr]['content'] === $phpcsFile->eolChar
) {
$stackPtr--;
}
if (isset(Tokens::$phpcsCommentTokens[$tokens[$stackPtr]['code']]) === true) {
$prevNonWhiteSpace = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($tokens[$stackPtr]['line'] !== $tokens[$prevNonWhiteSpace]['line']) {
// Ignore PHPCS annotation comments if they are on a line by themselves.
return;
}
unset($prevNonWhiteSpace);
}
$lineLength = ($tokens[$stackPtr]['column'] + $tokens[$stackPtr]['length'] - 1);
// Record metrics for common line length groupings.
if ($lineLength <= 80) {
$phpcsFile->recordMetric($stackPtr, 'Line length', '80 or less');
} else if ($lineLength <= 120) {
$phpcsFile->recordMetric($stackPtr, 'Line length', '81-120');
} else if ($lineLength <= 150) {
$phpcsFile->recordMetric($stackPtr, 'Line length', '121-150');
} else {
$phpcsFile->recordMetric($stackPtr, 'Line length', '151 or more');
}
if ($tokens[$stackPtr]['code'] === T_COMMENT
|| $tokens[$stackPtr]['code'] === T_DOC_COMMENT_STRING
) {
if ($this->ignoreComments === true) {
return;
}
// If this is a long comment, check if it can be broken up onto multiple lines.
// Some comments contain unbreakable strings like URLs and so it makes sense
// to ignore the line length in these cases if the URL would be longer than the max
// line length once you indent it to the correct level.
if ($lineLength > $this->lineLimit) {
$oldLength = strlen($tokens[$stackPtr]['content']);
$newLength = strlen(ltrim($tokens[$stackPtr]['content'], "/#\t "));
$indent = (($tokens[$stackPtr]['column'] - 1) + ($oldLength - $newLength));
$nonBreakingLength = $tokens[$stackPtr]['length'];
$space = strrpos($tokens[$stackPtr]['content'], ' ');
if ($space !== false) {
$nonBreakingLength -= ($space + 1);
}
if (($nonBreakingLength + $indent) > $this->lineLimit) {
return;
}
}
}//end if
if ($this->absoluteLineLimit > 0
&& $lineLength > $this->absoluteLineLimit
) {
$data = [
$this->absoluteLineLimit,
$lineLength,
];
$error = 'Line exceeds maximum limit of %s characters; contains %s characters';
$phpcsFile->addError($error, $stackPtr, 'MaxExceeded', $data);
} else if ($lineLength > $this->lineLimit) {
$data = [
$this->lineLimit,
$lineLength,
];
$warning = 'Line exceeds %s characters; contains %s characters';
$phpcsFile->addWarning($warning, $stackPtr, 'TooLong', $data);
}
}//end checkLineLength()
}//end class

View File

@ -0,0 +1,67 @@
<?php
/**
* Checks that all file names are lowercased.
*
* @author Andy Grunwald <andygrunwald@gmail.com>
* @copyright 2010-2014 Andy Grunwald
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class LowercasedFilenameSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_OPEN_TAG];
}//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)
{
$filename = $phpcsFile->getFilename();
if ($filename === 'STDIN') {
return;
}
$filename = basename($filename);
$lowercaseFilename = strtolower($filename);
if ($filename !== $lowercaseFilename) {
$data = [
$filename,
$lowercaseFilename,
];
$error = 'Filename "%s" doesn\'t match the expected filename "%s"';
$phpcsFile->addError($error, $stackPtr, 'NotFound', $data);
$phpcsFile->recordMetric($stackPtr, 'Lowercase filename', 'no');
} else {
$phpcsFile->recordMetric($stackPtr, 'Lowercase filename', 'yes');
}
// Ignore the rest of the file.
return ($phpcsFile->numTokens + 1);
}//end process()
}//end class

View File

@ -0,0 +1,51 @@
<?php
/**
* Checks that only one class is declared per file.
*
* @author Andy Grunwald <andygrunwald@gmail.com>
* @copyright 2010-2014 Andy Grunwald
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class OneClassPerFileSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_CLASS];
}//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)
{
$nextClass = $phpcsFile->findNext($this->register(), ($stackPtr + 1));
if ($nextClass !== false) {
$error = 'Only one class is allowed in a file';
$phpcsFile->addError($error, $nextClass, 'MultipleFound');
}
}//end process()
}//end class

View File

@ -0,0 +1,51 @@
<?php
/**
* Checks that only one interface is declared per file.
*
* @author Andy Grunwald <andygrunwald@gmail.com>
* @copyright 2010-2014 Andy Grunwald
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class OneInterfacePerFileSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_INTERFACE];
}//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)
{
$nextInterface = $phpcsFile->findNext($this->register(), ($stackPtr + 1));
if ($nextInterface !== false) {
$error = 'Only one interface is allowed in a file';
$phpcsFile->addError($error, $nextInterface, 'MultipleFound');
}
}//end process()
}//end class

View File

@ -0,0 +1,55 @@
<?php
/**
* Checks that only one object structure is declared per file.
*
* @author Mponos George <gmponos@gmail.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\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class OneObjectStructurePerFileSniff 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,
];
}//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)
{
$nextClass = $phpcsFile->findNext($this->register(), ($stackPtr + 1));
if ($nextClass !== false) {
$error = 'Only one object structure is allowed in a file';
$phpcsFile->addError($error, $nextClass, 'MultipleFound');
}
}//end process()
}//end class

View File

@ -0,0 +1,51 @@
<?php
/**
* Checks that only one trait is declared per file.
*
* @author Alexander Obuhovich <aik.bold@gmail.com>
* @copyright 2010-2014 Alexander Obuhovich
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Files;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class OneTraitPerFileSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_TRAIT];
}//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)
{
$nextClass = $phpcsFile->findNext($this->register(), ($stackPtr + 1));
if ($nextClass !== false) {
$error = 'Only one trait is allowed in a file';
$phpcsFile->addError($error, $nextClass, 'MultipleFound');
}
}//end process()
}//end class

View File

@ -0,0 +1,89 @@
<?php
/**
* Ensures each statement is on a line by itself.
*
* @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\Formatting;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class DisallowMultipleStatementsSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [T_SEMICOLON];
}//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();
$prev = $phpcsFile->findPrevious([T_SEMICOLON, T_OPEN_TAG, T_OPEN_TAG_WITH_ECHO], ($stackPtr - 1));
if ($prev === false
|| $tokens[$prev]['code'] === T_OPEN_TAG
|| $tokens[$prev]['code'] === T_OPEN_TAG_WITH_ECHO
) {
$phpcsFile->recordMetric($stackPtr, 'Multiple statements on same line', 'no');
return;
}
// Ignore multiple statements in a FOR condition.
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
foreach ($tokens[$stackPtr]['nested_parenthesis'] as $bracket) {
if (isset($tokens[$bracket]['parenthesis_owner']) === false) {
// Probably a closure sitting inside a function call.
continue;
}
$owner = $tokens[$bracket]['parenthesis_owner'];
if ($tokens[$owner]['code'] === T_FOR) {
return;
}
}
}
if ($tokens[$prev]['line'] === $tokens[$stackPtr]['line']) {
$phpcsFile->recordMetric($stackPtr, 'Multiple statements on same line', 'yes');
$error = 'Each PHP statement must be on a line by itself';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'SameLine');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->addNewline($prev);
if ($tokens[($prev + 1)]['code'] === T_WHITESPACE) {
$phpcsFile->fixer->replaceToken(($prev + 1), '');
}
$phpcsFile->fixer->endChangeset();
}
} else {
$phpcsFile->recordMetric($stackPtr, 'Multiple statements on same line', 'no');
}
}//end process()
}//end class

View File

@ -0,0 +1,343 @@
<?php
/**
* Checks alignment of assignments.
*
* If there are multiple adjacent assignments, it will check that the equals signs of
* each assignment are aligned. It will display a warning to advise that the signs should be aligned.
*
* @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\Formatting;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
class MultipleStatementAlignmentSniff 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 = false;
/**
* The maximum amount of padding before the alignment is ignored.
*
* If the amount of padding required to align this assignment with the
* surrounding assignments exceeds this number, the assignment will be
* ignored and no errors or warnings will be thrown.
*
* @var integer
*/
public $maxPadding = 1000;
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
$tokens = Tokens::$assignmentTokens;
unset($tokens[T_DOUBLE_ARROW]);
return $tokens;
}//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)
{
$tokens = $phpcsFile->getTokens();
// Ignore assignments used in a condition, like an IF or FOR.
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
foreach ($tokens[$stackPtr]['nested_parenthesis'] as $start => $end) {
if (isset($tokens[$start]['parenthesis_owner']) === true) {
return;
}
}
}
$lastAssign = $this->checkAlignment($phpcsFile, $stackPtr);
return ($lastAssign + 1);
}//end process()
/**
* 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 checkAlignment($phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
$assignments = [];
$prevAssign = null;
$lastLine = $tokens[$stackPtr]['line'];
$maxPadding = null;
$stopped = null;
$lastCode = $stackPtr;
$lastSemi = null;
$find = Tokens::$assignmentTokens;
unset($find[T_DOUBLE_ARROW]);
for ($assign = $stackPtr; $assign < $phpcsFile->numTokens; $assign++) {
if (isset($find[$tokens[$assign]['code']]) === false) {
if ($tokens[$assign]['code'] === T_CLOSURE
|| $tokens[$assign]['code'] === T_ANON_CLASS
) {
$assign = $tokens[$assign]['scope_closer'];
$lastCode = $assign;
continue;
}
// Skip past the content of arrays.
if ($tokens[$assign]['code'] === T_OPEN_SHORT_ARRAY
&& isset($tokens[$assign]['bracket_closer']) === true
) {
$assign = $lastCode = $tokens[$assign]['bracket_closer'];
continue;
}
if ($tokens[$assign]['code'] === T_ARRAY
&& isset($tokens[$assign]['parenthesis_opener']) === true
&& isset($tokens[$tokens[$assign]['parenthesis_opener']]['parenthesis_closer']) === true
) {
$assign = $lastCode = $tokens[$tokens[$assign]['parenthesis_opener']]['parenthesis_closer'];
continue;
}
// A blank line indicates that the assignment block has ended.
if (isset(Tokens::$emptyTokens[$tokens[$assign]['code']]) === false) {
if (($tokens[$assign]['line'] - $tokens[$lastCode]['line']) > 1) {
break;
}
$lastCode = $assign;
if ($tokens[$assign]['code'] === T_SEMICOLON) {
if ($tokens[$assign]['conditions'] === $tokens[$stackPtr]['conditions']) {
if ($lastSemi !== null && $prevAssign !== null && $lastSemi > $prevAssign) {
// This statement did not have an assignment operator in it.
break;
} else {
$lastSemi = $assign;
}
} else {
// Statement is in a different context, so the block is over.
break;
}
}
}//end if
continue;
} else if ($assign !== $stackPtr && $tokens[$assign]['line'] === $lastLine) {
// Skip multiple assignments on the same line. We only need to
// try and align the first assignment.
continue;
}//end if
if ($assign !== $stackPtr) {
// Has to be nested inside the same conditions as the first assignment.
if ($tokens[$assign]['conditions'] !== $tokens[$stackPtr]['conditions']) {
break;
}
// Make sure it is not assigned inside a condition (eg. IF, FOR).
if (isset($tokens[$assign]['nested_parenthesis']) === true) {
foreach ($tokens[$assign]['nested_parenthesis'] as $start => $end) {
if (isset($tokens[$start]['parenthesis_owner']) === true) {
break(2);
}
}
}
}//end if
$var = $phpcsFile->findPrevious(
Tokens::$emptyTokens,
($assign - 1),
null,
true
);
// Make sure we wouldn't break our max padding length if we
// aligned with this statement, or they wouldn't break the max
// padding length if they aligned with us.
$varEnd = $tokens[($var + 1)]['column'];
$assignLen = $tokens[$assign]['length'];
if ($assign !== $stackPtr) {
if (($varEnd + 1) > $assignments[$prevAssign]['assign_col']) {
$padding = 1;
$assignColumn = ($varEnd + 1);
} else {
$padding = ($assignments[$prevAssign]['assign_col'] - $varEnd + $assignments[$prevAssign]['assign_len'] - $assignLen);
if ($padding <= 0) {
$padding = 1;
}
if ($padding > $this->maxPadding) {
$stopped = $assign;
break;
}
$assignColumn = ($varEnd + $padding);
}//end if
if (($assignColumn + $assignLen) > ($assignments[$maxPadding]['assign_col'] + $assignments[$maxPadding]['assign_len'])) {
$newPadding = ($varEnd - $assignments[$maxPadding]['var_end'] + $assignLen - $assignments[$maxPadding]['assign_len'] + 1);
if ($newPadding > $this->maxPadding) {
$stopped = $assign;
break;
} else {
// New alignment settings for previous assignments.
foreach ($assignments as $i => $data) {
if ($i === $assign) {
break;
}
$newPadding = ($varEnd - $data['var_end'] + $assignLen - $data['assign_len'] + 1);
$assignments[$i]['expected'] = $newPadding;
$assignments[$i]['assign_col'] = ($data['var_end'] + $newPadding);
}
$padding = 1;
$assignColumn = ($varEnd + 1);
}
} else if ($padding > $assignments[$maxPadding]['expected']) {
$maxPadding = $assign;
}//end if
} else {
$padding = 1;
$assignColumn = ($varEnd + 1);
$maxPadding = $assign;
}//end if
$found = 0;
if ($tokens[($var + 1)]['code'] === T_WHITESPACE) {
$found = $tokens[($var + 1)]['length'];
if ($found === 0) {
// This means a newline was found.
$found = 1;
}
}
$assignments[$assign] = [
'var_end' => $varEnd,
'assign_len' => $assignLen,
'assign_col' => $assignColumn,
'expected' => $padding,
'found' => $found,
];
$lastLine = $tokens[$assign]['line'];
$prevAssign = $assign;
}//end for
if (empty($assignments) === true) {
return $stackPtr;
}
$numAssignments = count($assignments);
$errorGenerated = false;
foreach ($assignments as $assignment => $data) {
if ($data['found'] === $data['expected']) {
continue;
}
$expectedText = $data['expected'].' space';
if ($data['expected'] !== 1) {
$expectedText .= 's';
}
if ($data['found'] === null) {
$foundText = 'a new line';
} else {
$foundText = $data['found'].' space';
if ($data['found'] !== 1) {
$foundText .= 's';
}
}
if ($numAssignments === 1) {
$type = 'Incorrect';
$error = 'Equals sign not aligned correctly; expected %s but found %s';
} else {
$type = 'NotSame';
$error = 'Equals sign not aligned with surrounding assignments; expected %s but found %s';
}
$errorData = [
$expectedText,
$foundText,
];
if ($this->error === true) {
$fix = $phpcsFile->addFixableError($error, $assignment, $type, $errorData);
} else {
$fix = $phpcsFile->addFixableWarning($error, $assignment, $type.'Warning', $errorData);
}
$errorGenerated = true;
if ($fix === true && $data['found'] !== null) {
$newContent = str_repeat(' ', $data['expected']);
if ($data['found'] === 0) {
$phpcsFile->fixer->addContentBefore($assignment, $newContent);
} else {
$phpcsFile->fixer->replaceToken(($assignment - 1), $newContent);
}
}
}//end foreach
if ($numAssignments > 1) {
if ($errorGenerated === true) {
$phpcsFile->recordMetric($stackPtr, 'Adjacent assignments aligned', 'no');
} else {
$phpcsFile->recordMetric($stackPtr, 'Adjacent assignments aligned', 'yes');
}
}
if ($stopped !== null) {
return $this->checkAlignment($phpcsFile, $stopped);
} else {
return $assignment;
}
}//end checkAlignment()
}//end class

View File

@ -0,0 +1,58 @@
<?php
/**
* Ensures there is no space after cast tokens.
*
* @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\Formatting;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
class NoSpaceAfterCastSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return Tokens::$castTokens;
}//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 ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
return;
}
$error = 'A cast statement must not be followed by a space';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceFound');
if ($fix === true) {
$phpcsFile->fixer->replaceToken(($stackPtr + 1), '');
}
}//end process()
}//end class

View File

@ -0,0 +1,69 @@
<?php
/**
* Ensures there is a single space after cast tokens.
*
* @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\Formatting;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
class SpaceAfterCastSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return Tokens::$castTokens;
}//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 ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) {
$error = 'A cast statement must be followed by a single space';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpace');
if ($fix === true) {
$phpcsFile->fixer->addContent($stackPtr, ' ');
}
$phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', 0);
return;
}
$phpcsFile->recordMetric($stackPtr, 'Spacing after cast statement', $tokens[($stackPtr + 1)]['length']);
if ($tokens[($stackPtr + 1)]['length'] !== 1) {
$error = 'A cast statement must be followed by a single space';
$fix = $phpcsFile->addFixableError($error, $stackPtr, 'TooMuchSpace');
if ($fix === true) {
$phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
}
}
}//end process()
}//end class

View File

@ -0,0 +1,78 @@
<?php
/**
* Ensures there is a single space after a NOT operator.
*
* @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\Formatting;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
class SpaceAfterNotSniff 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_BOOLEAN_NOT];
}//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();
$spacing = 0;
if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
$spacing = $tokens[($stackPtr + 1)]['length'];
}
if ($spacing === 1) {
return;
}
$message = 'There must be a single space after a NOT operator; %s found';
$fix = $phpcsFile->addFixableError($message, $stackPtr, 'Incorrect', [$spacing]);
if ($fix === true) {
if ($spacing === 0) {
$phpcsFile->fixer->addContent($stackPtr, ' ');
} else {
$phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
}
}
}//end process()
}//end class

View File

@ -0,0 +1,147 @@
<?php
/**
* Ensures that variables are not passed by reference when calling a function.
*
* @author Florian Grandel <jerico.dev@gmail.com>
* @copyright 2009-2014 Florian Grandel
* @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
*/
namespace PHP_CodeSniffer\Standards\Generic\Sniffs\Functions;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
class CallTimePassByReferenceSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
return [
T_STRING,
T_VARIABLE,
];
}//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();
$findTokens = array_merge(
Tokens::$emptyTokens,
[T_BITWISE_AND]
);
$prev = $phpcsFile->findPrevious($findTokens, ($stackPtr - 1), null, true);
// Skip tokens that are the names of functions or classes
// within their definitions. For example: function myFunction...
// "myFunction" is T_STRING but we should skip because it is not a
// function or method *call*.
$prevCode = $tokens[$prev]['code'];
if ($prevCode === T_FUNCTION || $prevCode === T_CLASS) {
return;
}
// If the next non-whitespace token after the function or method call
// is not an opening parenthesis then it cant really be a *call*.
$functionName = $stackPtr;
$openBracket = $phpcsFile->findNext(
Tokens::$emptyTokens,
($functionName + 1),
null,
true
);
if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
return;
}
if (isset($tokens[$openBracket]['parenthesis_closer']) === false) {
return;
}
$closeBracket = $tokens[$openBracket]['parenthesis_closer'];
$nextSeparator = $openBracket;
$find = [
T_VARIABLE,
T_OPEN_SHORT_ARRAY,
];
while (($nextSeparator = $phpcsFile->findNext($find, ($nextSeparator + 1), $closeBracket)) !== false) {
if (isset($tokens[$nextSeparator]['nested_parenthesis']) === false) {
continue;
}
if ($tokens[$nextSeparator]['code'] === T_OPEN_SHORT_ARRAY) {
$nextSeparator = $tokens[$nextSeparator]['bracket_closer'];
continue;
}
// Make sure the variable belongs directly to this function call
// and is not inside a nested function call or array.
$brackets = $tokens[$nextSeparator]['nested_parenthesis'];
$lastBracket = array_pop($brackets);
if ($lastBracket !== $closeBracket) {
continue;
}
// Checking this: $value = my_function(...[*]$arg...).
$tokenBefore = $phpcsFile->findPrevious(
Tokens::$emptyTokens,
($nextSeparator - 1),
null,
true
);
if ($tokens[$tokenBefore]['code'] === T_BITWISE_AND) {
// Checking this: $value = my_function(...[*]&$arg...).
$tokenBefore = $phpcsFile->findPrevious(
Tokens::$emptyTokens,
($tokenBefore - 1),
null,
true
);
// We have to exclude all uses of T_BITWISE_AND that are not
// references. We use a blacklist approach as we prefer false
// positives to not identifying a pass-by-reference call at all.
$tokenCode = $tokens[$tokenBefore]['code'];
if ($tokenCode === T_VARIABLE
|| $tokenCode === T_CLOSE_PARENTHESIS
|| $tokenCode === T_CLOSE_SQUARE_BRACKET
|| $tokenCode === T_LNUMBER
|| isset(Tokens::$assignmentTokens[$tokenCode]) === true
) {
continue;
}
// T_BITWISE_AND represents a pass-by-reference.
$error = 'Call-time pass-by-reference calls are prohibited';
$phpcsFile->addError($error, $tokenBefore, 'NotAllowed');
}//end if
}//end while
}//end process()
}//end class

View File

@ -0,0 +1,173 @@
<?php
/**
* Checks that calls to methods and functions are spaced correctly.
*
* @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\Functions;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Util\Tokens;
class FunctionCallArgumentSpacingSniff implements Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
$tokens = Tokens::$functionNameTokens;
$tokens[] = T_VARIABLE;
$tokens[] = T_CLOSE_CURLY_BRACKET;
$tokens[] = T_CLOSE_PARENTHESIS;
return $tokens;
}//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();
// Skip tokens that are the names of functions or classes
// within their definitions. For example:
// function myFunction...
// "myFunction" is T_STRING but we should skip because it is not a
// function or method *call*.
$functionName = $stackPtr;
$ignoreTokens = Tokens::$emptyTokens;
$ignoreTokens[] = T_BITWISE_AND;
$functionKeyword = $phpcsFile->findPrevious($ignoreTokens, ($stackPtr - 1), null, true);
if ($tokens[$functionKeyword]['code'] === T_FUNCTION || $tokens[$functionKeyword]['code'] === T_CLASS) {
return;
}
if ($tokens[$stackPtr]['code'] === T_CLOSE_CURLY_BRACKET
&& isset($tokens[$stackPtr]['scope_condition']) === true
) {
// Not a function call.
return;
}
// If the next non-whitespace token after the function or method call
// is not an opening parenthesis then it cant really be a *call*.
$openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($functionName + 1), null, true);
if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
return;
}
if (isset($tokens[$openBracket]['parenthesis_closer']) === false) {
return;
}
$closeBracket = $tokens[$openBracket]['parenthesis_closer'];
$nextSeparator = $openBracket;
$find = [
T_COMMA,
T_VARIABLE,
T_CLOSURE,
T_OPEN_SHORT_ARRAY,
];
while (($nextSeparator = $phpcsFile->findNext($find, ($nextSeparator + 1), $closeBracket)) !== false) {
if ($tokens[$nextSeparator]['code'] === T_CLOSURE) {
// Skip closures.
$nextSeparator = $tokens[$nextSeparator]['scope_closer'];
continue;
} else if ($tokens[$nextSeparator]['code'] === T_OPEN_SHORT_ARRAY) {
// Skips arrays using short notation.
$nextSeparator = $tokens[$nextSeparator]['bracket_closer'];
continue;
}
// Make sure the comma or variable belongs directly to this function call,
// and is not inside a nested function call or array.
$brackets = $tokens[$nextSeparator]['nested_parenthesis'];
$lastBracket = array_pop($brackets);
if ($lastBracket !== $closeBracket) {
continue;
}
if ($tokens[$nextSeparator]['code'] === T_COMMA) {
if ($tokens[($nextSeparator - 1)]['code'] === T_WHITESPACE) {
$prev = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($nextSeparator - 2), null, true);
if (isset(Tokens::$heredocTokens[$tokens[$prev]['code']]) === false) {
$error = 'Space found before comma in function call';
$fix = $phpcsFile->addFixableError($error, $nextSeparator, 'SpaceBeforeComma');
if ($fix === true) {
$phpcsFile->fixer->replaceToken(($nextSeparator - 1), '');
}
}
}
if ($tokens[($nextSeparator + 1)]['code'] !== T_WHITESPACE) {
$error = 'No space found after comma in function call';
$fix = $phpcsFile->addFixableError($error, $nextSeparator, 'NoSpaceAfterComma');
if ($fix === true) {
$phpcsFile->fixer->addContent($nextSeparator, ' ');
}
} else {
// If there is a newline in the space, then they must be formatting
// each argument on a newline, which is valid, so ignore it.
$next = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextSeparator + 1), null, true);
if ($tokens[$next]['line'] === $tokens[$nextSeparator]['line']) {
$space = strlen($tokens[($nextSeparator + 1)]['content']);
if ($space > 1) {
$error = 'Expected 1 space after comma in function call; %s found';
$data = [$space];
$fix = $phpcsFile->addFixableError($error, $nextSeparator, 'TooMuchSpaceAfterComma', $data);
if ($fix === true) {
$phpcsFile->fixer->replaceToken(($nextSeparator + 1), ' ');
}
}
}
}//end if
} else {
// Token is a variable.
$nextToken = $phpcsFile->findNext(Tokens::$emptyTokens, ($nextSeparator + 1), $closeBracket, true);
if ($nextToken !== false) {
if ($tokens[$nextToken]['code'] === T_EQUAL) {
if (($tokens[($nextToken - 1)]['code']) !== T_WHITESPACE) {
$error = 'Expected 1 space before = sign of default value';
$fix = $phpcsFile->addFixableError($error, $nextToken, 'NoSpaceBeforeEquals');
if ($fix === true) {
$phpcsFile->fixer->addContentBefore($nextToken, ' ');
}
}
if ($tokens[($nextToken + 1)]['code'] !== T_WHITESPACE) {
$error = 'Expected 1 space after = sign of default value';
$fix = $phpcsFile->addFixableError($error, $nextToken, 'NoSpaceAfterEquals');
if ($fix === true) {
$phpcsFile->fixer->addContent($nextToken, ' ');
}
}
}
}
}//end if
}//end while
}//end process()
}//end class

View File

@ -0,0 +1,175 @@
<?php
/**
* Checks that the opening brace of a function is on the line after the function 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\Functions;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
class OpeningFunctionBraceBsdAllmanSniff implements Sniff
{
/**
* Should this sniff check function braces?
*
* @var boolean
*/
public $checkFunctions = true;
/**
* Should this sniff check closure braces?
*
* @var boolean
*/
public $checkClosures = false;
/**
* Registers the tokens that this sniff wants to listen for.
*
* @return void
*/
public function register()
{
return [
T_FUNCTION,
T_CLOSURE,
];
}//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']) === false) {
return;
}
if (($tokens[$stackPtr]['code'] === T_FUNCTION
&& (bool) $this->checkFunctions === false)
|| ($tokens[$stackPtr]['code'] === T_CLOSURE
&& (bool) $this->checkClosures === false)
) {
return;
}
$openingBrace = $tokens[$stackPtr]['scope_opener'];
$closeBracket = $tokens[$stackPtr]['parenthesis_closer'];
if ($tokens[$stackPtr]['code'] === T_CLOSURE) {
$use = $phpcsFile->findNext(T_USE, ($closeBracket + 1), $tokens[$stackPtr]['scope_opener']);
if ($use !== false) {
$openBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($use + 1));
$closeBracket = $tokens[$openBracket]['parenthesis_closer'];
}
}
$functionLine = $tokens[$closeBracket]['line'];
$braceLine = $tokens[$openingBrace]['line'];
$lineDifference = ($braceLine - $functionLine);
if ($lineDifference === 0) {
$error = 'Opening brace should be on a new line';
$fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceOnSameLine');
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
$indent = $phpcsFile->findFirstOnLine([], $openingBrace);
if ($tokens[$indent]['code'] === T_WHITESPACE) {
$phpcsFile->fixer->addContentBefore($openingBrace, $tokens[$indent]['content']);
}
$phpcsFile->fixer->addNewlineBefore($openingBrace);
$phpcsFile->fixer->endChangeset();
}
$phpcsFile->recordMetric($stackPtr, 'Function opening brace placement', 'same line');
} else if ($lineDifference > 1) {
$error = 'Opening brace should be on the line after the declaration; found %s blank line(s)';
$data = [($lineDifference - 1)];
$fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceSpacing', $data);
if ($fix === true) {
for ($i = ($tokens[$stackPtr]['parenthesis_closer'] + 1); $i < $openingBrace; $i++) {
if ($tokens[$i]['line'] === $braceLine) {
$phpcsFile->fixer->addNewLineBefore($i);
break;
}
$phpcsFile->fixer->replaceToken($i, '');
}
}
}//end if
$next = $phpcsFile->findNext(T_WHITESPACE, ($openingBrace + 1), null, true);
if ($tokens[$next]['line'] === $tokens[$openingBrace]['line']) {
if ($next === $tokens[$stackPtr]['scope_closer']) {
// Ignore empty functions.
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 !== 1) {
return;
}
// We need to actually find the first piece of content on this line,
// as if this is a method with tokens before it (public, static etc)
// or an if with an else before it, then we need to start the scope
// checking from there, rather than the current token.
$lineStart = $phpcsFile->findFirstOnLine(T_WHITESPACE, $stackPtr, true);
// The opening brace is on the correct line, now it needs to be
// checked to be correctly indented.
$startColumn = $tokens[$lineStart]['column'];
$braceIndent = $tokens[$openingBrace]['column'];
if ($braceIndent !== $startColumn) {
$expected = ($startColumn - 1);
$found = ($braceIndent - 1);
$error = 'Opening brace indented incorrectly; expected %s spaces, found %s';
$data = [
$expected,
$found,
];
$fix = $phpcsFile->addFixableError($error, $openingBrace, 'BraceIndent', $data);
if ($fix === true) {
$indent = str_repeat(' ', $expected);
if ($found === 0) {
$phpcsFile->fixer->addContentBefore($openingBrace, $indent);
} else {
$phpcsFile->fixer->replaceToken(($openingBrace - 1), $indent);
}
}
}//end if
$phpcsFile->recordMetric($stackPtr, 'Function opening brace placement', 'new line');
}//end process()
}//end class

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