PHP Mess Detector
Ruleset name:Ruleset description:
Code Size Rules
The Code Size Ruleset contains a collection of rules that find code size related problems.
// Cyclomatic Complexity = 12 class Foo { 1 public function example() { 2 if ($a == $b) { 3 if ($a1 == $b1) { fiddle(); 4 } else if ($a2 == $b2) { fiddle(); } else { fiddle(); } 5 } else if ($c == $d) { 6 while ($c == $d) { fiddle(); } 7 } else if ($e == $f) { 8 for ($n = 0; $n < $h; $n++) { fiddle(); } } else{ switch ($z) { 9 case 1: fiddle(); break; 10 case 2: fiddle(); break; 11 case 3: fiddle(); break; 12 default: fiddle(); break; } } } }
class Foo { function bar() { // lots of complicated code } }
class Foo { public function doSomething() { print("Hello world!" . PHP_EOL); print("Hello world!" . PHP_EOL); // 98 copies omitted for brevity. } }
class Foo { public function bar() { // 1000 lines of code } }
class Foo { public function addData( $p0, $p1, $p2, $p3, $p4, $p5, $p5, $p6, $p7, $p8, $p9, $p10) { } }
public class Foo { public $value; public $something; public $var; // [... more more public attributes ...] public function doWork() {} public function doMoreWork() {} public function doWorkAgain() {} // [... more more public methods ...] }
class Person { protected $one; private $two; private $three; [... many more fields ...] }
class Foo { public function bar() { if ($a == $b) { if ($a1 == $b1) { fiddle(); } else if ($a2 == $b2) { fiddle(); } else { } } } public function baz() { if ($a == $b) { if ($a1 == $b1) { fiddle(); } else if ($a2 == $b2) { fiddle(); } else { } } } // Several other complex methods }
Controversial Rules
This ruleset contains a collection of controversial rules.
class Foo { public function bar() { $name = $_POST['foo']; } }
class class_name { }
class ClassName { protected $property_name; }
class ClassName { public function get_name() { } }
class ClassName { public function doSomething($user_name) { } }
class ClassName { public function doSomething() { $data_module = new DataModule(); } }
Design Rules
The Code Size Ruleset contains a collection of rules that find software design related problems.
class Foo { public function bar($param) { if ($param === 42) { exit(23); } } }
class Foo { public function bar($param) { if ($param === 42) { eval('$param = 23;'); } } }
class Foo { public function bar($param) { A: if ($param === 42) { goto X; } Y: if (time() % 42 === 23) { goto Z; } X: if (time() % 23 === 42) { goto Y; } Z: return 42; } }
class Foo { /** * @var \foo\bar\X */ private $x = null; /** * @var \foo\bar\Y */ private $y = null; /** * @var \foo\bar\Z */ private $z = null; public function setFoo(\Foo $foo) {} public function setBar(\Bar $bar) {} public function setBaz(\Baz $baz) {} /** * @return \SplObjectStorage * @throws \OutOfRangeException * @throws \InvalidArgumentException * @throws \ErrorException */ public function process(Iterator $it) {} // ... }
Naming Rules
The Naming Ruleset contains a collection of rules about names - too long, too short, and so forth.
class Something { private $q = 15; // VIOLATION - Field public static function main( array $as ) { // VIOLATION - Formal $r = 20 + $this->q; // VIOLATION - Local for (int $i = 0; $i < 10; $i++) { // Not a Violation (inside FOR) $r += $this->q; } } }
class Something { protected $reallyLongIntName = -3; // VIOLATION - Field public static function main( array $argumentsList[] ) { // VIOLATION - Formal $otherReallyLongName = -5; // VIOLATION - Local for ($interestingIntIndex = 0; // VIOLATION - For $interestingIntIndex < 10; $interestingIntIndex++ ) { } } }
class ShortMethod { public function a( $index ) { // Violation } }
class MyClass { // this is bad because it is PHP 4 style public function MyClass() {} // this is good because it is a PHP 5 constructor public function __construct() {} }
class Foo { const MY_NUM = 0; // ok const myTest = ""; // fail }
class Foo { /** * @return boolean */ public function getFoo() {} // bad /** * @return bool */ public function isFoo(); // ok /** * @return boolean */ public function getFoo($bar); // ok, unless checkParameterizedMethods=true }
Unused Code Rules
The Unused Code Ruleset contains a collection of rules that find unused code.
class Something { private static $FOO = 2; // Unused private $i = 5; // Unused private $j = 6; public function addOne() { return $this->j++; } }
class Foo { public function doSomething() { $i = 5; // Unused } }
class Something { private function foo() {} // unused }
class Foo { private function bar($howdy) { // $howdy is not used } }
PHP Code Sniffer
Ruleset name:Ruleset description:
Generic - Classes
Sniffs related to PHP classes
// bar.php class foo {} // foo.php class foo {}
Generic - Code Analysis
Sniff for detecting superfluously complex expressions and trivial but harder to spot mistakes
if($x == 5) { // nothing in the statements body }
class Foo { public function bar($x) { for (;$x > 5;) { /* ... */ } // No Init or Update part, may as well be: while ($x > 5) } }
class Foo { public function bar($x) { $a = array(1, 2, 3, 4); for ($i = 0; $i < count($a); $i++) { $a[$i] *= $i; } } }
function bar($x) { for ($i = 0; $i < 10; $i++) { for ($k = 0; $k < 20; $i++) { echo 'Hello'; } } }
if(true) { }
final class Bar { final public function() { } }
function bar($x, $y) { echo $y; // No $x usage }
class Foo { public function __construct($foo, $bar) { parent::__construct($foo, $bar); } }
Generic - Commenting
Detects patterns in comments
function worldPeace() { // TODO: Achieve this }
function worldPeace() { // FIXME: Not working yet }
Generic - ControlStructures
Sniffs related to control structures
// No braces! Gets reported! while(true) $x++; if($x > 10); break; // Also not allowed if($x) continue; //Everything needs braces if($x) { continue; }
Generic - Debug
Javascript related checking options. Show in general not be needed for phpcs. The named tools have to be installed for these sniffs to work.
Generic - Files
Sniffs related to file layout
// One of those lines is wrong. $eolChar decides which one echo "My line";\r\n echo "My other line";\n
Generic - Formatting
Sniffs related to basic formatting
$x = 6; $y = 7; $z = 6 * 7;
//Valid $stuff = 1; $otherStuff = 2; //Invalid $stuff = 1; $otherStuff = 2; //Also valid $myVar = 1 $myOtherVariable = 2; $x = 100; // Must not be indeted because of the blank line! while($x--) { echo $x, PHP_EOL; }
//Valid $x = (string)1; //Invalid $x = (string) 1;
//Valid $x = (string) 1; //Invalid $x = (string)1; //Also invalid $x = (string) 1; // Only 1 space allowed!
Generic - Functions
Sniffs related to function arguments and brace styles
//Invalid function foo($bar) { $bar++; } $x = 1; foo(&$x); //Valid function foo(&$bar) { $bar++; } $x = 1; foo($x);
NO SAMPLE AVAILABLE. Contributions welcome.
//Valid function foo() { // ... } //Invalid function foo() { // ... }
//Valid function foo() { // ... } //Invalid function foo() { // ... }
Generic - Metrics
Sniffs related to code metrics
Just check Wikipedia for the details
// Too deep! if($a) { if($b) { if($c) { if($d) { if($e) { if($f) { echo 'Stuff'; } } } } } }
Generic - NamingConventions
Sniffs related to naming things
class foo { // Old style constructor is not allowed! public function foo() { } }
const FOO = 'ok'; define('bad', 7); x = bad; // Not ok
Generic - PHP
Sniffs related to basic PHP usage
// Raises error set_magic_quotes_runtime(false);
<? echo 'This is not acceptable!';
delete($foo); sizeof($bar);
true, false, null // Good TRUE, FALSE, NULL // Bad TrUe, FaLsE, NuLl // Also bad
@stuff(); if(@$_GET['stuff']) { }
TRUE, FALSE, NULL // Good true, false, null // Bad TrUe, FaLsE, NuLl // Also bad
Generic - Strings
Sniffs related to string handling
echo "hi" . "ho"; // Also not allowed: echo "hi" ."there" ."how" ."are" ."you?";
Generic - VersionControl
Sniffs related to version control
Generic - WhiteSpace
Sniffs related whitespace usage for structuring source files.
if($x == 1) { TAB something(); }
if($x == 1) { echo "hi"; // should be 4 spaces not 8! } if($x == 1) { if($y == 2) { echo "hi"; // should be 8 spaces not 6! } }
MySource - PHP
Sniffs related to basic PHP usage
// Forbidden $class = "..."; $args = "1, 'string', 'dynamicBuildingArgs'"; eval("$x = new $bar($args)"); // Use $x = new $bar($arg1, $arg2); // No eval // Or use Reflection $x = new ReflectionClass($bar); $args = array(1, 'string', 'dynamicBuildingArgs'); $x->newInstanceArgs($args);
class foo { function bar() { $x = $_GET['hi']; // Forbidden! } }
// Forbidden function bar() { return foo(); } // Allowed function bar() { $x = foo(); return $x; }
PEAR - Classes
PEAR coding standard sniffs related to classes
<?php class Foo_Bar { //... code goes here } ?>
PEAR - Commenting
Sniffs related to how PEAR thinks structures should be commented.
Rules: - A doc comment must exist. - There has to be a blank newline after the short description. - There has to be a blank newline between the long and short description. - There has to be a blank newline between the long description and tags. - The tags have to appear in the following order. A question mark denotes optional tags: @category, @package, @subpackage?, @author, @copyright?, @license?, @version, @link, @see?, @since?, @deprecated? - Check the indentation of each tag. - Check required and optional tags and the format of their content. /** * Sample class short comment * * Sample long desc * * @category Something * @package MyPackage * @author name * @copyright something * @license BSD Licence * @version * @link url */ class TheCommentedClass { }
Rules: - A doc comment must exist. - There has to be a blank newline after the short description. - There has to be a blank newline between the long and short description. - There has to be a blank newline between the long description and tags. - A PHP version must be specified. - The tags have to appear in the following order. A question mark denotes optional tags: @category, @package, @subpackage?, @author, @copyright?, @license?, @version, @link, @see?, @since?, @deprecated? - Check the indentation of each tag. - Check required and optional tags and the format of their content. /** * Sample file short comment * * Sample long desc * * PHP Version: 1.2.3 * * @category Something * @package MyPackage * @author name * @copyright something * @license BSD Licence * @version * @link url */ /** * // class docs */ class MyClass { }
Rules: - A comment must exist - There must be a blank newline after the short description. - There must be a blank newline between the long and short description. - There must be a blank newline between the long description and tags. - Parameter names must represent those in the method. - Parameter comments must be in the correct order - Parameter comments must be complete - A space must be present before the first and after the last parameter - A return type tag must exists - There must be one blank line between body and headline comments - Any throw tag must have an exception class.
function foo { # Invalid comment // Valid comment /* Also valid */ }
PEAR - ControlStructures
Sniffs related to control structures
do {EOL...} while (...);EOL while (...) {EOL for (...) {EOL if (...) {EOL foreach (...) {EOL } else if (...) {EOL } elseif (...) {EOL } else {EOL do {EOL'
if (($condition1 || $condition2) && $condition3 && $condition4 && $condition5 ) { }
PEAR - Files
Sniffs related to how file inclusion works in PEAR.
// These are statements and should not have brackets. require_once('blank.inc'); require('blank.inc'); // Conditionally including a class file: use include_once if ($test) { require_once 'blank.inc'; require 'blank.inc'; } // Unconditionally including a class file: use require_once include_once 'blank.inc'; include 'blank.inc'; // These are ok if ($test) { include_once 'blank.inc'; include 'blank.inc'; }
PEAR - Formatting
Sniffs related to how code should be formatted.
// Valid $GLOBALS['TSFE']->additionalHeaderData[$this->strApplicationName] = $this->xajax->getJavascript(t3lib_extMgm::siteRelPath('nr_xajax')); // Invalid, wrong indention depth $GLOBALS['TSFE']->additionalHeaderData[$this->strApplicationName] = $this->xajax->getJavascript(t3lib_extMgm::siteRelPath('nr_xajax')); // Invalid, wrong indention depth $GLOBALS['TSFE']->additionalHeaderData[$this->strApplicationName] = $this->xajax->getJavascript(t3lib_extMgm::siteRelPath('nr_xajax')); // Invalid, equals sign needs to be on the next line $GLOBALS['TSFE']->additionalHeaderData[$this->strApplicationName] = $this->xajax->getJavascript(t3lib_extMgm::siteRelPath('nr_xajax'));
PEAR - Functions
Sniffs related to functions calling and declarations.
// Valid test($arg, $arg2); // All invalid test (); test( ); test() ; test( $arg); test( $arg ); test ( $arg ); // No error for multi line calls test( $a, $b ); // Heredocs dont need to be indented. test( <<<EOH Anyone want to recomment parse errors? EOH );
// Valid examples: function foo($a, $b, $c); function test( $a, $b ) { } function &testFunction($arg1, $arg2, ) { } function validateUrl( $url, $requireScheme=TRUE, array $allowedSchemes=array( 'http', 'https', ) ) { }
// Valid function myFunction($arg1, $arg2='hello') { } // Invalid function myFunction($arg2='hello', $arg1) { }
PEAR - NamingConventions
Sniffs related to PEAR naming conventions
class VALID_Name {} class Valid_Name {} class ValidName {} class Invalid_name {} // N needs to be uppercase class invalid_name {}
//Valid public function abc() {} private function _test() {} //Invalid public function _abc() {} private function test() {}
// Valid private $_foo; public $bar; //Invalid private $foo; public $_bar;
PEAR - WhiteSpace
Sniffs related to white space and indention in PEAR.
$object ->setBar($foo) ->setFoo($bar);
class Test { public function __construct() { } public function test1() { } public function test2() { } private function test3() { } }
//Valid indentions /** * This is a comment 1. * This is a comment 2. * This is a comment 3. * This is a comment 4. */ public function close() { // All ok. if (TRUE) { if (TRUE) { } else if (FALSE) { foreach ($tokens as $token) { switch ($token) { case '1': case '2': if (true) { if (false) { if (false) { if (false) { echo 'hello'; } } } } break; case '5': break; } do { while (true) { foreach ($tokens as $token) { for ($i = 0; $i < $token; $i++) { echo 'hello'; } } } } while (true); } } } }
PHPUnitStandard - Testing
A coding standard for PHPUnit tests written and mainted by @elblinkin. You will need to install an addition pear package in order to use these Sniffs! See: https://github.com/elblinkin/PHPUnit-CodeSniffer
class FooTest extends PHPUnit_Framework_TestCase { protected function setUp() { // ok } protected function somethingSomethingTestHelper() { // not allowed } }
// File: project/tests/phpunit/foo/bar/BazTest.php // Should contain: class Foo_Bar_BazTest { }
project/tests/phpunit/foo/bar/BazTest.php // ok project/tests/otherTool/but/by/accident/containts/a/phpunit/testcase.php // Not ok
project/tests/unit/FooTest.php // ok project/tests/unit/BarTes.php // not ok!
class FooTest { public function testBar() { $this->expectedOutputString('string(3) "foo"'); var_dump("foo"); // No! Use assertions for compairing! } }
class FooTest { public function testBar() { $this->expectedOutputString('foo'); echo "foo"; // No! Use assertions for compairing! } }
class FooTest extends PHPUnit_Framework_TestCase { public function testPrivateMethod() { $method = new ReflectionMethod( // No! Testing private methods is not allowed! 'Foo', 'doSomethingPrivate' ); $method->setAccessible(TRUE); $x = new Foo(); $this->assertEquals( 'blah', $x->doSomethingPrivate(); ); } }
protected function testImportantBehavior() { // Will not get executed because it is not public } /** * @dataProvider provideDataForTest */ public function testSomething($a, $b) { } protected function provideDataForTest() { // Test will fail as there is no executable data provider method }
PSR1 - Classes
PSR1 class rules
// Forbidden class Foo { } class Bar { } // Allowed: // Foo.php namespace \my\vendor; class Foo { } // Bar.php namespace \my\vendor; class Bar { }
PSR1 - Files
PSR1 file rules
// Forbidden if (!defined(FOO)) { die(1); } class Foo { // ... }
PSR2 - Classes
PSR2 class rules
- 1 space between keywords - 1 space after class name - The keyword must be on the same line as the class name - Only one interface may be specified per line in a multi-line implements declaration - The closing braces for interface methods must go on the next line after the body - Closing brace must be on a line by itself
- Property names should not be prefixed with an underscore to indicate visibility - The var keyword must not be used to declare a property - There must not be more than one property declared per statement - Visibility must be declared on all properties
PSR2 - ControlStructures
PSR2 control structures rules
- 0 spaces after opening bracket - 0 spaces before closing bracket
// Forbidden if ($x) { // ... } else if ($y) { // ... } // Allowed if ($x) { // ... } elseif ($y) { // ... }
- Keywords must be lowercase - Keywords must be indented $indent spaces from SWITCH keyword - CASE keyword must be followed by a single space - There must be no space before the colon in a case statement - Statements must not be defined using curly braces - Terminating statement must be indented to the same level as the CASE body - There must be a comment when fall-through is intentional in a non-empty case body
PSR2 - Files
PSR2 file rules
// Forbidden // --------- <?php //... ?>EOL // Allowed // ------- <?php //... >?\n EOL
PSR2 - Methods
PSR2 method rules
- Method name should not be prefixed with an underscore to indicate visibility - The static declaration must come after the visibility declaration - The final declaration must precede the visibility declaration - The abstract declaration must precede the visibility declaration
PSR2 - Namespaces
PSR2 method rules
// Forbidden <?php namespace foo\bar; class x { } // Allowed <?php namespace foo\bar; class x { }
- There must be one USE keyword per declaration - USE declarations must go after the first namespace declaration - There must be one blank line after the last USE statement
Squiz - Files
Squiz coding standard releated to files
Squiz - PHP
Squiz coding standard related to PHP usage
// Forbidden $var = !$foo; $var = ($foo || $bar); $var = ($foo === TRUE); $var = ($foo === TRUE || $bar === FALSE); $var = (!$foo);
// Forbidden $x = $y ? 1 : 2;
// Forbidden $x = $y = 4; if($x = 5) {} // Allowed $x = 4; $y = $x; if($x == 5) {}
// Forbidden echo "foo"; ob_end_flush(); // Allowed $output = ob_get_contents(); ob_end_clean();
// Forbidden for($i = 0; $i < count($i); ++$i) { } // Allowed $amountOfElements = count($i); for($i = 0; $i < $amountOfElements; ++$i) { }
// Forbidden <?php echo "hi"; ?> // Allowed <?php echo "hi"; ?> // Also forbidden <?php echo "hi"; ?>
// Forbidden functions sizeof delete print is_null create_function
// Forbidden function bar() { global $foo; }
// Forbidden $x = <<<BAR asdf jkl; BAR; // Allowed $x = 'asdf' . PHP_EOL . 'jkl;'
// Forbidden function bar() { function foo() { } }
// Forbidden Array_Map(...); array_Map(...); array_maP(...); Array_map(...); // Allowed array_map(...);
// Forbidden function foo() { echo "Hallo"; return; echo "World"; // Dead code }
Squiz - Scope
Squiz coding standard related to scoping
// Forbidden class foo { var $x = 1; } // Allowed class foo { public $a = 1; protected $b = 2; private $c = 3; }
// Forbidden class foo { function bar() { } } // Allowed class foo { public function bar() { } protected function baz() { } private fuction bak() { } }
// Forbidden class foo { public static function bar() { $this->isNotWorking(); } } // Allowed class foo { public function bar() { $this->isWorking(); } public static function baz() { self::isAlsoWorking(); } }
Squiz - WhiteSpace
Squiz coding standard sniffs related whitespace usage for structuring source files. The sniffs are general and can be widely used.
// Forbidden $x = ( array)"foo"; $x = (array )"foo"; // Allowed $x = (array)"foo";
// No spaces after opening bracket and no spaces before closing bracket // No blank lines at the start or the end of a control structure
class foo { public function bar() { } public function baz() { } }
// Forbidden echo"hi"; echo "hi"; $x = new StdClass(); // Allowed echo "hi"; $x = new StdClass();
// Forbidden if($x&& $y) {} if($x &&$y) {} if($x && $y) {} if($x && $y) {} // Allowed if($x && $y) {}
// Forbidden class foo { private $foo; private $bar; } // Allowed class foo { private $foo; private $bar; } // Also allowed class foo { private $foo; /** * @var string */ private $bar; }
// Forbidden $x = new StdClass(); $x -> foo = 1; // Allowed $x = new StdClass(); $x->foo = 1; echo "foo";
// Forbidden $x = $a&$b; $x=$y; $x=& $y; // Allowed $x = $a & $b; $x = $y; $x =& $y;
// Forbidden x = { a: 'x', b : 1, c : 2, } // Allowed x = { a: 'x', b: 1, c: 2, }
// Forbidden function foo() { } function foo() { echo "bar"; } // Allowed function foo() { }
// Forbidden public function protected function // Allowed public function protected function
// Forbidden $x = 1 ; echo "foo" ; // Allowed $x = 1; echo "foo";
Checks that no whitespace proceeds the first content of the file, exists after the last content of the file, resides after content on any line, or are two empty lines in functions.
Zend - Debug
Zend Studio (The IDE) related sniffs
Zend - Files
Sniffs related to files
// Valid <html>... <?php echo "hi!"; ?> ...</html> // Invalid <?php class Foo { } ?> // End of file here
Zend - NamingConventions
Zend Framework variable naming conventions
// Valid $varName = 'hello'; $varname = 'hello'; class MyClass { $varName = 'hello'; // This is ok as ZF uses it to denote private vars $_varName = 'hello'; } // Invalid $var_name = 'hello'; class MyClass { $var_name = 'hello'; } // Discouraged $varname2 = 'hello'; class MyClass { $varName2 = 'hello'; }