Rules list

Introduction

$HTTP_RAW_POST_DATA

$HTTP_RAW_POST_DATA is deprecated, and should be replaced by php://input.

$HTTP_RAW_POST_DATA is deprecated since PHP 5.6.

It is possible to ready by setting always_populate_raw_post_data to -1.

<?php

// PHP 5.5 and older
$postdata = $HTTP_RAW_POST_DATA;

// PHP 5.6 and more recent
$postdata = file_get_contents(php://input);

?>

See also $HTTP_RAW_POST_DATA.

Command Line Php/RawPostDataUsage
Analyzers CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

$this Belongs To Classes Or Traits

$this variable represents only the current object.

It is a pseudo-variable, and should be used within class’s or trait’s methods (except for static) and not outside.

PHP 7.1 is stricter and check for $this at several positions. Some are found by static analysis, some are dynamic analysis.

<?php

// as an argument
function foo($this) {
    // Using global
    global $this;
    // Using static (not a property)
    static $this;

    // Can't unset it
    unset($this);

    try {
        // inside a foreach
        foreach($a as $this) {  }
        foreach($a as $this => $b) {  }
        foreach($a as $b => $this) {  }
    } catch (Exception $this) {
        // inside a catch
    }

    // with Variable Variable
    $a = this;
    $$a = 42;
}

class foo {
    function bar() {
        // Using references
        $a =& $this;
        $a = 42;

        // Using 'extract(), 'parse_str() or similar functions
        extract([this => 42]);  // throw new Error(Cannot re-assign $this)
        var_dump($this);
    }

    static function '__call($name, $args) {
        // Using '__call
        var_dump($this); // prints object(C)#1 (0) {}, php-7.0 printed NULL
        $this->test();   // prints ops
    }

}
?>
Command Line Classes/ThisIsForClasses
Analyzers Analyze

$this Is Not An Array

$this variable represents the current object and it is not an array, unless the class (or its parents) has the ‘ArrayAccess interface.

<?php

// $this is an array
class Foo extends 'ArrayAccess {
    function bar() {
        ++$this[3];
    }
}

// $this is not an array
class Foo2 {
    function bar() {
        ++$this[3];
    }
}

?>
Command Line Classes/ThisIsNotAnArray
Analyzers Analyze

$this Is Not For Static Methods

Static methods shouldn’t use $this variable.

$this variable represents an object (the current object) and it is not compatible with a static method, which may operate without any object.

<?php

class foo {
    static public function bar() {
        return $this->a;   // No $this usage in a static method
    }
}

?>
Command Line Classes/ThisIsNotForStatic
clearPHP no-static-this
Analyzers Analyze

** For Exponent

PHP 5.6 introduced the operator ‘** to provide exponents, instead of the slower function ‘pow().

<?php
    $cube = pow(2, 3); // 8

    $cubeInPHP56 = 2 '** 3; // 8
?>

If the code needs to be backward compatible to 5.5 or less, don’t use the new operator.

Command Line Php/NewExponent
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55

::class

PHP 5.5 introduced a special class constant, relying on the ‘class’ keyword. It will solve the classname that is used in the left part of the operator.

<?php
class foo {
    public function bar( ) {
        echo ClassName::class;
    }
}

$f = new Foo( );
$f->bar( );
// return Namespace\ClassName

?>
Command Line Php/StaticclassUsage
Analyzers CompatibilityPHP53, CompatibilityPHP54

@ Operator

@ is the ‘no scream’ operator : it will suppress error output.

<?php

// Set x with incoming value, or else null.
$x = @$_GET['x'];

?>

This operator is actually very slow : it will process the error all the way up, and finally decide not to display it. It is often faster to check the conditions first, then run the method without @.

You may also set display_error to 0 in the php.ini : this will avoid user’s error display, but will keep the error in the PHP logs, for later processing.

The only situation where @ is useful is when a native PHP function displays errors messages when error happens and there is no way to check it from the code.

This is the case with ‘fopen(), ‘stream_socket_server(), ‘token_get_all().

Command Line Structures/Noscream
clearPHP no-noscream
Analyzers Analyze

Abstract Static Methods

Methods cannot be both abstract and static. Static methods belong to a class, and will not be overridden by the child class. For normal methods, PHP will start at the object level, then go up the hierarchy to find the method. With static, you have to mention the name, or use Late Static Binding, with self or static. Hence, it is useless to have an abstract static method : it should be a simple static method.

A child class is able to declare a method with the same name than a static method in the parent, but those two methods will stay independant.

This is not the case anymore in PHP 7.0+.

<?php

abstract class foo {
    // This is not possible
    static abstract function bar() ;
}

?>

See also Why does PHP 5.2+ disallow abstract static class methods?.

Command Line Classes/AbstractStatic
Analyzers Analyze

Access Protected Structures

It is not allowed to access protected properties or methods from outside the class or its relatives.

Command Line Classes/AccessProtected
Analyzers Analyze

Accessing Private

List of calls to private properties/methods that will compile but yield some fatal error upon execution.

<?php

class a {
    private $a;
}

class b extends a {
    function c() {
        $this->a;
    }
}

?>
Command Line Classes/AccessPrivate
Analyzers Analyze

Action Should Be In Controller

Action methods should be in a controller and public.

<?php

use Zend\Mvc\Controller\AbstractActionController;

class SomeController extends AbstractActionController
{
    // Good method
    public function indexAction()
    {
        doSomething();
    }

    // Bad method : protected
    // turn protected into public, or drop the Action suffix
    protected function protectedIndexAction()
    {
        doSomething();
    }

    // Bad method : private
    // turn private into public, or drop the Action suffix
    protected function privateIndexAction()
    {
        doSomething();
    }

}


?>
Command Line ZendF/ActionInController
Analyzers ZendFramework

Adding Zero

Adding 0 is useless, as 0 is the neutral element for addition. It triggers a cast to integer, though behavior changes from PHP 7.0 to PHP 7.1.

<?php

$a = 123 + 0;
$a = 0 + 123;

// Also works with minus
$b = 0 - $c; // drop the 0, but keep the minus
$b = $c - 0; // drop the 0 and the minus

$a += 0;
$a -= 0;

?>

If it is used to type cast a value to integer, then casting (integer) is clearer.

Command Line Structures/AddZero
clearPHP no-useless-math
Analyzers Analyze

Aliases Usage

PHP manual recommends to avoid function aliases.

Some functions have several names, and both may be used the same way. However, one of the names is the main name, and the others are aliases. Aliases may be removed or change or dropped in the future. Even if this is not forecast, it is good practice to use the main name, instead of the aliases.

<?php

// official way to count an array
$n = count($array);

// official way to count an array
$n = sizeof($array);

?>

Aliases are compiled in PHP, and do not provide any performances over the normal function.

Aliases are more likely to be removed later, but they have been around for a long time.

See documentation : List of function aliases.

Command Line Functions/AliasesUsage
clearPHP no-aliases
Analyzers Analyze

All Uppercase Variables

Usually, global variables are all in uppercase, so as to differentiate them easily. Though, this is not always the case, with examples like $argc, $argv or $http_response_header.

When using custom variables, try to use lowercase $variables, $camelCase, $sturdyCase or $snake_case.

<?php

// PHP super global, also identified by the initial _
$localVariable = $_POST;

// PHP globals
$localVariable = $GLOBALS['HTTPS'];

?>

Predefined Variables

Command Line Variables/VariableUppercase
Analyzers Coding Conventions

Already Parents Interface

The same interface is implemented by a class and one of its children.

That way, the child doesn’t need to implement the interface, nor define its methods to be an instance of the interface.

<?php

interface i {
    function i();
}

class A implements i {
    function i() {
        return '__METHOD__;
    }
}

// This implements is useless.
class AB extends A implements i {
    // No definition for function i()
}

// Implements i is understated
class AB extends A {
    // redefinition of the i method
    function i() {
        return '__METHOD__.' ';
    }
}

$x = new AB;
var_dump($x 'instanceof i);
// true

$x = new AC;
var_dump($x 'instanceof i);
// true

?>
Command Line Interfaces/AlreadyParentsInterface
Analyzers Analyze

Altering Foreach Without Reference

Foreach() loop that should use a reference.

When using a foreach loop that modifies the original source, it is recommended to use referenced variables, rather than access the original value with $source[$index].

Using references is then must faster, and easier to read.

<?php

// Using references in foreach
foreach($source as $key => &$value) {
    $value = newValue($value, $key);
}

// Avoid foreach : use array_map
$source = array_walk($source, 'newValue');
    // Here, $key MUST be the second argument or newValue

// Slow version to update the array
foreach($source as $key => &$value) {
    $source[$key] = newValue($value, $key);
}
?>

You may also use ‘array_walk() or ‘array_map() (when $key is not used) to avoid the use of foreach.

Command Line Structures/AlteringForeachWithoutReference
clearPHP use-reference-to-alter-in-foreach
Analyzers Analyze

Alternative Syntax Consistence

PHP allows for two syntax : the alternative syntax, and the classic syntax.

The classic syntax is almost always used. When used, the alternative syntax is used in templates.

This analysis reports files that are using both syntax at the same time. This is confusing.

<?php

// Mixing both syntax is confusing.
foreach($array as $item) :
    if ($item > 1) {
        print $item elements\n;
    } else {
        print $item element\n;
    }
endforeach;

?>
Command Line Structures/AlternativeConsistenceByFile
Analyzers Analyze

Always Anchor Regex

Unanchored regex finds the requested pattern, and leaves room for malicious content.

Without ^ and $, the regex is searches for any pattern that satisfies its criteria, leaving any unused part of the string available for abitrary content. It is recommended to use both anchor

<?php

$birthday = getSomeDate($_GET);

// Permissive version : $birthday = '1970-01-01<script>xss();</script>';
if (!preg_match('/\d{4}-\d{2}-\d{2}/', $birthday) {
    error('Wrong data format for your birthday!');
}

// Restrictive version : $birthday = '1970-01-01';
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $birthday) {
    error('Wrong data format for your birthday!');
}

echo 'Your birthday is on '.$birthday;

?>

Note that $ may be a line ending, still leaving room after it for injection.

<?php

$birthday = '1970-01-01'.PHP_EOL.'<script>xss();</script>';

?>

This analysis reports false positive when the regex is used to search a pattern in a much larger string. Check if this rule doesn’t apply, though.

See also CWE-625: Permissive Regular Expression.

Command Line Security/AnchorRegex
Analyzers Security

Always Positive Comparison

Some PHP native functions, such as ‘count(), strlen(), or ‘abs() only returns positive or null values.

When comparing them to 0, the following expressions are always true and should be avoided.

<?php

$a = [1, 2, 3];

var_dump(count($a) >= 0);
var_dump(count($a) < 0);

?>
Command Line Structures/NeverNegative
Analyzers Analyze

Ambiguous Array Index

Those indexes are defined with different types, in the same array.

Array indices only accept integers and strings, so any other type of literal is reported.

<?php

$x = [ 1  => 1,
      '1' => 2,
      1.0 => 3,
      true => 4];
// $x only contains one element : 1 => 4

// Still wrong, immediate typecast to 1
$x[1.0]  = 5;
$x[true] = 6;

?>

They are indeed distinct, but may lead to confusion.

Command Line Arrays/AmbiguousKeys
Analyzers Analyze

Ambiguous Static

Methods or properties are sometimes defined static, and sometimes not. This is error prone, as it require a better knowledge of the code to make it static or not.

Try to keep the static-ness of methods simple, and unique. Consider renaming the methods to distinguish them easily. A method and a static method have probably different responsabilities.

<?php

class a {
    function mixedStaticMethod() {}
}

class b {
    static function mixedStaticMethod() {}
}

/... a lot more code later .../

$c->mixedStaticMethod();
// or
$c::mixedStaticMethod();

?>
Command Line Classes/AmbiguousStatic
Analyzers Analyze

Anonymous Classes

Anonymous classes.

<?php

// Anonymous class, available since PHP 7.0
$object = new class { function '__construct() { echo '__METHOD__; } };

?>
Command Line Classes/Anonymous
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Assign Default To Properties

Properties may be assigned default values at declaration time. Such values may be later modified, if needed.

<?php

class foo {
    private $propertyWithDefault = 1;
    private $propertyWithoutDefault;
    private $propertyThatCantHaveDefault;

    public function '__construct() {
        // Skip this extra line, and give the default value above
        $this->propertyWithoutDefault = 1;

        // Static expressions are available to set up simple computation at definition time.
        $this->propertyWithoutDefault = OtherClass::CONSTANT + 1;

        // Arrays, just like scalars, may be set at definition time
        $this->propertyWithoutDefault = [1,2,3];

        // Objects or resources can't be made default. That is OK.
        $this->propertyThatCantHaveDefault = fopen('/path/to/file.txt');
        $this->propertyThatCantHaveDefault = new Fileinfo();
    }
}

?>

Default values will save some instructions in the constructor, and makes the value obvious in the code.

Command Line Classes/MakeDefault
clearPHP use-properties-default-values
Analyzers Analyze

Assign With And

The lettered logical operators yield to assignation.

It is recommended to use the &&, ^ and || operators, instead of and, or and xor, to prevent confusion.

<?php

// The expected behavior is
// The following are equivalent
 $a =  $b  && $c;
 $a = ($b && $c);

// The unexpected behavior is
// The following are equivalent
 $a = $b  and $c;
($a = $b) and $c;

?>

See also Operator precedence.

Command Line Php/AssignAnd
Analyzers Analyze

Assigned Twice

The same variable is assigned twice in the same function.

While this is possible and quite common, it is also a good practice to avoid changing a value from one literal to another. It is far better to assign the new value to

Incremental changes to a variables are not reported here.

<?php

function foo() {
    // incremental changes of $a;
    $a = 'a';
    $a++;
    $a = uppercase($a);

    $b = 1;
    $c = bar($b);
    // B changed its purpose. Why not call it $d?
    $b = array(1,2,3);

    // This is some forgotten debug
    $e = $config->getSomeList();
    $e = array('OneElement');
}

?>
Command Line Variables/AssignedTwiceOrMore
Analyzers Analyze

Avoid Concat In Loop

Concatenations inside a loop generate a lot of temporary variables. They are accumulated and tend to raise the memory usage, leading to slower performances.

It is recommended to store the values in an array, and then use ‘implode() on that array to make the concatenation at once. The effect is positive when the source array has at least 50 elements.

<?php

// Concatenation in one operation
$tmp = array();
foreach(data_source() as $data) {
    $tmp[] = $data;
}
$final = implode('', $tmp);

// Concatenation in many operations
foreach(data_source() as $data) {
    $final .= $data;
}

?>

The same doesn’t apply to addition and multiplication, with ‘array_sum() and array_multiply(), as those operations work on the current memory allocation, and don’t need to allocate new memory at each step.

Command Line Performances/NoConcatInLoop
Analyzers Performances

Avoid Double Prepare

Double prepare shoud be avoided, for security reasons.

When preparing in two phases, any placeholder from the first part may be escaped by the second prepare, leading to their neutralization. This way, injecting ‘ %s ‘, leads to creating %s outside quotes : ‘ ‘ %s ‘ ‘ (external quotes are from the first prepare, while the internal set of quotes are from the second).

It is recommended to build the query and to prepare it in one call, to avoid such pitfall.

<?php

// Only one prepare
    $args = [$u, $t];
    $res = $wpdb->prepare(' select * from table user = %s and type = %s', $args);

// also only one prepare
    $args = [$u];
    $query = 'select * from table user = %s and type = %s';
    if ( $condition) {
        $query .= ' and type = %s';
        $args[] = $t;
    }
    $res = $wpdb->prepare($query, $args);

// double prepare
    $where = $wpdb->prepare('where user = %s', $s);
    $res = $wpdb->prepare(' select * from table $where AND other = %d', );

?>

See also On WordPress Security and Contributing and Disclosure: WordPress WPDB SQL Injection - Technical.

Command Line Wordpress/DoublePrepare
Analyzers Wordpress

Avoid Large Array Assignation

Avoid setting large arrays to local variables. This is done everytime the function is called.

There are different ways to avoid this : inject the array, build the array once. Using an constant or even a global variable is faster.

The effect on small arrays (less than 10 elements) is not significant. Arrays with 10 elements or more are reported here. The effect is also more important on functions that are called often, or within loops.

<?php

// with constants, for functions
const ARRAY = array(1,2,3,4,5,6,7,8,9,10,11);
function foo() {
    $array = ARRAY;
    //more code
}

// with class constants, for methods
class x {
    const ARRAY = array(1,2,3,4,5,6,7,8,9,10,11);
    function foo() {
        $array = self::ARRAY;
        //more code
    }
}

// with properties, for methods
class x {
    private $array = array(1,2,3,4,5,6,7,8,9,10,11);

    function foo() {
        $array = $this->array;
        //more code
    }
}

// injection, leveraging default values
function foo($array = array(1,2,3,4,5,6,7,8,9,10,11)) {
    //more code
}

// local cache with static
function foo() {
    static $array;
    if ($array === null) {
        $array = array(1,2,3,4,5,6,7,8,9,10,11);
    }

    //more code
}

// Avoid creating the same array all the time in a function
class x {
    function foo() {
        // assign to non local variable is OK.
        // Here, to a property, though it may be better in a '__construct or as default values
        $this->s = array(1,2,3,4,5,6,7,8,9,10,11);

        // This is wasting resources, as it is done each time.
        $array = array(1,2,3,4,5,6,7,8,9,10,11);
    }
}

?>
Command Line Structures/NoAssignationInFunction
Analyzers Performances

Avoid Non Wordpress Globals

Refren using any global variable that is not Wordpress’s own.

Global variables are available for write and read across the whole application, making their data both easily accessible, and difficult to track when a unexpected change happen. It is recommended to rely on a mix of arguments passing and classes structures to reduce the code of any variable to a smaller part of the code.

<?php

my_hook() {
    // This is a Wordpress global
    $GLOBALS['is_safari'] = true;

    // is_iphone7 is not a Wordpress variable
    global $is_iphone7;
}

?>

See also Global Variables

Command Line Wordpress/AvoidOtherGlobals
Analyzers Wordpress

Avoid Optional Properties

Avoid optional properties, to prevent litering the code with existence checks.

When a property has to be checked once for existence, it is safer to check it each time. This leads to a decrease in readability.

Either make sure the property is set with an actual object rather than with null, or use a void object. A void object offers the same interface than the expected object, but does nothing. It allows calling its methods, without running into a Fatal error, nor testing it.

<?php

// Example is courtesy 'The Coding Machine' : it has been adapted from its original form. See link below.

class MyMailer {
    private $logger;

    public function '__construct(LoggerInterface $logger = null) {
        $this->logger = $logger;
    }

    private function sendMail(Mail $mail) {
        // Since $this->logger may be null, it must be tested anytime it is used.
        if ($this->logger) {
            $this->logger->info('Mail successfully sent.');
        }
    }
}

?>

See also Avoid optional services as much as possible, The Null Object Pattern – Polymorphism in Domain Models, and Practical PHP Refactoring: Introduce Null Object.

Command Line Classes/AvoidOptionalProperties
Analyzers Analyze

Avoid Parenthesis

Avoid Parenthesis for language construct. Languages constructs are a few PHP native elements, that looks like functions but are not.

Among other distinction, those elements cannot be directly used as variable function call, and they may be used with or without parenthesis.

<?php

// normal usage of include
include 'file.php';

// This looks like a function and is not
include('file2.php');

?>

The usage of parenthesis actually give some feeling of confort, it won’t prevent PHP from combining those argument with any later operators, leading to unexpected results.

Even if most of the time, usage of parenthesis is legit, it is recommended to avoid them.

Command Line Structures/PrintWithoutParenthesis
Analyzers Analyze

Avoid Those Hash Functions

The following cryptographic algorithms are considered unsecure, and should be replaced with new and more performant algorithms.

MD2, MD4, MD5, SHA0, SHA1, CRC, DES, 3DES, RC2, RC4.

When possible, avoid using them, may it be as PHP functions, or hashing function configurations (mcrypt, hash...).

<?php

// Weak cryptographic algorithm
echo md5('The quick brown fox jumped over the lazy dog.');

// Weak crypotgraphic algorthim, used with a modern PHP extension (easier to update)
echo hash('md5', 'The quick brown fox jumped over the lazy dog.');

// Strong crypotgraphic algorthim, used with a modern PHP extension
echo hash('sha156', 'The quick brown fox jumped over the lazy dog.');

?>

Weak crypto are commonly used for hashing values when caching them. In such cases, security is not a primary concern. However, it may later become such, when hackers get access to the cache folders, or if the cached identifier is published. As a preventive protection, it is recommended to always use a secure hashing function.

See also ` <https://en.wikipedia.org/wiki/Secure_Hash_Algorithms>`_.

Command Line Security/AvoidThoseCrypto
Analyzers Security

Avoid Using stdClass

stdClass is the default class for PHP. It is instantiated when PHP needs to return a object, but no class is specifically available.

It is recommended to avoid instantiating this class, nor use it is any way.

<?php

$json = '{a:1,b:2,c:3}';
$object = json_decode($json);
// $object is a stdClass, as returned by json_decode

// Fast building of $o
$a = [];
$a['a'] = 1;
$a['b'] = 2;
$a['c'] = 3;
json_encode( (object) $a);

// Slow building of $o
$o = new stdClass();
$o->a = 1;
$o->b = 2;
$o->c = 3;
json_encode($o);

?>

If you need a stdClass object, it is faster to build it as an array, then cast it, than instantiate stdClass. This is a micro-optimisation.

Command Line Php/UseStdclass
Analyzers Analyze

Avoid array_push()

‘array_push() is slower than the [] operator.

This is also true if the [] operator is called several times, while ‘array_push() may be called only once. And using count after the push is also faster than collecting ‘array_push() return value.

<?php

$a = [1,2,3];
// Fast version
$a[] = 4;

$a[] = 5;
$a[] = 6;
$a[] = 7;
$count = count($a);

// Slow version
array_push($a, 4);
$count = array_push($a, 5,6,7);

// Multiple version :
$a[] = 1;
$a[] = 2;
$a[] = 3;
array_push($a, 1, 2, 3);


?>

This is a micro-optimisation.

Command Line Performances/AvoidArrayPush
Analyzers Performances

Avoid array_unique()

The native function ‘array_unique() is much slower than using other alternative, such as ‘array_count_values(), ‘array_flip()/‘array_keys(), or even a ‘foreach() loops.

<?php

// using 'array_unique()
$uniques = array_unique($someValues);

// When values are strings or integers
$uniques = array_keys(array_count_values($someValues));
$uniques = array_flip(array_flip($someValues))

//even some loops are faster.
$uniques = [];
foreach($someValues as $s) {
    if (!in_array($uniques, $s)) {
        $uniques[] $s;
    }
}

?>
Command Line Structures/NoArrayUnique
Analyzers Performances

Avoid get_class()

get_class() should be replaced with the ‘instanceof operator to check the class of an object.

get_class() will only compare the full namespace name of the object’s class, while ‘instanceof actually resolve the name, using the local namespace and aliases.

<?php

    use Stdclass as baseClass;

    function foo($arg) {
        // Slow and prone to namespace errors
        if (get_class($arg) === 'Stdclass') {
            // doSomething()
        }
    }

    function bar($arg) {
        // Faster, and uses aliases.
        if ($arg 'instanceof baseClass) {
            // doSomething()
        }
    }
?>
Command Line Structures/UseInstanceof
Analyzers none

Avoid glob() Usage

‘glob() and ‘scandir() sorts results by default. If you don’t need that sorting, save some time by requesting NOSORT with those functions.

Besides, whenever possible, use ‘scandir() instead of ‘glob().

<?php

// Scandir without sorting is the fastest.
scandir('docs/', SCANDIR_SORT_NONE);

// Scandir sorts files by default. Same as above, but with sorting
scandir('docs/');

// glob sorts files by default. Same as below, but no sorting
glob('docs/*', GLOB_NOSORT);

// glob sorts files by default. This is the slowest version
glob('docs/*');

?>

Using ‘opendir() and a while loop may be even faster.

This analysis skips ‘scandir() and ‘glob() if they are explicitely configured with flags (aka, sorting is explicitely needed).

Glob() accepts wildchar, that may not easily replaced with ‘scandir() or ‘opendir().

See also Putting glob to the test.

Command Line Performances/NoGlob
Analyzers Performances

Avoid sleep()/usleep()

‘sleep() and ‘usleep() help saturate the web server.

Pausing the script for a specific amount of time means that the Web server is also making all related ressources sleep, such as database, sockets, session, etc. This may used to set up a DOS on the server.

<?php

$begin = microtime(true);
checkLogin($user, $password);
$end   = microtime(true);

// Making all login checks looks the same
usleep(1000000 - ($end - $begin) * 1000000);

// Any hit on this page now uses 1 second, no matter if load is high or not
// Is it now possible to saturate the webserver in 1 s ?

?>

As much as possible, avoid delaying the end of the script.

‘sleep() and ‘usleep() have less impact in commandline (CLI).

Command Line Security/NoSleep
Analyzers Security

Bail Out Early

When using conditions, it is recommended to quit in the current context, and avoid else clause altoghter.

The main benefit is to make clear the method applies a condition, and stop quickly went it is not satisfied. The main sequence is then focused on the useful code.

This works with the ‘break, ‘continue, throw and goto keywords too, depending on situations.

<?php

// Bailing out early, low level of indentation
function foo1($a) {
    if ($a > 0) {
        return false;
    }

    $a++;
    return $a;
}

// Works with 'continue too
foreach($array as $a => $b) {
    if ($a > 0) {
        'continue false;
    }

    $a++;
    return $a;
}

// No need for else
function foo2($a) {
    if ($a > 0) {
        return false;
    } else {
        $a++;
    }

    return $a;
}

// No need for else : return goes into then.
function foo3($a) {
    if ($a < 0) {
        $a++;
    } else {
        return false;
    }

    return $a;
}

?>
Command Line Structures/BailOutEarly
Analyzers Analyze

Binary Glossary

List of all the integer values using the binary format.

<?php

$a = 0b10;
$b = 0B0101;

?>
Command Line Type/Binary
Analyzers CompatibilityPHP53

Bracketless Blocks

PHP allows one liners as ‘for(), ‘foreach(), ‘while(), ‘do..while() loops, or as then/else expressions.

It is generally considered a bad practice, as readability is lower and there are non-négligeable risk of excluding from the loop the next instruction.

<?php

// Legit one liner
foreach(range('a', 'z') as $letter) ++$letterCount;

// More readable version, even for a one liner.
foreach(range('a', 'z') as $letter) {
    ++$letterCount;
}

?>

‘switch() cannot be without bracket.

Command Line Structures/Bracketless
Analyzers Coding Conventions

Break Outside Loop

Starting with PHP 7, ‘break or ‘continue that are outside a loop (for, ‘foreach(), do...`’while() <http://php.net/manual/en/control-structures.while.php>`_, ‘while()) or a ‘switch() statement won’t compile anymore.

It is not possible anymore to include a piece of code inside a loop that will then ‘break.

<?php

    // outside a loop : This won't compile
    'break 1;

    foreach($array as $a) {
        'break 1; // Compile OK

        'break 2; // This won't compile, as this 'break is in one loop, and not 2
    }

    foreach($array as $a) {
        foreach($array2 as $a2) {
            'break 2; // OK in PHP 5 and 7
        }
    }
?>
Command Line Structures/BreakOutsideLoop
Analyzers Analyze, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Break With 0

Cannot ‘break 0, as this makes no sense. Break 1 is the minimum, and is the default value.

<?php
    // Can't 'break 0. Must be 1 or more, depending on the level of nesting.
    for($i = 0; $i < 10; $i++) {
        'break 0;
    }

    for($i = 0; $i < 10; $i++) {
        for($j = 0; $j < 10; $j++) {
            'break 2;
        }
    }

?>
Command Line Structures/Break0
Analyzers CompatibilityPHP53

Break With Non Integer

When using a ‘break, the argument of the operator must be a positive non-null integer literal or be omitted.

Other values were acceptable in PHP 5.3 and previous version, but this is now reported as an error.

<?php
    // Can't 'break $a, even if it contains an integer.
    $a = 1;
    for($i = 0; $i < 10; $i++) {
        'break $a;
    }

    // can't 'break on float
    for($i = 0; $i < 10; $i++) {
        for($j = 0; $j < 10; $j++) {
            'break 2.2;
        }
    }

?>
Command Line Structures/BreakNonInteger
Analyzers CompatibilityPHP54, CompatibilityPHP70, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Buried Assignation

Those assignations are buried in the code, and placed in unexpected situations.

They are difficult to spot, and may be confusing. It is advised to place them in a more visible place.

<?php

// $b may be assigned before processing $a
$a = $c && ($b = 2);

// legit syntax, but the double assignation is not obvious.
for($i = 2, $j = 3; $j < 10; $j++) {

}
?>
Command Line Structures/BuriedAssignation
Analyzers Analyze

CakePHP 2.5.0 Undefined Classes

CakePHP classes, interfaces and traits that are not defined in version 2.5.0.

Command Line Cakephp/Cakephp25
Analyzers Cakephp

CakePHP 2.6.0 Undefined Classes

CakePHP classes, interfaces and traits that are not defined in version 2.6.0. 5 new classes

Command Line Cakephp/Cakephp26
Analyzers Cakephp

CakePHP 2.7.0 Undefined Classes

CakePHP classes, interfaces and traits that are not defined in version 2.7.0. 12 new classes

2 removed classes

Command Line Cakephp/Cakephp27
Analyzers Cakephp

CakePHP 2.8.0 Undefined Classes

CakePHP classes, interfaces and traits that are not defined in version 2.8.0. 8 new classes

Command Line Cakephp/Cakephp28
Analyzers Cakephp

CakePHP 2.9.0 Undefined Classes

CakePHP classes, interfaces and traits that are not defined in version 2.9.0. 16 new classes

2 removed classes

Command Line Cakephp/Cakephp29
Analyzers Cakephp

CakePHP 3.0 Deprecated Class

According to the Cake 3.0 migration guide, the following class is deprecated and should be removed.

  • Set (CakeUtilitySet) : replace it with Hash (CakeUtilityHash)
Command Line Cakephp/Cake30DeprecatedClass
Analyzers Cakephp

CakePHP 3.0.0 Undefined Classes

CakePHP classes, interfaces and traits that are not defined in version 3.0.0. 754 new classes 13 new interfaces 34 new traits

1062 removed classes 7 removed interfaces

Command Line Cakephp/Cakephp30
Analyzers Cakephp

CakePHP 3.1.0 Undefined Classes

CakePHP classes, interfaces and traits that are not defined in version 3.1.0. 64 new classes 5 new interfaces 5 new traits

16 removed classes

Command Line Cakephp/Cakephp31
Analyzers Cakephp

CakePHP 3.2.0 Undefined Classes

CakePHP classes, interfaces and traits that are not defined in version 3.2.0. 27 new classes 4 new interfaces 4 new traits

1 removed classe

Command Line Cakephp/Cakephp32
Analyzers Cakephp

CakePHP 3.3 Deprecated Class

According to the Cake 3.3 migration guide, the following class is deprecated and should be removed.

  • Mcrypt (CakeUtilityCryptoMcrypt) : replace it with CakeUtilityCryptoOpenssl or ext/openssl
Command Line Cakephp/Cake33DeprecatedClass
Analyzers Cakephp

CakePHP 3.3.0 Undefined Classes

CakePHP classes, interfaces and traits that are not defined in version 3.3.0. 93 new classes 5 new interfaces 1 new trait

19 removed classes 1 removed interface

Command Line Cakephp/Cakephp33
Analyzers Cakephp

CakePHP 3.4.0 Undefined Classes

CakePHP classes, interfaces and traits that are not defined in version 3.4.0. 41 new classes 1 new interface 1 new trait

16 removed classes 2 removed traits

Command Line Cakephp/Cakephp34
Analyzers Cakephp

CakePHP Used

CakePHP classes, interfaces and traits being used in the code.

<?php

namespace App\Controller;

use Cake\Controller\Controller;

class AppController extends Controller
{

    public function initialize()
    {
        // Always enable the CSRF component.
        $this->loadComponent('Csrf');
    }

}

?>

See also CakePHP.

Command Line Cakephp/CakePHPUsed
Analyzers Cakephp

Calltime Pass By Reference

PHP doesn’t allow when a value is turned into a reference at functioncall, since PHP 5.4.

Either the function use a reference in its signature, either the reference won’t pass.

<?php

function foo($name) {
    $arg = ucfirst(strtolower($name));
    echo 'Hello '.$arg;
}

$a = 'name';
foo(&$a);

?>
Command Line Structures/CalltimePassByReference
Analyzers CompatibilityPHP54, CompatibilityPHP70, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Can’t Extend Final

It is not possible to extend final classes.

Since PHP fails with a fatal error, this means that the extending class is probably not used in the rest of the code. Check for dead code.

<?php
    // File Foo
    final class foo {
        public final function bar() {
            // doSomething
        }
    }
?>

In a separate file :

<?php
    // File Bar
    class bar extends foo {

    }
?>
Command Line Classes/CantExtendFinal
Analyzers Analyze, Dead code

Cant Inherit Abstract Method

Inheriting abstract methods was made available in PHP 7.2. In previous versions, it emits a Fatal error.

<?php

abstract class A           { abstract function bar(stdClass $x);  }
abstract class B extends A { abstract function bar($x): stdClass; }

//   Fatal error: Can't inherit abstract function A::bar()
?>

See also PHP RFC: Allow abstract function override.

Command Line Classes/CantInheritAbstractMethod
Analyzers CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Cant Use Return Value In Write Context

Until PHP 5.5, it was not possible to use directly function calls inside an ‘empty() function call : they were met with a ‘Can’t use function return value in write context’ fatal error.

<?php

function foo($boolean) {
    return $boolean;
}

// Valid since PHP 5.5
echo empty(foo(true)) : 'true' : 'false';

?>

This also applies to methodcalls, static or not.

Command Line Php/CantUseReturnValueInWriteContext
Analyzers CompatibilityPHP53, CompatibilityPHP54

Cast To Boolean

This expression may be reduced by casting to boolean type.

<?php

$variable = $condition == 'met' ? 1 : 0;
// Same as
$variable = (bool) $condition == 'met';

$variable = $condition == 'met' ? 0 : 1;
// Same as (Note the condition inversion)
$variable = (bool) $condition != 'met';
// also, with an indentical condition
$variable = !(bool) $condition == 'met';



?>
Command Line Structures/CastToBoolean
Analyzers Analyze

Catch Overwrite Variable

The try/catch structure uses some variables that also in use in this scope. In case of a caught exception, the exception will be put in the catch variable, and overwrite the current value, loosing some data.

<?php

// variables and caught exceptions are distinct
$argument = 1;
try {
    methodThatMayRaiseException($argument);
} (Exception $e) {
    // here, $e has been changed to an exception.
}

// variables and caught exceptions are overlapping
$e = 1;
try {
    methodThatMayRaiseException();
} (Exception $e) {
    // here, $e has been changed to an exception.
}

?>

It is recommended to use another name for these catch variables.

Command Line Structures/CatchShadowsVariable
clearPHP no-catch-overwrite
Analyzers Analyze

Check All Types

When checking for time, avoid using else. Mention explicitely all tested type, and raise an exception when reaching else.

PHP has a short list of scalar types : null, boolean, integer, real, strings, object, resource and array. When a variable is not holding one the the type, then it may be of any other type.

Most of the time, when using a simple is_*() / else test, this is relying on the conception of the code. By construction, the arguments may be one of two types : array or string.

What happens often is that in case of failure in the code (database not working, another class not checking its results), a third type is pushed to the structure, and it ends up breaking the execution.

The safe way is to check the various types all the time, and use the default case (here, the else) to throw exception() or test an assertion and handle the special case.

<?php

// hasty version
if (is_array($argument)) {
    $out = $argument;
} else {
    // Here, $argument is NOT an array. What if it is an object ? or a NULL ?
    $out = array($argument);
}

// Safe type checking : do not assume that 'not an array' means that it is the other expected type.
if (is_array($argument)) {
    $out = $argument;
} elseif (is_string($argument)) {
    $out = array($argument);
} else {
    assert(false, '$argument is not an array nor a string, as expected!');
}

?>

Using ‘is_callable(), is_iterable() with this structure is fine : when variable is callable or not, while a variable is an integer or else.

Using a type test without else is also accepted here. This is a special treatment for this test, and all others are ignored. This aspect may vary depending on situations and projects.

Command Line Structures/CheckAllTypes
Analyzers Analyze

Child Class Removes Typehint

PHP 7.2 introduced the ability to remove a typehint when overloarding a method. This is not valid code for older versions.

<?php

class foo {
    function foobar(foo $a) {}
}

class bar extends foo {
    function foobar($a) {}
}

?>
Command Line Classes/ChildRemoveTypehint
Analyzers CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Class Const With Array

Constant defined with const keyword may be arrays but only stating with PHP 5.6. Define never accept arrays : it only accepts scalar values.

Command Line Php/ClassConstWithArray
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55

Class Function Confusion

Avoid classes and functions bearing the same name.

When functions and classes bear the same name, calling them may be confusing. This may also lead to forgotten ‘new’ keyword.

<?php

class foo {}

function foo() {}

// Forgetting the 'new' operator is easy
$object = new foo();
$object = foo();

?>
Command Line Php/ClassFunctionConfusion
Analyzers Analyze

Class Name Case Difference

The spotted classes are used with a different case than their definition. While PHP accepts this, it makes the code harder to read.

It may also be a violation of coding conventions.

<?php

// This use statement has wrong case for origin.
use Foo as X;

// Definition of the class
class foo {}

// Those instantiations have wrong case
new FOO();
new X();

?>

See also PHP class name constant case sensitivity and PSR-11.

Command Line Classes/WrongCase
Analyzers Coding Conventions, Analyze

Class Should Be Final By Ocramius

‘Make your classes always final, if they implement an interface, and no other public methods are defined’.

When a class should be final, as explained by Ocramiux (Marco Pivetta).

Full article : When to declare classes final.

<?php

interface i1 {
    function i1() ;
}

// Class should final, as its public methods are in an interface
class finalClass implements i1 {
    // public interface
    function i1 () {}

    // private method
    private function a1 () {}
}

?>
Command Line Classes/FinalByOcramius
Analyzers Analyze

Class, Interface Or Trait With Identical Names

The following names are used at the same time for classes, interfaces or traits. For example,

class a {} interface a {} trait a {}

Even if they are in different namespaces, this makes them easy to confuse. Besides, it is recommended to have markers to differentiate classes from interfaces from traits.

Command Line Classes/CitSameName
Analyzers Analyze

Classes Mutually Extending Each Other

Those classes are extending each other, creating an extension loop. PHP will yield a fatal error at running time, even if it is compiling the code.

<?php

// This code is lintable but won't run
class Foo extends Bar { }
class Bar extends Foo { }

// The loop may be quite large
class Foo extends Bar { }
class Bar extends Bar2 { }
class Bar2 extends Foo { }

?>
Command Line Classes/MutualExtension
Analyzers Analyze

Close Tags

PHP manual recommends that script should be left open, without the final closing ?>. This way, one will avoid the infamous bug ‘Header already sent’, associated with left-over spaces, that are lying after this closing tag.

Command Line Php/CloseTags
clearPHP leave-last-closing-out
Analyzers Coding Conventions

Closure May Use $this

When closure were introduced in PHP, they couldn’t use the $this variable, making is cumbersome to access local properties when the closure was created within an object.

<?php

// Invalid code in PHP 5.4 and less
class Test
{
    public function testing()
    {
        return function() {
            var_dump($this);
        };
    }
}

$object = new Test;
$function = $object->testing();
$function();

?>

This is not the case anymore since PHP 5.4.

See also Anonymus Functions.

Command Line Php/ClosureThisSupport
Analyzers CompatibilityPHP53

Common Alternatives

In the following conditional structures, expressions were found that are common to both ‘then’ and ‘else’. It may be interesting, though not always possible, to put them both out of the conditional, and reduce line count.

<?php
if ($c == 5) {
    $b = strtolower($b[2]);
    $a++;
} else {
    $b = strtolower($b[2]);
    $b++;
}
?>

may be rewritten in :

<?php

$b = strtolower($b[2]);
if ($c == 5) {
    $a++;
} else {
    $b++;
}

?>
Command Line Structures/CommonAlternatives
Analyzers Analyze

Compare Hash

When comparing hash values, it is important to use the strict comparison : === or !==.

In a number of situations, the hash value will start with ‘0e’, and PHP will understand that the comparison involves integers : it will then convert the strings into numbers, and it may end up converting them to 0.

Here is an example

<?php

// The two following passwords hashes matches, while they are not the same.
$hashed_password = 0e462097431906509000000000000;
if (hash('md5','240610708',false) == $hashed_password) {
  print Matched.\n;
}
?>

You may also use ‘password_hash() and ‘password_verify().

See also Magic Hashes.

Command Line Security/CompareHash
clearPHP strict-comparisons
Analyzers Security

Compared Comparison

Usually, comparison are sufficient, and it is rare to have to compare the result of comparison. Check if this two-stage comparison is really needed.

Command Line Structures/ComparedComparison
Analyzers Analyze

Concrete Visibility

Methods that implements an interface in a class must be public.

PHP doesn’t lint this, but won’t start a script with a Fatal error : ‘Access level to c::iPrivate() must be public (as in class i) ‘;

<?php

interface i {
    function iPrivate() ;
    function iProtected() ;
    function iPublic() ;
}

class c implements i {
    // Methods that implements an interface in a class must be public.
    private function iPrivate() {}
    protected function iProtected() {}
    public function iPublic() {}
}

?>

See also Interfaces.

Command Line Interfaces/ConcreteVisibility
Analyzers Analyze

Confusing Names

The following variables’s name are very close and may lead to confusion.

Variables are 3 letters long (at least). Variables names build with an extra ‘s’ are omitted. Variables may be scattered across the code, or close to each other.

Variables which differ only by case, or by punctuation or by numbers are reported here.

<?php

    // Variable names with one letter difference
    $fWScale = 1;
    $fHScale = 1;
    $fScale = 2;

    $oFrame = 3;
    $iFrame = new Foo();

    $v2_norm = array();
    $v1_norm = 'string';

    $exept11 = 1;
    $exept10 = 2;
    $exept8 = 3;

    // Variables that differ by punctation
    $locale = 'fr';
    $_locate = 'en';

    // Variables that differ by numbers
    $x11 = 'a';
    $x12 = 'b';

    // Variables that differ by numbers
    $songMP3 = 'a';
    $Songmp3 = 'b';

    // This even looks like a typo
    $privileges  = 1;
    $privilieges = true;

    // This is not reported : Adding extra s is tolerated.
    $rows[] = $row;

?>

See also How to pick bad function and variable names.

Command Line Variables/CloseNaming
Analyzers Analyze

Const With Array

The const keyword supports array since PHP 5.6.

Command Line Php/ConstWithArray
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55

Constant Class

A class or an interface only made up of constants. Constants usually have to be used in conjunction of some behavior (methods, class...) and never alone.

As such, they should be PHP constants (build with define or const), or included in a class with other methods and properties.

Command Line Classes/ConstantClass
Analyzers Analyze

Constant Comparison

Constant to the left or right is a favorite.

Comparisons are commutative : they may be $a == B or B == $a. The analyzed code show less than 10% of one of the two : for consistency reasons, it is recommended to make them all the same.

Putting the constant on the left is also called ‘Yoda Comparison’, as it mimics the famous characters style of speech. It prevents errors like ‘B = $a’ where the comparison is turned into an assignation.

The natural way is to put the constant on the right. It is often less surprising.

Every comparison operator is used when finding the favorite.

<?php

//
if ($a === B) { doSomething(); }
if ($c > D) { doSomething(); }
if ($e !== G) { doSomething(); }
do { doSomething(); } while ($f === B);
while ($a === B) { doSomething(); }

// be consistent
if (B === $a) {}

// Compari
if (B <= $a) {}

?>
Command Line Structures/ConstantComparisonConsistance
Analyzers Coding Conventions

Constant Scalar Expressions

Starting with PHP 5.6, it is possible to define constant that are the result of expressions.

Those expressions (using simple operators) may only manipulate other constants, and all values must be known at compile time.

This is not compatible with previous versions.

Command Line Structures/ConstantScalarExpression
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55

Constants Created Outside Its Namespace

Constants Created Outside Its Namespace.

Using the ‘define() function, it is possible to create constant outside their namespace, but using the fully qualified namespace.

<?php

namespace A\B {
    // define A\B\C as 1
    define('C', 1);
}

namespace D\E {
    // define A\B\C as 1, while outside the A\B namespace
    define('A\B\C', 1);
}

?>

However, this makes the code confusing and difficult to debug. It is recommended to move the constant definition to its namespace.

Command Line Constants/CreatedOutsideItsNamespace
Analyzers Analyze

Constants With Strange Names

List of constants being defined with names that are incompatible with PHP standards.

<?php

// Define a valid PHP constant
define('ABC', 1);
const ABCD = 2;

// Define an invalid PHP constant
define('ABC!', 1);
echo defined('ABC!') ? constant('ABC!') : 'Undefined';

// Const doesn't allow illegal names

?>
Command Line Constants/ConstantStrangeNames
Analyzers Analyze

Could Be Class Constant

When a property is defined and read, but never modified, it may be a constant.

<?php

class foo {
    // $this->bar is never modified.
    private $bar = 1;

    // $this->foofoo is modified, at least once
    private $foofoo = 2;

    function method($a) {
        $this->foofoo = $this->bar + $a + $this->foofoo;

        return $this->foofoo;
    }

}

?>

Starting with PHP 5.6, even array() may be defined as constants.

Command Line Classes/CouldBeClassConstant
Analyzers Analyze

Could Be Else

Merge opposition conditions into one if/then structure.

When two if/then structures follow each other, using a condition and its opposite, they may be merged into one.

<?php

// Short version
if ($a == 1) {
    $b = 2;
} else {
    $b = 1;
}

// Long version
if ($a == 1) {
    $b = 2;
}

if ($a != 1) {
    $b = 3;
}

?>
Command Line Structures/CouldBeElse
Analyzers Analyze

Could Be Private Class Constant

Class constant may use ‘private’ visibility.

Since PHP 7.1, constants may also have a public/protected/private visibility. This restrict their usage to anywhere, class and children or class.

As a general rule, it is recommended to make constant ‘private’ by default, and to relax this restriction as needed. PHP makes them public by default.

<?php

class foo {
    // pre-7.1 style
    const PRE_71_CONSTANT = 1;

    // post-7.1 style
    private const PRIVATE_CONSTANT = 2;
    public const PUBLIC_CONSTANT = 3;

    function bar() {
        // PRIVATE CONSTANT may only be used in its class
        echo self::PRIVATE_CONSTANT;
    }
}

// Other constants may be used anywhere
function x($a = foo::PUBLIC_CONSTANT) {
    echo $a.' '.foo:PRE_71_CONSTANT;
}

?>

See also Class Constants.

Command Line Classes/CouldBePrivateConstante
Analyzers Analyze

Could Be Protected Class Constant

Class constant may use ‘protected’ visibility.

Since PHP 7.1, constants may also have a public/protected/private visibility. This restrict their usage to anywhere, class and children or class.

As a general rule, it is recommended to make constant ‘private’ by default, and to relax this restriction as needed. PHP makes them public by default.

<?php

class foo {
    // pre-7.1 style
    const PRE_71_CONSTANT = 1;

    // post-7.1 style
    protected const PROTECTED_CONSTANT = 2;
    public const PUBLIC_CONSTANT = 3;
}

class foo2 extends foo {
    function bar() {
        // PROTECTED_CONSTANT may only be used in its class or its children
        echo self::PROTECTED_CONSTANT;
    }
}

class foo3 extends foo {
    function bar() {
        // PROTECTED_CONSTANT may only be used in its class or any of its children
        echo self::PROTECTED_CONSTANT;
    }
}

// Other constants may be used anywhere
function x($a = foo::PUBLIC_CONSTANT) {
    echo $a.' '.foo:PRE_71_CONSTANT;
}

?>
Command Line Classes/CouldBeProtectedConstant
Analyzers Analyze

Could Be Protected Method

Those methods are declared public, but are never used publicly. They may be made protected.

<?php

class foo {
    // Public, and used publicly
    public publicMethod() {}

    // Public, but never used outside the class or its children
    public protectedMethod() {}

    private function bar() {
        $this->protectedMethod();
    }
}

$foo = new Foo();
$foo->publicMethod();

?>

These properties may even be made private.

Command Line Classes/CouldBeProtectedMethod
Analyzers Analyze

Could Be Protected Property

Those properties are declared public, but are never used publicly. They may be made protected.

<?php

class foo {
    // Public, and used publicly
    public $publicProperty;
    // Public, but never used outside the class or its children
    public $protectedProperty;

    function bar() {
        $this->protectedProperty = 1;
    }
}

$foo = new Foo();
$foo->publicProperty = 3;

?>

This property may even be made private.

Command Line Classes/CouldBeProtectedProperty
Analyzers Analyze

Could Be Static

This global is only used in one function or method. It may be called ‘static’, instead of global. This allows you to keep the value between call to the function, but will not be accessible outside this function.

<?php
function foo( ) {
    static $variableIsReservedForX; // only accessible within foo( ), even between calls.
    global $variableIsGlobal;       //      accessible everywhere in the application
}
?>
Command Line Structures/CouldBeStatic
Analyzers Analyze

Could Be Typehinted Callable

Those arguments may use the callable Typehint.

‘callable’ is a PHP keyword that represents callback functions. Those may be used in dynamic function call, like $function(); or as callback functions, like with ‘array_map();

callable may be a string representing a function name or a static call (including ::), an array with two elements, (a class or object, and a method), or a closure.

When arguments are used to call a function, but are not marked with ‘callable’, they are reported by this analysis.

<?php

function foo(callable $callable) {
    // very simple callback
    return $callable();
}

function foo2($array, $callable) {
    // very simple callback
    return array_map($array, $callable);
}

?>

See also Callback / callable.

Command Line Functions/CouldBeCallable
Analyzers Analyze

Could Make A Function

When a function is called across the code with the same arguments often enough, it should be turned into a local API.

This approach is similar to turning literals into constants : it centralize the value, it helps refactoring by updating it. It also makes the code more readable. Moreover, it often highlight common grounds between remote code locations.

The analysis looks for functions calls, and checks the arguments. When the calls occurs more than 4 times, it is reported.

<?php

// str_replace is used to clean '&' from strings.
// It should be upgraded to a central function
function foo($arg ) {
    $arg = str_replace('&', '', $arg);
    // do something with $arg
}

class y {
    function bar($database ) {
        $value = $database->queryName();
        $value = str_replace('&', '', $value);
        // $value = removeAmpersand($value);
        // do something with $arg2
    }
}

// helper function
function removeAmpersand($string) {
    return str_replace('&', '', $string);
}

?>
Command Line Functions/CouldCentralize
Analyzers Analyze

Could Typehint

Arguments that are tested with ‘instanceof gain from making it a Typehint.

<?php

function foo($a, $b) {
    // $a is tested for B with 'instanceof.
    if (!$a 'instanceof B) {
        return;
    }

    // More code
}

function foo(B $a, $b) {
    // May omit the initial test

    // More code
}

?>
Command Line Functions/CouldTypehint
Analyzers Analyze

Could Use Alias

This long name may be reduced by using an available alias.

<?php

use a\b\c;

// This may be reduced with the above alias
new a\b\c\d();

// This too
new a\b\c\d\e\f();

// This yet again
new a\b\c();

?>
Command Line Namespaces/CouldUseAlias
Analyzers Analyze

Could Use Short Assignation

Use short assignement operator, to speed up code, and keep syntax clear.

Some operators, like + or *, have a compact and fast ‘do-and-assign’ version. They looks like a compacted version for = and the operator. This syntax is good for readability, and saves some memory in the process.

Depending on the operator, not all permutations of arguments are possible.

Addition and short assignation of addition have a different set of features when applied to arrays. Do not exchange one another in that case.

<?php

$a = 10 + $a;
$a += 10;

$b = $b - 1;
$b -= 1;

$c = $c * 2;
$c *= 2;

$d = $d / 3;
$d /= 3;

$e = $e % 4;
$e %= 4;

$f = $f | 5;
$f |= 5;

$g = $g & 6;
$g &= 6;

$h = $h ^ 7;
$h ^= 7;

$i = $i >> 8;
$i >>= 8;

$j = $j << 9;
$j <<= 9;

?>

Short operators are faster than the extended version, though it is a micro-optimization.

See also Assignation Operators.

Command Line Structures/CouldUseShortAssignation
clearPHP use-short-assignations
Analyzers Analyze, Performances

Could Use __DIR__

Use ‘__DIR__ function to access the current file’s parent directory.

<?php

assert(dirname('__FILE__) == '__DIR__);

?>

‘__DIR__ has been introduced in PHP 5.3.0.

Command Line Structures/CouldUseDir
Analyzers Analyze

Could Use self

‘self’ keyword refers to the current class, or any of its parents. Using it is just as fast as the full classname, it is as readable and it is will not be changed upon class or namespace change.

It is also routinely used in traits : there, ‘self’ represents the class in which the trait is used, or the trait itself.

<?php

class x {
    const FOO = 1;

    public function bar() {
        return self::FOO;
// same as return x::FOO;
    }
}

?>
Command Line Classes/ShouldUseSelf
Analyzers Analyze

Could Use str_repeat()

Use ‘str_repeat() or ‘str_pad() instead of making a loop.

Making a loop to repeat the same concatenation is actually much longer than using ‘str_repeat(). As soon as the loop repeats more than twice, ‘str_repeat() is much faster. With arrays of 30, the difference is significative, though the whole operation is short by itself.

<?php

// This adds 7 'e' to $x
$x .= str_repeat('e', 7);

// This is the same as above,
for($a = 3; $a < 10; ++$a) {
    $x .= 'e';
}

// here, $default must contains 7 elements to be equivalent to the previous code
foreach($default as $c) {
    $x .= 'e';
}

?>
Command Line Structures/CouldUseStrrepeat
Analyzers Analyze

Crc32() Might Be Negative

‘crc32() may return a negative number, on 32bits platforms.

According to the manual : Because PHP’s integer type is signed many crc32 checksums will result in negative integers on 32bit platforms. On 64bit installations all ‘crc32() results will be positive integers though.

<?php

// display the checksum with %u, to make it unsigned
echo sprintf('%u', crc32($str));

// turn the checksum into an unsigned hexadecimal
echo dechex(crc32($str));

// avoid concatenating crc32 to a string, as it may be negative on 32bits platforms
echo 'prefix'.crc32($str);

?>

See also crc32().

Command Line Php/Crc32MightBeNegative
Analyzers Analyze

Curly Arrays

It is possible to access individual elements in an array by using its offset between square brackets [] or curly brackets {}.

<?php

$array = ['a', 'b', 'c', 'd', 'e'];

print $array[2]; // displays 'b';
print $array{3}; // displays 'c';


?>

Curly brackets are seldom used, and will probably confuse or surprise the reader. It is recommended not to used them.

See also array.

Command Line Arrays/CurlyArrays
Analyzers Coding Conventions

Dangling Array References

Always unset a referenced-variable used in a loop.

It is highly recommended to unset blind variables when they are set up as references after a loop.

<?php

$array = array(1,2,3,4);

foreach($array as &$a) {
    $a += 1;
}
// This only unset the reference, not the value
unset($a);




// Dangling array problem
foreach($array as &$a) {
    $a += 1;
}
//$array === array(3,4,5,6);

// This does nothing (apparently)
foreach($array as $a) {}
//$array === array(3,4,5,6);

?>

When omitting this step, the next loop that will also require this variable will deal with garbage values, and produce unexpected results.

See also : No Dangling Reference.

Command Line Structures/DanglingArrayReferences
clearPHP no-dangling-reference
Analyzers Analyze

Deep Definitions

Structures, such as functions, classes, interfaces, traits, etc. may be defined anywhere in the code, including inside functions. This is legit code for PHP.

Since the availability of __autoload, there is no need for that kind of code. Structures should be defined, and accessible to the autoloading. Inclusion and deep definitions should be avoided, as they compell code to load some definitions, while autoloading will only load them if needed.

Functions are excluded from autoload, but shall be gathered in libraries, and not hidden inside other code.

Constants definitions are tolerated inside functions : they may be used for avoiding repeat, or noting the usage of such function.

Command Line Functions/DeepDefinitions
Analyzers Analyze

Define With Array

PHP 7.0 has the ability to define an array as a constant, using the ‘define() native call. This was not possible until that version, only with the const keyword.

<?php

//Defining an array as a constant
define('MY_PRIMES', [2, 3, 5, 7, 11]);

?>
Command Line Php/DefineWithArray
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Dependant Trait

Traits should be autonomous. It is recommended to avoid depending on methods or properties that should be in the using class.

The following traits make usage of methods and properties, static or not, that are not defined in the trait. This means the host class must provide those methods and properties, but there is no way to enforce this.

This may also lead to dead code : when the trait is removed, the host class have unused properties and methods.

<?php

// autonomous trait : all it needs is within the trait
trait t {
    private $p = 0;

    function foo() {
        return ++$this->p;
    }
}

// dependant trait : the host class needs to provide some properties or methods
trait t2 {
    function foo() {
        return ++$this->p;
    }
}

class x {
    use t2;

    private $p = 0;
}
?>
Command Line Traits/DependantTrait
Analyzers Analyze

Deprecated Code

The following functions have been deprecated in PHP. Whatever the version you are using, it is recommended to stop using them and replace them with a durable equivalent.

Command Line Php/Deprecated
clearPHP no-deprecated
Analyzers Analyze

Deprecated Methodcalls in Cake 3.2

According to the Cake Migration Guide, the following are deprecated and should be changed.

  • Shell::error()
  • CakeDatabaseExpressionQueryExpression::type()
  • CakeORMResultSet::_calculateTypeMap()
  • CakeORMResultSet::_castValues()

See also Cake 3.2 migration guide.

Command Line Cakephp/Cake32DeprecatedMethods
Analyzers Cakephp

Deprecated Methodcalls in Cake 3.3

According to the Cake 3.3 migration guide, the following are deprecated and should be changed.

  • Shell::error()
Command Line Cakephp/Cake33DeprecatedMethods
Analyzers Cakephp

Deprecated Static calls in Cake 3.3

According to the Cake 3.3 migration guide, the following are deprecated and should be changed.

  • Router::mapResources() is deprecated. Use routing scopes and $routes->resources() instead.
  • Router::redirect() is deprecated. Use routing scopes and $routes->redirect() instead.
Command Line Cakephp/Cake33DeprecatedStaticmethodcall
Analyzers Cakephp

Deprecated Trait in Cake 3.3

According to the Cake 3.3 migration guide, the following are deprecated and should be changed.

  • CakeRoutingRequestActionTrait
Command Line Cakephp/Cake33DeprecatedTraits
Analyzers Cakephp

Dereferencing String And Arrays

PHP allows the direct dereferencing of strings and arrays.

This was added in PHP 5.5. There is no need anymore for an intermediate variable between a string and array (or any expression generating such value) and accessing an index.

<?php
$x = array(4,5,6);
$y = $x[2] ; // is 6

May be replaced by
$y = array(4,5,6)[2];
$y = [4,5,6][2];
?>
Command Line Structures/DereferencingAS
Analyzers CompatibilityPHP53, CompatibilityPHP54

Direct Injection

The following code act directly upon PHP incoming variables like $_GET and $_POST. This make those snippet very unsafe.

<?php

// Direct injection
echo Hello.$_GET['user']., welcome.;

// less direct injection
foo($_GET['user']);
function foo($user) {
    echo Hello.$user., welcome.;
}

?>
Command Line Security/DirectInjection
Analyzers Security

Don’t Change Incomings

PHP hands over a lot of information using special variables like $_GET, $_POST, etc... Modifying those variables and those values inside de variables means that the original content is lost, while it will still look like raw data, and, as such, will be untrustworthy.

<?php

// filtering and keeping the incoming value.
$_DATA'id'] = (int) $_GET['id'];

// filtering and changing the incoming value.
$_GET['id'] = strtolower($_GET['id']);

?>

It is recommended to put the modified values in another variable, and keep the original one intact.

Command Line Structures/NoChangeIncomingVariables
Analyzers Analyze

Dont Change The Blind Var

When using a ‘foreach(), the blind variables hold a copy of the original value. It is confusing to modify them, as it seems that the original value may be changed.

When actually changing the original value, use the reference in the foreach definition to make it obvious, and save the final reassignation.

When the value has to be prepared before usage, then save the filtered value in a separate variable. This makes the clean value obivous, and preserve the original value for a future usage.

<?php

// $bar is duplicated and kept
$foo = [1, 2, 3];
foreach($foo as $bar) {
    // $bar is updated but its original value is kept
    $nextBar = $bar + 1;
    print $bar . ' => ' . ($nextBar) . PHP_EOL;
    foobar($nextBar);
}

// $bar is updated and lost
$foo = [1, 2, 3];
foreach($foo as $bar) {
    // $bar is updated but its final value is lost
    print $bar . ' => ' . (++$bar) . PHP_EOL;
    // Now that $bar is reused, it is easy to confuse its value
    foobar($bar);
}

// $bar is updated and kept
$foo = [1, 2, 3];
foreach($foo as &$bar) {
    // $bar is updated and keept
    print $bar . ' => ' . (++$bar) . PHP_EOL;
    foobar($bar);
}

?>
Command Line Structures/DontChangeBlindKey
Analyzers Analyze

Dont Echo Error

It is recommended to avoid displaying error messages directly to the browser.

<?php

// Inside a 'or' test
mysql_connect('localhost', $user, $pass) or 'die(mysql_error());

// Inside a if test
$result = pg_query( $db, $query );
if( !$result )
{
     echo Erreur SQL: . pg_error();
     'exit;
}

?>

Error messages should be logged, but not displayed.

Command Line Security/DontEchoError
Analyzers Analyze, Security

Double Assignation

This happens when a container (variable, property, array index) is assigned with values twice in a row. One of them is probably a debug instruction, that was forgotten.

<?php

// Normal assignation
$a = 1;

// Double assignation
$b = 2;
$b = 3;

?>
Command Line Structures/DoubleAssignation
Analyzers Analyze

Double Instructions

Twice the same call in a row. This is worth a check.

<?php

?>
Command Line Structures/DoubleInstruction
Analyzers Analyze

Drop Else After Return

Avoid else clause when the then clause returns, but not the else.

The else may simply be set in the main sequence of the function.

This is also true if else has a return, and then not : simply reverse the condition.

<?php

// drop the else
if ($a) {
    return $a;
} else {
    doSomething();
}

// drop the then
if ($b) {
    doSomething();
} else {
    return $a;
}

// return in else and then
if ($a3) {
    return $a;
} else {
    $b = doSomething();
    return $b;
}

?>
Command Line Structures/DropElseAfterReturn
Analyzers Analyze

Echo Or Print

Echo and print have the same functional use. <?= is also considered in this analysis.

There seems to be a choice that is not enforced : one form is dominent, (> 90%) while the others are rare.

The analyzed code has less than 10% of one of the three : for consistency reasons, it is recommended to make them all the same.

It happens that print, echo or <?= are used depending on coding style and files. One file may be consistently using print, while the others are all using echo.

<?php

echo 'a';
echo 'b';
echo 'c';
echo 'd';
echo 'e';
echo 'f';
echo 'g';
echo 'h';
echo 'i';
echo 'j';
echo 'k';

// This should probably be written 'echo';
print 'l';

?>
Command Line Structures/EchoPrintConsistance
Analyzers Coding Conventions

Echo With Concat

Optimize your echo’s by not concatenating at echo() time, but serving all argument separated. This will save PHP a memory copy. If values (literals and variables) are small enough, this won’t have impact. Otherwise, this is less work and less memory waste.

<?php
  echo $a, ' b ', $c;
?>

instead of

<?php
  echo  $a . ' b ' . $c;
  echo $a b $c;
?>
Command Line Structures/EchoWithConcat
clearPHP no-unnecessary-string-concatenation
Analyzers Performances, Analyze

Ellipsis Usage

Usage of the ... keyword. It may be in function definitions, either in functioncalls.

... allows for packing or unpacking arguments into an array.

<?php

$args = [1, 2, 3];
foo(...$args);
// Identical to foo(1,2,3);

function bar(...$a) {
    // Identical to : $a = 'func_get_args();
}
?>
Command Line Php/EllipsisUsage
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55

Else If Versus Elseif

Always use elseif instead of else and if.

“The keyword elseif SHOULD be used instead of else if so that all control keywords look like single words”. Quoted from the PHP-FIG documentation

<?php

// Using elseif
if ($a == 1) { doSomething(); }
elseif ($a == 2) { doSomethingElseIf(); }
else { doSomethingElse(); }

// Using else if
if ($a == 1) { doSomething(); }
else if ($a == 2) { doSomethingElseIf(); }
else { doSomethingElse(); }

// Using else if, no {}
if ($a == 1)  doSomething();
else if ($a == 2) doSomethingElseIf();
else  doSomethingElse();

?>

.

Command Line Structures/ElseIfElseif
Analyzers Analyze

Empty Blocks

The listed control structures are empty, or have one of the commanded block empty. It is recommended to remove those blocks, so as to reduce confusion in the code.

<?php

foreach($foo as $bar) ; // This block seems erroneous
    $foobar++;

if ($a === $b) {
    doSomething();
} else {
    // Empty block. Remove this
}

// Blocks containing only empty expressions are also detected
for($i = 0; $i < 10; $i++) {
    ;
}

// Although namespaces are not control structures, they are reported here
namespace A;
namespace B;

?>
Command Line Structures/EmptyBlocks
Analyzers Analyze

Empty Classes

List of empty classes. Classes that are directly derived from an exception are omited.

<?php

//Empty class
class foo extends bar {}

//Not an empty class
class foo2 extends bar {
    const FOO = 2;
}

//Not an empty class, as derived from Exception
class barException extends \Exception {}

?>
Command Line Classes/EmptyClass
Analyzers Analyze

Empty Function

Function or method whose body is empty.

Such functions or methods are rarely useful. As a bare minimum, the function should return some useful value, even if constant.

<?php

// classic empty function
function emptyFunction() {}

class bar {
    // classic empty method
    function emptyMethod() {}

    // classic empty function
    function emptyMethodWithParent() {}
}

class barbar extends bar {
    // NOT an empty method : it overwrites the parent method
    function emptyMethodWithParent() {}
}

?>
Command Line Functions/EmptyFunction
Analyzers Analyze

Empty Instructions

Empty instructions are part of the code that have no instructions. This may be trailing semi-colon or empty blocks for if-then structures.

$condition = 3;;;; if ($condition) { }

Command Line Structures/EmptyLines
Analyzers Dead code, Analyze

Empty Interfaces

Empty interfaces are a code smell. Interfaces should contains at least a method or a constant, and not be totally empty.

<?php

// an empty interface
interface empty {}

// an normal interface
interface normal {
    public function i() ;
}

// a constants interface
interface constantsOnly {
    const FOO = 1;
}

?>

See also Empty interfaces are bad practice and Blog : Are empty interfaces code smell?.

Command Line Interfaces/EmptyInterface
Analyzers Analyze

Empty List

Empty list() are not allowed anymore in PHP 7. There must be at least one variable in the list call.

<?php

//Not accepted since PHP 7.0
list() = array(1,2,3);

//Still valid PHP code
list(,$x) = array(1,2,3);

?>
Command Line Php/EmptyList
Analyzers Analyze, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Empty Namespace

Declaring a namespace in the code and not using it for structure declarations (classes, interfaces, etc...) or global instructions is useless.

Using simple style :

<?php

namespace X;
// This is useless

namespace Y;

class foo {}

?>

Using bracket-style syntax :

<?php

namespace X {
    // This is useless
}

namespace Y {

    class foo {}

}

?>
Command Line Namespaces/EmptyNamespace
clearPHP no-empty-namespace
Analyzers Analyze, Dead code

Empty Slots In Arrays

PHP tolerates the last element of an array to be empty.

<?php
    $a = array( 1, 2, 3, );
    $b =      [ 4, 5, ];
?>
Command Line Arrays/EmptySlots
Analyzers Coding Conventions

Empty Traits

List of all empty trait defined in the code.

<?php

// empty trait
trait t { }

// Another empty trait
trait t2 {
    use t;
}

?>

Such traits may be reserved for future use. They may also be forgotten, and dead code.

Command Line Traits/EmptyTrait
Analyzers Analyze

Empty Try Catch

The code does try, then catch errors but do no act upon the error.

<?php

try {
    doSomething();
} catch ('Throwable $e) {
    // simply ignore this
}

?>

At worst, the error should be logged, so as to measure the actual usage of the catch expression.

catch( Exception $e) (PHP 5) or catch(‘Throwable $e) with empty catch block should be banned, as they will simply ignore any error.

Command Line Structures/EmptyTryCatch
Analyzers Analyze

Empty With Expression

‘empty() doesn’t accept expressions until PHP 5.5. Until then, it is necessary to store the result of the expression in a variable and then, test it with ‘empty().

<?php

// PHP 5.5+ 'empty() usage
if (empty(strtolower($b . $c))) {
    doSomethingWithoutA();
}

// Compatible 'empty() usage
$a = strtolower($b . $c);
if (empty($a)) {
    doSomethingWithoutA();
}

?>
Command Line Structures/EmptyWithExpression
Analyzers CompatibilityPHP55, CompatibilityPHP70, CompatibilityPHP56, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Encoded Simple Letters

Some simple letters are written in escape sequence.

Usually, escape sequences are made to encode unusual characters. Using escape sequences for simple characters, like letters or numbers is suspicious.

<?php

// This escape sequence makes eval hard to spot
$a = ev1l;
$a('php_info();');

// With a PHP 7.0 unicode code point sequence
$a = ev\u{41}l;
$a('php_info();');

// With a PHP 5.0+ hexadecimal sequence
$a = ev\x41l;
$a('php_info();');

?>
Command Line Security/EncodedLetters
Analyzers Security

Error Messages

Error message when an error is reported in the code. Those messages will be read by whoever is triggering the error, and it has to be helpful.

It is a good excercice to read the messages out of context, and try to understand what is about.

<?php

// Not so helpful messages
'die('Here be monsters');
'exit('An error happened');
throw new Exception('Exception thrown at runtime');

?>

Error messages are spotted via ‘die, ‘exit or exception.

Command Line Structures/ErrorMessages
Analyzers ZendFramework

Eval() Usage

Using ‘eval() is bad for performances (compilation time), for caches (it won’t be compiled), and for security (if it includes external data).

<?php
    // Avoid using incoming data to build the 'eval() expression : any filtering error leads to PHP injection
    $mathExpression = $_GET['mathExpression'];
    $mathExpression = preg_replace('#[^0-9+\-*/\(/)]#is', '', $mathExpression); // expecting 1+2
    $literalCode = '$a = '.$mathExpression.';';
    eval($literalCode);
    echo $a;

    // If eval'ed code is known at compile time, it is best to put it inline
    $literalCode = ''phpinfo();';
    eval($literalCode);

?>

Most of the time, it is possible to replace the code by some standard PHP, like variable variable for accessing a variable for which you have the name. At worse, including a pre-generated file will be faster.

For PHP 7.0 and later, it is important to put ‘eval() in a try..catch expression.

Command Line Structures/EvalUsage
clearPHP no-eval
Analyzers Analyze, Performances

Exception Order

When catching exception, the most specialized exceptions must be in the early catch, and the most general exceptions must be in the later catch. Otherwise, the general catches intercept the exception, and the more specialized will not be read.

<?php

class A extends \Exception {}
class B extends A {}

try {
    throw new A();
}
catch(A $a1) { }
catch(B $b2 ) {
    // Never reached, as previous Catch is catching the early worm
}

?>
Command Line Exceptions/AlreadyCaught
Analyzers Dead code

Exit() Usage

Using ‘exit or ‘die() in the code makes the code untestable (it will ‘break unit tests). Morover, if there is no reason or string to display, it may take a long time to spot where the application is stuck.

<?php

// Throw an exception, that may be caught somewhere
throw new \Exception('error');

// Dying with error message.
'die('error');

function foo() {
    //exiting the function but not dying
    if (somethingWrong()) {
        return true;
    }
}
?>

Try exiting the function/class with return, or throw exception that may be caught later in the code.

Command Line Structures/ExitUsage
clearPHP no-exit
Analyzers Analyze, ZendFramework

Exponent Usage

Usage of the ‘** operator or **=, to make exponents.

<?php

$eight = 2 '** 3;

$sixteen = 4;
$sixteen \*\*\= 2;

?>
Command Line Php/ExponentUsage
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55

Failed Substr Comparison

The extracted string must be of the size of the compared string.

This is also true for negative lengths.

<?php

// Possible comparison
if (substr($a, 0, 3) === 'abc') { }
if (substr($b, 4, 3) === 'abc') { }

// Always failing
if (substr($a, 0, 3) === 'ab') { }
if (substr($a, 3, -3) === 'ab') { }

// Omitted in this analysis
if (substr($a, 0, 3) !== 'ab') { }

?>
Command Line Structures/FailingSubstrComparison
Analyzers Analyze

Fetch One Row Format

When reading results with ext/Sqlite3, it is recommended to explicitely request SQLITE3_NUM or SQLITE3_ASSOC, while avoiding the default value and SQLITE3_BOTH.

<?php

$res = $database->query($query);

// Fastest version, but less readable
$row = $res->fetchArray(\SQLITE3_NUM);
// Almost the fastest version, and more readable
$row = $res->fetchArray(\SQLITE3_ASSOC);

// Default version. Quite slow
$row = $res->fetchArray();

// Worse case
$row = $res->fetchArray(\SQLITE3_BOTH);

?>

This is a micro-optimisation. The difference may be visible with 200k rows fetches, and measurable with 10k.

Command Line Performances/FetchOneRowFormat
Analyzers Performances

For Using Functioncall

It is recommended to avoid functioncall in the ‘for() statement.

<?php

// Fastest way
$nb = count($array);
for($i = 0; $i < $nb; ++$i) {
    doSomething($i);
}

// Same as above, but slow
for($i = 0; $i < count($array); ++$i) {
    doSomething($i);
}

// Same as above, but slow
foreach($portions as &$portion) {
    // here, 'array_sum() doesn't depends on the $grade. It should be out of the loop
    $portion = $portion / array_sum($portions);
}

$total = array_sum($portion);
foreach($portion as &$portion) {
    $portion = $portion / $total;
}

?>

This is true with any kind of functioncall that returns the same value throughout the loop.

Command Line Structures/ForWithFunctioncall
clearPHP no-functioncall-in-loop
Analyzers Analyze, Performances

Foreach Don’t Change Pointer

In PHP 7.0, the foreach loop won’t change the internal pointer of the array, but will work on a copy. So, applying array pointer’s functions such as ‘current() or ‘next() to the source array won’t have the same behavior than in PHP 5.

This anly applies when a ‘foreach() by reference is used.

<?php

$numbers = range(1, 10);
next($numbers);
foreach($numbers as &$number){
    print $number;
    print current($numbers).\n; // Always
}

?>
Command Line Php/ForeachDontChangePointer
Analyzers CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Foreach Needs Reference Array

When using foreach with a reference as value, the source must be a referenced array, which is a variable (or array or property or static property). When the array is the result of an expression, the array is not kept in memory after the foreach loop, and any change made with & are lost.

This will do nothing

<?php
    foreach(array(1,2,3) as &$value) {
        $value *= 2;
    }
?>

This will have an actual effect

<?php
    $array = array(1,2,3);
    foreach($array as &$value) {
        $value *= 2;
    }
?>
Command Line Structures/ForeachNeedReferencedSource
Analyzers Analyze

Foreach Reference Is Not Modified

Foreach statement may loop using a reference, especially when the loop has to change values of the array it is looping on. In the spotted loop, reference are used but never modified. They may be removed.

Command Line Structures/ForeachReferenceIsNotModified
Analyzers Analyze

Foreach With list()

PHP 5.5 introduced the ability to use list in foreach loops. This was not possible in the earlier versions.

<?php
    foreach($array as list($a, $b)) {
        // do something
    }
?>

Previously, it was compulsory to extract the data from the blind array :

<?php
    foreach($array as $c) {
        list($a, $b) = $c;
        // do something
    }
?>
Command Line Structures/ForeachWithList
Analyzers CompatibilityPHP53, CompatibilityPHP54

Forgotten Interface

The following classes have been found implementing an interface’s methods, though it doesn’t explicitely implements this interface. This may have been forgotten.

<?php

interface i {
    function i();
}

// i is not implemented and declared
class foo {
    function i() {}
    function j() {}
}

// i is implemented and declared
class foo implements i {
    function i() {}
    function j() {}
}

?>
Command Line Interfaces/CouldUseInterface
Analyzers Analyze

Forgotten Thrown

An exception is instantiated, but not thrown.

<?php

class MyException() extends \Exception { }

if ($error !== false) {
    // This looks like 'throw' was omitted
    new MyException();
}

?>
Command Line Exceptions/ForgottenThrown
Analyzers Analyze

Forgotten Visibility

Some classes elements (property, method, and constant since PHP 7.1) are missing their explicit visibility.

By default, it is public. It should at least be mentioned as public, or may be reviewed as protected or private.

final, static and abstract are not counted as visibility. Only public, private and protected. The PHP 4 var keyword is counted as undefined.

Traits, classes and interfaces are checked.

<?php

// Explicit visibility
class X {
    protected sconst NO_VISIBILITY_CONST = 1; // For PHP 7.2 and later

    private $noVisibilityProperty = 2;

    public function Method() {}
}

// Missing visibility
class X {
    const NO_VISIBILITY_CONST = 1; // For PHP 7.2 and later

    var $noVisibilityProperty = 2; // Only with var

    function NoVisibilityForMethod() {}
}

?>

See also Visibility and Understanding The Concept Of Visibility In Object Oriented PHP.

Command Line Classes/NonPpp
clearPHP always-have-visibility
Analyzers Analyze

Forgotten Whitespace

Those are white space that are at either end of a script : at the beginning or the end.

<?php
    // This script has no forgotten whitespace, not at the beginning
    function foo() {}

    // This script has no forgotten whitespace, not at the end
?>

Usually, such white space are forgotten, and may end up summoning the infamous ‘headers already sent’ error. It is better to remove them.

See also How to fix Headers already sent error in PHP.

Command Line Structures/ForgottenWhiteSpace
Analyzers Analyze

Fully Qualified Constants

Constants defined with their namespace.

When defining constants with ‘define() function, it is possible to include the actual namespace :

<?php

define('a\b\c', 1);

?>

However, the name should be fully qualified without the initial . Here, abc constant will never be accessible as a namespace constant, though it will be accessible via the ‘constant() function.

Also, the namespace will be absolute, and not a relative namespace of the current one.

Command Line Namespaces/ConstantFullyQualified
Analyzers Analyze

Function Subscripting

It is possible to use the result of a methodcall directly as an array, without storing the result in a temporary variable.

This works, given that the method actually returns an array.

This syntax was not possible until PHP 5.4. Until then, it was compulsory to store the result in a variable first. Although this is now superfluous, it has been a standard syntax in PHP, and is still being used.

<?php

function foo() {
    return array(1 => 'a', 'b', 'c');
}

echo foo()[1]; // displays 'a';

// Function subscripting, the old way
function foo() {
    return array(1 => 'a', 'b', 'c');
}

$x = foo();
echo $x[1]; // displays 'a';

?>

Storing the result in a variable is still useful if the result is actually used more than once.

Command Line Structures/FunctionSubscripting
Analyzers CompatibilityPHP53

Function Subscripting, Old Style

Since PHP 5.4, it is now possible use function results as an array, and access directly its element :

<?php

function foo() {
    return array(1 => 'a', 'b', 'c');
}

echo foo()[1]; // displays 'a';

// Function subscripting, the old way
function foo() {
    return array(1 => 'a', 'b', 'c');
}

$x = foo();
echo $x[1]; // displays 'a';

?>
Command Line Structures/FunctionPreSubscripting
Analyzers Analyze

Functions Removed In PHP 5.4

Those functions were removed in PHP 5.4.

<?php

// Deprecated as of PHP 5.4.0
$link = mysql_connect('localhost', 'mysql_user', 'mysql_password');
$db_list = mysql_list_dbs($link);

while ($row = mysql_fetch_object($db_list)) {
     echo $row->Database . \n;
}

?>

See also Deprecated features in PHP 5.4.x.

Command Line Php/Php54RemovedFunctions
Analyzers CompatibilityPHP54, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73, CompatibilityPHP55, CompatibilityPHP56

Functions Removed In PHP 5.5

Those functions were removed in PHP 5.5.

Command Line Php/Php55RemovedFunctions
Analyzers CompatibilityPHP55, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73, CompatibilityPHP56

Getting Last Element

Getting the last element of an array is done with ‘count() or ‘end().

<?php

$array = [1, 2, 3];

// Best solutions, just as quick as each other
$last = $array[count($array) - 1];
$last = end($array);

// Bad solutions

// popping, but restoring the value.
$last = array_pop($array);
$array[] = $last;

// array_unshift would be even worse

// reversing array
$last = array_reverse($array)[0];

// slicing the array
$last = array_slice($array, -1)[0]',
$last = current(array_slice($array, -1));
);

?>
Command Line Arrays/GettingLastElement
Analyzers Performances

Global Inside Loop

The global keyword must be out of loops. It is evaluated each loop, slowing the whole process.

<?php

// Good idea, global is used once
global $total;
foreach($a as $b) {
    $total += $b;
}

// Bad idea, this is slow.
foreach($a as $b) {
    global $total;
    $total += $b;
}
?>
Command Line Structures/GlobalOutsideLoop
Analyzers Performances

Global Usage

List usage of globals variables, with global keywords or direct access to $GLOBALS.

It is recommended to avoid using global variables, at it makes it very difficult to track changes in values across the whole application.

Command Line Structures/GlobalUsage
clearPHP no-global
Analyzers Analyze

Group Use Declaration

The group use declaration is used in the code.

<?php

// Adapted from the RFC documentation
// Pre PHP 7 code
use some\name_space\ClassA;
use some\name_space\ClassB;
use some\name_space\ClassC as C;

use function some\name_space\fn_a;
use function some\name_space\fn_b;
use function some\name_space\fn_c;

use const some\name_space\ConstA;
use const some\name_space\ConstB;
use const some\name_space\ConstC;

// PHP 7+ code
use some\name_space\{ClassA, ClassB, ClassC as C};
use function some\name_space\{fn_a, fn_b, fn_c};
use const some\name_space\{ConstA, ConstB, ConstC};

?>

See also Group Use Declaration RFC and Using namespaces: Aliasing/Importing.

Command Line Php/GroupUseDeclaration
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Group Use Trailing Comma

The usage of a final empty slot in array() was allowed with use statements. This works in PHP 7.2 and more recent.

Although this empty instruction is ignored at execution, this allows for clean presentation of code, and short diff when committing in a VCS.

<?php

// Valid in PHP 7.2 and more recent.
use a\b\{c,
         d,
         e,
         f,
        };

// This won't compile in 7.1 and older.

?>

See also ` <https://wiki.php.net/rfc/list-syntax-trailing-commas>`_ and Revisit trailing commas in function arguments.

Command Line Php/GroupUseTrailingComma
Analyzers CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Hardcoded Passwords

Hardcoded passwords in the code.

Hardcoding passwords is a bad idea. Not only it make the code difficult to change, but it is an information leak. It is better to hide this kind of information out of the code.

<?php

$ftp_server = '300.1.2.3';   //
$conn_id = ftp_connect($ftp_server);

// login with username and password
$login_result = ftp_login($conn_id, 'login', 'password');

?>
Command Line Functions/HardcodedPasswords
clearPHP no-hardcoded-credential
Analyzers Analyze, Security

Hash Algorithms

There is a long but limited list of hashing algorithm available to PHP. The one found below doesn’t seem to be existing.

Command Line Php/HashAlgos
Analyzers Analyze

Hash Algorithms Incompatible With PHP 5.3

List of hash algorithms incompatible with PHP 5.3. They were introduced in newer version, and, as such, are not available with older versions.

fnv132, fnv164 and joaat were added in PHP 5.4.

<?php

// Valid in PHP 5.4 +
hash('joaat', 'string');

// Valid in PHP all versions
hash('crc32', 'string');

?>
Command Line Php/HashAlgos53
Analyzers CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Hash Algorithms Incompatible With PHP 5.4/5

List of hash algorithms incompatible with PHP 5.4 and 5.5. They were introduced in newer version, or removed in PHP 5.4. As such, they are not available with older versions.

Command Line Php/HashAlgos54
Analyzers CompatibilityPHP54, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73, CompatibilityPHP55, CompatibilityPHP56

Heredoc Delimiter

Heredoc and Nowdoc expressions may use a variety of delimiters.

There seems to be a standard delimiter in the code, and some exceptions : one or severals forms are dominent (> 90%), while the others are rare.

The analyzed code has less than 10% of the rare delimiters. For consistency reasons, it is recommended to make them all the same.

Generally, one or two delimiters are used, with generic value. It is recommended to use a humanly readable delimiter : SQL, HTML, XML, GREMLIN, etc. This helps readability in the code.

<?php

echo <<<SQL
SELECT * FROM table1;
SQL;

echo <<<SQL
SELECT * FROM table2;
SQL;

echo <<<SQL
SELECT * FROM table3;
SQL;

echo <<<SQL
SELECT * FROM table4;
SQL;

echo <<<SQL
SELECT * FROM table5;
SQL;

echo <<<SQL
SELECT * FROM table11;
SQL;

echo <<<SQL
SELECT * FROM table12;
SQL;

echo <<<SQL
SELECT * FROM table13;
SQL;

// Nowdoc
echo <<<'SQL'
SELECT * FROM table14;
SQL;

echo <<<SQL
SELECT * FROM table15;
SQL;


echo <<<HEREDOC
SELECT * FROM table215;
HEREDOC;

?>
Command Line Structures/HeredocDelimiterFavorite
Analyzers Coding Conventions

Hexadecimal In String

Mark strings that may be confused with hexadecimal.

Until PHP 7.0, PHP recognizes hexadecimal numbers inside strings, and converts them accordingly.

PHP 7.0 and until 7.1, converts the string to 0, silently.

PHP 7.1 and later, emits a ‘A non-numeric value encountered’ warning, and convert the string to 0.

<?php
    $a = '0x0030';
    print $a + 1;
    // Print 49

    $c = '0x0030zyc';
    print $c + 1;
    // Print 49

    $b = 'b0x0030';
    print $b + 1;
    // Print 0
?>
Command Line Type/HexadecimalString
Analyzers CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Hidden Use Expression

The use expression for namespaces should always be at te beginning of the namespace block.

It is where everyone expect them, and it is less confusing than having them at various levels.

<?php

// This is visible
use A;

class B {}

// This is hidden
use C as D;

class E extends D {
    use traitT; // This is a use for a trait

    function foo() {
        // This is a use for a closure
        return function ($a) use ($b) {}
    }
}

?>
Command Line Namespaces/HiddenUse
Analyzers Analyze

Htmlentities Calls

‘htmlentities() and ‘htmlspecialchars() are used to prevent injecting special characters in HTML code. As a bare minimum, they take a string and encode it for HTML.

The second argument of the functions is the type of protection. The protection may apply to quotes or not, to HTML4 or 5, etc. It is highly recommended to set it explicitely.

The third argument of the functions is the encoding of the string. In PHP 5.3, it as ‘ISO-8859-1’, in 5.4, was ‘UTF-8’, and in 5.6, it is now default_charset, a php.ini configuration that has the default value of ‘UTF-8’. It is highly recommended to set this argument too, to avoid distortions from the configuration.

Also, note that arguments 2 and 3 are constants and string (respectively), and should be issued from the list of values available in the manual. Other values than those will make PHP use the default values.

Command Line Structures/Htmlentitiescall
Analyzers Analyze

Identical Conditions

These logical expressions contain members that are identical.

This means those expressions may be simplified.

<?php

// twice $a
if ($a || $b || $c || $a) {  }

// Hiding is parenthesis is bad
if (($a) ^ ($a)) {}

// expressions may be large
if ($a === 1 && 1 === $a) {}

?>
Command Line Structures/IdenticalConditions
Analyzers Analyze

If With Same Conditions

Successive If / then structures that have the same condition may be either merged or have one of the condition changed.

<?php

if ($a == 1) {
    doSomething();
}

if ($a == 1) {
    doSomethingElse();
}

// May be replaced by
if ($a == 1) {
    doSomething();
    doSomethingElse();
}

?>

Note that if the values used in the condition have been modified in the first if/then structure, the two distinct conditions may be needed.

<?php

// May not be merged
if ($a == 1) {
    // Check that this is really the situation
    $a = checkSomething();
}

if ($a == 1) {
    doSomethingElse();
}

?>
Command Line Structures/IfWithSameConditions
Analyzers Analyze

Iffectations

Affectations that appears in a condition.

Iffectations are a way to do both a test and an affectations. They may also be typos, such as if ($x = 3) { ... }, leading to a constant condition.

<?php

// an iffectation : assignation in a If condition
if($connexion = mysql_connect($host, $user, $pass)) {
    $res = mysql_query($connexion, $query);
}

// Iffectation may happen in while too.
while($row = mysql_fetch($res)) {
    $store[] = $row;
}

?>
Command Line Structures/Iffectation
Analyzers Analyze

Illegal Name For Method

PHP has reserved usage of methods starting with __ for magic methods. It is recommended to avoid using this prefix, to prevent confusions.

<?php

class foo{
    // Constructor
    function '__construct() {}

    // Constructor's typo
    function __constructor() {}

    // Illegal function name, even as private
    private function __bar() {}
}

?>
Command Line Classes/WrongName
Analyzers Analyze

Implement Is For Interface

When deriving classes, implements should be used for interfaces, and extends with classes.

Command Line Classes/ImplementIsForInterface
Analyzers Analyze

Implemented Methods Are Public

Class methods that are defined in an interface must be public. They cannot be either private, nor protected.

This error is not reported by lint, but is reported at execution time.

<?php

interface i {
    function foo();
}

class X {
    // This method is defined in the interface : it must be public
    protected function foo() {}

    // other methods may be private
    private function bar() {}
}

?>
Command Line Classes/ImplementedMethodsArePublic
Analyzers Analyze

Implicit Global

Global variables, that are used in local scope with global keyword, but are not declared as global in the global scope. They may be mistaken with distinct values, while, in PHP, variables in the global scope are truly global.

<?php

// This is implicitely global
$implicitGlobal = 1;

global $explicitGlobal;
$explicitGlobal = 2;

foo();
echo $explicitFunctionGlobal;

function foo() {
    // This global is needed, but not the one in the global space
    global $implicitGlobal, $explicitGlobal, $explicitFunctionGlobal;

    // This won't be a global, as it must be 'global' in a function scope
    $notImplicitGlobal = 3;
    $explicitFunctionGlobal = 3;
}

?>
Command Line Structures/ImplicitGlobal
Analyzers Analyze

Incompilable Files

Files that cannot be compiled, and, as such, be run by PHP. Scripts are linted against various versions of PHP.

This is usually undesirable, as all code must compile before being executed. It may simply be that such files are not compilable because they are not yet ready for an upcoming PHP version.

<?php

// Can't compile this : Print only accepts one argument
print $a, $b, $c;

?>

Code that is incompilable with older PHP versions means that the code is breaking backward compatibility : good or bad is project decision.

When the code is used as a template for PHP code generation, for example at installation time, it is recommended to use a distinct file extension, so as to distinguish them from actual PHP code.

Command Line Php/Incompilable
clearPHP no-incompilable
Analyzers Analyze

Indices Are Int Or String

Indices in an array notation such as $array[‘indice’] may only be integers or string.

Boolean, Null or float will be converted to their integer or string equivalent.

<?php
    $a = [true => 1,
          1.0  => 2,
          1.2  => 3,
          1    => 4,
          '1'  => 5,
          0.8  => 6,
          0x1  => 7,
          01   => 8,

          null  => 1,
          ''    => 2,

          false => 1,
          0     => 2,

          '0.8' => 3,
          '01'  => 4,
          '2a'  => 5
          ];

    print_r($a);
?>::


Array
(
    [1] => 8
    [0] => 2
    [] => 2
    [0.8] => 3
    [01] => 4
    [2a] => 5
)

Decimal numbers are rounded to the closest integer; Null is transtyped to ‘’ (empty string); true is 1 and false is 0; Integers in strings are transtyped, while partial numbers or decimals are not analyzed in strings.

As a general rule of thumb, only use integers or strings that don’t look like integers.

This analyzer may find constant definitions, when available.

Command Line Structures/IndicesAreIntOrString
Analyzers Analyze

Indirect Injection

Look for injections through indirect usage for GPRC values ($_GET, $_POST, $_REQUEST, $_COOKIE).

<?php

$a = $_GET['a'];
echo $a;

?>
Command Line Security/IndirectInjection
Analyzers Security

Instantiating Abstract Class

Those code will raise a PHP fatal error at execution time : ‘Cannot instantiate abstract class’. The classes are actually abstract classes, and should be derived into a concrete class to be instantiated.

Command Line Classes/InstantiatingAbstractClass
Analyzers Analyze

Interpolation

The following strings contain variables that are will be replaced. However, the following characters are ambiguous, and may lead to confusion.

For example, “$x[1]->b”.will be read by PHP as $x[1].->b” and not like “{$x[1]->b}”.

It is advised to add curly brackets around those structures to make them non-ambiguous.

Command Line Type/StringInterpolation
Analyzers Coding Conventions

Invalid Constant Name

According to PHP’s manual, constant names, ‘ A valid constant name starts with a letter or underscore, followed by any number of letters, numbers, or underscores.’.

Constant, when defined using ‘define() function, must follow this regex ::

/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/
<?php

define('+3', 1); // wrong constant!

echo constant('+3'); // invalid constant access

?>
Command Line Constants/InvalidName
Analyzers Analyze

Invalid Octal In String

Starting with PHP 7.1, any octal sequence inside a string can’t be beyong 7. Those will be a fatal error at parsing time.

In PHP 7.0 and older, those sequences were silently adapted (divided by 0).

<?php

// Emit no error in PHP 7.1
echo 0; // @

// Emit an error in PHP 7.1
echo 0; // @

?>

See also Integers.

Command Line Type/OctalInString
Analyzers CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Is Actually Zero

This addition actually may be reduced because one term is actually negated by another.

This kind of error happens when the expression is very large : the more terms are included, the more chances are that some auto-annihilation happens.

This error may also be a simple typo : for example, calculating the difference between two consecutive terms.

<?php

// This is quite obvious
$a = 2 - 2;

// This is obvious too. This may be a typo-ed difference between two consecutive terms.
// Could have been $c = $fx[3][4] - $fx[3][3] or $c = $fx[3][5] - $fx[3][4];
$c = $fx[3][4] - $fx[3][4];

// This is less obivous
$a = $b[3] - $c + $d->foo(1,2,3) + $c + $b[3];

?>
Command Line Structures/IsZero
Analyzers Analyze

Is Zend Framework 1 Controller

Mark a class as being a Zend Framework Controller.

<?php

class AController extends Zend_Controller_Action {
    // Controller code
}

?>
Command Line ZendF/IsController
Analyzers ZendFramework

Is Zend Framework 1 Helper

Mark a class as being a Zend Framework Helper.

<?php

class AnHelper extends Zend_View_Helper_Abstract {
    // Controller code
}

?>
Command Line ZendF/IsHelper
Analyzers ZendFramework

Isset With Constant

Until PHP 7, it was possible to use arrays as constants, but it was not possible to test them with ‘isset.

<?php
const X = [1,2,3];

if ('isset(X[4])) {}
?>

This would yield an error :

Fatal error: Cannot use ‘isset() on the result of an expression (you can use “null !== expression” instead) in test.php on line 7

This is a backward incompatibility.

Command Line Structures/IssetWithConstant
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Join file()

Applying ‘join() or ‘implode() to the result of ‘file() provides the same results than using ‘file_get_contents(), but at a higher cost of memory and processing.

<?php

// memory and CPU intensive
$content = join('', file('path/to/file.txt'));

// memory intensive
$content = file_get_contents('path/to/file.txt');

// Consider reading the data line by line and processing it along the way,
// to save memory
$fp = fopen('path/to/file.txt', 'r');
while($line = fget($fp)) {
    // process a line
}
fclose($fp);

?>

Always use ‘file_get_contents() to get the content of a file as a string.

Command Line Performances/JoinFile
Analyzers Performances

List Short Syntax

Usage of short syntax version of list().

<?php

// PHP 7.1 short list syntax
// PHP 7.1 may also use key => value structures with list
[$a, $b, $c] = ['2', 3, '4'];

// PHP 7.0 list syntax
list($a, $b, $c) = ['2', 3, '4'];

?>
Command Line Php/ListShortSyntax
Analyzers CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

List With Appends

List() behavior has changed in PHP 7.0 and it has impact on the indexing when list is used with the [] operator.

<?php

$x = array();
list($x[], $x[], $x[]) = [1, 2, 3];

print_r($x);

?>

In PHP 7.0, results are ::

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
)

In PHP 5.6, results are ::

Array
(
    [0] => 3
    [1] => 2
    [2] => 1
)
Command Line Php/ListWithAppends
Analyzers CompatibilityPHP70, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP71

List With Keys

Setting keys when using list() is a PHP 7.1 feature.

<?php

// PHP 7.1 and later only
list('a' => $a, 'b' => $b) = ['b' => 1, 'c' => 2, 'a' => 3];

?>
Command Line Php/ListWithKeys
Analyzers CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Locally Unused Property

Those properties are defined in a class, and this class doesn’t have any method that makes use of them.

While this is syntacticly correct, it is unusual that defined ressources are used in a child class. It may be worth moving the definition to another class, or to move accessing methods to the class.

<?php

class foo {
    public $unused, $used;// property $unused is never used in this class

    function bar() {
        $this->used++; // property $used is used in this method
    }
}

class foofoo extends foo {
    function bar() {
        $this->unused++; // property $unused is used in this method, but defined in the parent class
    }
}

?>
Command Line Classes/LocallyUnusedProperty
Analyzers Analyze, Dead code

Logical Mistakes

Avoid logical mistakes within long expressions.

Sometimes, the logic is not what it seems. It is important to check the actual impact of every part of the logical expression. Do not hesitate to make a table with all possible cases. If those cases are too numerous, it may be time to rethink the whole expression.

<?php

// Always false
if ($a != 1 || $a != 2) { }

// $a == 1 is useless
if ($a == 1 || $a != 2) {}

// Always false
if ($a == 1 && $a == 2) {}

// $a != 2 is useless
if ($a == 1 && $a != 2) {}

?>

Based on article from Andrey Karpov Logical Expressions in C/C++. Mistakes Made by Professionals

Command Line Structures/LogicalMistakes
Analyzers Analyze

Logical Should Use Symbolic Operators

Logical operators come in two flavors : and / &&, || / or, ^ / xor. However, they are not exchangeable, as && and and have different precedence.

It is recommended to use the symbol operators, rather than the letter ones.

Command Line Php/LogicalInLetters
clearPHP no-letter-logical
Analyzers Analyze

Logical To in_array

Multiples exclusive comparisons may be replaced by ‘in_array().

‘in_array() makes the alternatives more readable, especially when the number of alternatives is large. In fact, the list of alternative may even be set in a variable, and centralized for easier management.

Even two ‘or’ comparisons are slower than using a ‘in_array() call. More calls are even slower than just two. This is a micro-optimisation : speed gain is low, and marginal. Code centralisation is a more significant advantage.

<?php

// Set the list of alternative in a variable, property or constant.
$valid_values = array(1, 2, 3, 4);
if (in_array($a, $valid_values) ) {
    // doSomething()
}

if ($a == 1 || $a == 2 || $a == 3 || $a == 4) {
    // doSomething()
}

// in_array also works with strict comparisons
if (in_array($a, $valid_values, true) ) {
    // doSomething()
}

if ($a === 1 || $a === 2 || $a === 3 || $a === 4) {
    // doSomething()
}

?>

See also in_array().

Command Line Performances/LogicalToInArray
Analyzers Analyze

Lone Blocks

Any grouped code without a commanding structure is useless.

Blocks are compulsory when defining a structure, such as a class or a function. They are most often used with flow control instructions, like if then or switch.

Blocks are also valid syntax that group several instructions together, though they have no effect at all, except confuse the reader. Most often, it is a ruin from a previous flow control instruction, whose condition was removed or commented. They should be removed.

<?php

    // Lone block
    //foreach($a as $b)
    {
        $b++;
    }
?>
Command Line Structures/LoneBlock
Analyzers Analyze

Long Arguments

Long arguments should be put in variable, to preserve readability.

When literal arguments are too long, they ‘break the hosting structure by moving the next argument too far on the right. Whenever possible, long arguments should be set in a local variable to keep the readability.

<?php

// Now the call to foo() is easier to read.
$reallyBigNumber = <<<BIGNUMBER
123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
BIGNUMBER
foo($reallyBigNumber, 2, '12345678901234567890123456789012345678901234567890');

// where are the next arguments ?
foo('123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890', 2, '123456789012345678901234567890123456789012345678901234567890');

// This is still difficult to read
foo(<<<BIGNUMBER
123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
BIGNUMBER
, 2, '123456789012345678901234567890123456789012345678901234567890');

?>

Literal strings and heredoc strings, including variables, that are over 50 chars longs are reported here.

Command Line Structures/LongArguments
Analyzers Analyze

Lost References

Either avoid references, or propagate them correctly.

When assigning a referenced variable with another reference, the initial reference is lost, while the intend was to transfer the content.

<?php

function foo(&$lostReference, &$keptReference)
{
    $c = 'c';

    // $lostReference was a reference, but now, it is another
    $lostReference =& $c;
    // $keptReference was a reference : now it contains the actual value
    $keptReference = $c;
}

$bar = 'bar';
$bar2 = 'bar';
foo($bar, $bar2);

//displays bar c, instead of bar bar
print $bar. ' '.$bar2;

?>

Do not reassign a reference with another reference. Assign new content to the reference to change its value.

Command Line Variables/LostReferences
Analyzers Analyze

Magic Visibility

The class magic methods must have public visibility and cannot be static.

<?php

class foo{
    // magic method must bt public and non-static
    public static function '__clone($name) {    }

    // magic method can't be private
    private function '__get($name) {    }

    // magic method can't be protected
    private function '__set($name, $value) {    }

    // magic method can't be static
    public static function '__isset($name) {    }
}

?>

See Magic methods.

Command Line Classes/toStringPss
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72

Make Global A Property

Calling global (or $GLOBALS) in methods is slower and less testable than setting the global to a property, and using this property.

Using properties is slightly faster than calling global or $GLOBALS, though the gain is not important.

Setting the property in the constructor (or in a factory), makes the class easier to test, as there is now a simple point of configuration.

<?php

// Wrong way
class fooBad {
    function x() {
        global $a;
        $a->do();
        // Or $GLOBALS['a']->do();
    }
}

class fooGood {
    private $bar = null;

    function '__construct() {
        global $bar;
        $this->bar = $bar;
        // Even better, do this via arguments
    }

    function x() {
        $this->a->do();
    }
}

?>
Command Line Classes/MakeGlobalAProperty
Analyzers Analyze

Make One Call With Array

Avoid calling the same function several times by batching the calls with arrays.

Calling the same function to chain modifications tends to be slower than calling the same function with all the transformations at the same time. Some PHP functions accept scalars or arrays, and using the later is more efficient.

<?php

$string = 'abcdef';

//'str_replace() accepts arrays as arguments
$string = str_replace( ['a', 'b', 'c'],
                       ['A', 'B', 'C'],
                       $string);

// Too many calls to str_replace
$string = str_replace( 'a', 'A', $string);
$string = str_replace( 'b', 'B', $string);
$string = str_replace( 'c', 'C', $string);

// Too many nested calls to str_replace
$string = str_replace( 'a', 'A', str_replace( 'b', 'B', str_replace( 'c', 'C', $string)));

?>

Potential replacements :

Function Replacement
‘str_replace() ‘str_ireplace() ‘substr_replace() ‘preg_replace() ‘preg_replace_callback() ‘str_replace() ‘str_replace() ‘substr_replace() ‘preg_replace() ‘preg_replace_callback_array()
<?php
$subject = 'Aaaaaa Bbb';


//'preg_replace_callback_array() is better than multiple preg_replace_callback :
preg_replace_callback_array(
    [
        '~[a]+~i' => function ($match) {
            echo strlen($match[0]), ' matches for a found', PHP_EOL;
        },
        '~[b]+~i' => function ($match) {
            echo strlen($match[0]), ' matches for b found', PHP_EOL;
        }
    ],
    $subject
);

$result = preg_replace_callback('~[a]+~i', function ($match) {
            echo strlen($match[0]), ' matches for a found', PHP_EOL;
        }, $subject);

$result = preg_replace_callback('~[b]+~i', function ($match) {
            echo strlen($match[0]), ' matches for b found', PHP_EOL;
        }, $subject);

//'str_replace() accepts arrays as arguments
$string = str_replace( ['a', 'b', 'c'],
                       ['A', 'B', 'C'],
                       $string);

// Too many calls to str_replace
$string = str_replace( 'a', 'A');
$string = str_replace( 'b', 'B');
$string = str_replace( 'c', 'C');

?>
Command Line Performances/MakeOneCall
Analyzers Performances

Malformed Octal

Those numbers starts with a 0, so they are using the PHP octal convention. Therefore, one can’t use 8 or 9 figures in those numbers, as they don’t belong to the octal base. The resulting number will be truncated at the first erroneous figure. For example, 090 is actually 0, and 02689 is actually 22.

<?php

// A long way to write 0 in PHP 5
$a = 0890;

// A fatal error since PHP 7

?>

Also, note that very large octal, usually with more than 21 figures, will be turned into a real number and undergo a reduction in precision.

See also Integers.

Command Line Type/MalformedOctal
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Mark Callable

Create an attribute that guess what are the called function or methods, when possible.

Command Line Functions/MarkCallable
Analyzers Analyze

Method Used Below

Mark methods that are used in children classes.

<?php

class foo {
    // This method is used in children
    protected function protectedMethod() {}

    // This method is not used in children
    protected function localProtectedMethod() {}

    private function foobar() {
        // protectedMethod is used here, but defined in parent
        $this->localProtectedMethod();
    }
}

class foofoo extends foo {
    private function bar() {
        // protectedMethod is used here, but defined in parent
        $this->protectedMethod();
    }
}

?>

This doesn’t mark the current class, nor the (grand-)parent ones.

Command Line Classes/MethodUsedBelow
Analyzers Analyze

Methodcall On New

It is possible to call a method right at object instanciation.

This syntax was added in PHP 5.4+. Before, this was not possible : the object had to be stored in a variable first.

<?php

// Data is collected
$data = data_source();

// Data is saved, but won't be reused from this databaseRow object. It may be ignored.
$result = (new databaseRow($data))->save();

// The actual result of the save() is collected and tested.
if ($result !== true) {
    processSaveError($data);
}

?>

This syntax is interesting when the object is not reused, and may be discarded

Command Line Php/MethodCallOnNew
Analyzers CompatibilityPHP53

Mismatched Default Arguments

Arguments are relayed from one method to the other, and the arguments have different default values.

Although it is possible to have different default values, it is worth checking why this is actually the case.

<?php

function foo($a = null, $b = array() ) {
    // foo method calls directly bar.
    // When argument are provided, it's OK
    // When argument are omited, the default value is not the same as the next method
    bar($a, $b);
}

function bar($c = 1, $d = array() ) {

}

?>
Command Line Functions/MismatchedDefaultArguments
Analyzers Analyze

Mismatched Ternary Alternatives

A ternary operator should yield the same type on both branches.

Ternary operator applies a condition, and yield two different results. Those results will then be processed by code that expects the same types. It is recommended to match the types on both branches of the ternary operator.

<?php

// $object may end up in a very unstable state
$object = ($type == 'Type') ? new $type() : null;

//same result are provided by both alternative, though process is very different
$result = ($type == 'Addition') ? $a + $b : $a * $b;

//Currently, this is omitted
$a = 1;
$result = empty($condition) ? $a : 'default value';
$result = empty($condition) ? $a : getDefaultValue();

?>
Command Line Structures/MismatchedTernary
Analyzers Analyze

Mismatched Typehint

Relayed arguments don’t have the same typehint.

Typehint acts as a filter method. When an object is checked with a first class, and then checked again with a second distinct class, the whole process is always false : $a can’t be of two different classes at the same time.

<?php

// Foo() calls bar()
function foo(A $a, B $b) {
    bar($a, $b);
}

// $a is of A typehint in both methods, but
// $b is of B then BB typehing
function bar(A $a, BB $b) {

}

?>

Note : This analysis currently doesn’t check generalisation of classes : for example, when B is a child of BB, it is still reported as a mismatch.

Command Line Functions/MismatchedTypehint
Analyzers Analyze

Missing Cases In Switch

It seems that some cases are missing in this switch structure.

When comparing two differents ‘switch() structures, it appears that some cases are missing in one of them. The set of cases are almost identical, but one of the values are missing.

Switch() structures using strings as literals are compared in this analysis. When the discrepancy between two lists is below 25%, both switches are reported.

<?php

// This switch operates on a, b, c, d and default
switch($a) {
    case 'a': doSomethingA(); 'break 1;
    case 'b': doSomethingB(); 'break 1;
    case 'c': doSomethingC(); 'break 1;
    case 'd': doSomethingD(); 'break 1;
    default: doNothing();
}

// This switch operates on a, b, d and default
switch($o->p) {
    case 'a': doSomethingA(); 'break 1;
    case 'b': doSomethingB(); 'break 1;

    case 'd': doSomethingD(); 'break 1;
    default: doNothing();
}

?>

In the example, one may argue that the ‘c’ case is actually handled by the ‘default’ case. Otherwise, business logic may request that omission.

Command Line Structures/MissingCases
Analyzers Analyze

Mixed Concat And Interpolation

Mixed usage of concatenation and string interpolation is error prone. It is harder to read, and leads to overlooking the concatenation or the interpolation.

<?php

// Concatenation string
$a = $b . 'c' . $d;

// Interpolation strings
$a = {$b}c{$d};   // regular form
$a = {$b}c$d;     // irregular form

// Mixed Concatenation and Interpolation string
$a = {$b}c . $d;
$a = $b . c$d;
$a = $b . c{$d};

?>

This issue doesn’t change the output. It makes code less error prone.

Command Line Structures/MixedConcatInterpolation
Analyzers Coding Conventions, Analyze

Mixed Keys Arrays

Avoid mixing constants and literals in array keys.

When defining default values in arrays, it is recommended to avoid mixing constants and literals, as PHP may mistake them and overwrite the previous with the latter.

Either switch to a newer version of PHP (5.5 or newer), or make sure the resulting array is the one you expect. If not, reorder the definitions.

<?php

const ONE = 1;

$a = [ 1   => 2,
       ONE => 3];

?>
Command Line Arrays/MixedKeys
Analyzers CompatibilityPHP53, CompatibilityPHP54

Mkdir Default

‘mkdir() gives universal access to created folders, by default. It is recommended to gives a more limited set of rights (0755, 0700), or to explicitely set the rights to 0777.

<?php

// By default, this dir is 777
mkdir('/path/to/dir');

// Explicitely, this is wanted. It may also be audited easily
mkdir('/path/to/dir', 0777);

// This dir is limited to the current user.
mkdir('/path/to/dir', 0700);

?>

See also Why 777 Folder Permissions are a Security Risk.

Command Line Security/MkdirDefault
Analyzers Security

Modernize Empty With Expression

‘empty() accept expressions since PHP 5.5. There is no need to store the expression in a variable before testing, unless it is reused later.

<?php

// PHP 5.5+ 'empty() usage
if (empty(strtolower($b . $c))) {
    doSomethingWithoutA();
}

// Compatible 'empty() usage
$a = strtolower($b . $c);
if (empty($a)) {
    doSomethingWithoutA();
}

// $a2 is reused, storage is legit
$a2 = strtolower($b . $c);
if (empty($a2)) {
    doSomething();
} else {
    echo $a2;
}

?>

See also empty().

Command Line Structures/ModernEmpty
Analyzers Analyze

Multiple Alias Definitions

Some aliases are representing differents classes across the repository. This leads to potential confusion.

Across an application, it is recommended to use the same namespace for one alias. Failing to do this lead to the same keyword to represent different values in different files, with different behavior. Those are hard to find bugs.

<?php

namespace A {
    use d\d; // aka D
}

// Those are usually in different files, rather than just different namespaces.

namespace B {
    use b\c as D; // also D. This could be named something else
}

?>
Command Line Namespaces/MultipleAliasDefinitions
Analyzers Analyze

Multiple Alias Definitions Per File

Avoid aliasing the same name with different aliases. This leads to confusion.

<?php

// first occurrence
use name\space\ClasseName;

// when this happens, several other uses are mentionned

// name\space\ClasseName has now two names
use name\space\ClasseName as anotherName;

?>

See also Multiple Alias Definitions.

Command Line Namespaces/MultipleAliasDefinitionPerFile
Analyzers Analyze

Multiple Class Declarations

It is possible to declare several times the same class in the code. PHP will not mention it until execution time, since declarations may be conditional.

<?php

$a = 1;

// Conditional declaration
if ($a == 1) {
    class foo {
        function method() { echo 'class 1';}
    }
} else {
    class foo {
        function method() { echo 'class 2';}
    }
}

(new foo())->method();
?>

It is recommended to avoid declaring several times the same class in the code. The best practice is to separate them with namespaces, they are for here for that purpose. In case those two classes are to be used interchangeably, the best is to use an abstract class or an interface.

Command Line Classes/MultipleDeclarations
Analyzers Analyze

Multiple Classes In One File

It is regarded as a bad practice to cram more than one class per file. This is usually done to make life of __autoload() easier.

It is often difficult to find class foo in the bar.php file. This is also the case for interfaces and traits.

One good reason to have multiple classes in one file is to reduce include time by providing everything into one nice include.

Command Line Classes/MultipleClassesInFile
Analyzers Coding Conventions

Multiple Constant Definition

Some constants are defined several times in your code. This will lead to a fatal error, if they are defined during the same execution.

Multiple definitions may happens at boostrap, when the application code is collecting information about the current environnement. It may also happen at inclusion time, which one set of constant being loaded, while other definition are not, avoiding conflict. Both are false positive.

<?php

// OS is defined twice.
if (PHP_OS == 'Windows') {
    define('OS', 'Win');
} else {
    define('OS', 'Other');
}

?>
Command Line Constants/MultipleConstantDefinition
Analyzers Analyze

Multiple Definition Of The Same Argument

A method’s signature is holding twice (or more) the same argument. For example, function x ($a, $a) { ... }.

This is accepted as is by PHP 5, and the last parameter’s value will be assigned to the variable. PHP 7.0 and more recent has dropped this feature, and reports a fatal error when linting the code.

<?php
  function x ($a, $a) { print $a; };
  x(1,2); => display 2
?>

However, this is not common programming practise : all arguments should be named differently.

See also Prepare for PHP 7 error messages (part 3).

Command Line Functions/MultipleSameArguments
clearPHP all-unique-arguments
Analyzers CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Multiple Exceptions Catch()

Starting with PHP 7.1, it is possible to have several distinct exceptions class caught by the same catch, preventing code repetition.

<?php

// PHP 7.1 and more recent
try {
    throw new someException();
} catch (Single $s) {
    doSomething();
} catch (oneType | anotherType $s) {
    processIdentically();
} finally {

}

// PHP 7.0 and older
try {
    throw new someException();
} catch (Single $s) {
    doSomething();
} catch (oneType $s) {
    processIdentically();
} catch (anotherType $s) {
    processIdentically();
} finally {

}

?>

This is a backward incompabitible feature of PHP 7.1.

Command Line Exceptions/MultipleCatch
Analyzers CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Multiple Identical Trait Or Interface

There is no need to use the same trait, or implements the same interface more than once.

Up to PHP 7.1 (at least), this doesn’t raise any warning. Traits are only imported once, and interfaces may be implemented as many times as wanted.

<?php

class foo {
    use t3,t3,t3;
}

class bar implements i,i,i {

}

?>
Command Line Classes/MultipleTraitOrInterface
Analyzers Analyze

Multiple Index Definition

Indexes that are defined multiple times in the same array.

<?php
    // Multiple identical keys
    $x = array(1 => 2,
               2 => 3,
               1 => 3);

    // Multiple identical keys (sneaky version)
    $x = array(1 => 2,
               1.1 => 3,
               true => 4);

    // Multiple identical keys (automated version)
    $x = array(1 => 2,
               3,        // This will be index 2
               2 => 4);  // this index is overwritten
?>

They are indeed overwriting each other. This is most probably a typo.

Command Line Arrays/MultipleIdenticalKeys
Analyzers Analyze

Multiple Type Variable

Avoid using the same variable with different types of data.

It is recommended to use different names for differently typed data, while processing them. This prevents errors where one believe the variable holds the former type, while it has already been casted to the later.

Incrementing variables, with math operations or concatenation, is OK : the content changes, but not the type. And casting the variable without storing it in itself is OK.

<?php

// $x is an array
$x = range('a', 'z');
// $x is now a string
$x = join('', $x);
$c = count($x); // $x is not an array anymore


// $letters is an array
$letters = range('a', 'z');
// $alphabet is a string
$alphabet = join('', $letters);

// Here, $letters is cast by PHP, but the variable is changed.
if ($letters) {
    $count = count($letters); // $letters is still an array
}

?>
Command Line Structures/MultipleTypeVariable
Analyzers Analyze

Multiples Identical Case

Some cases are defined multiple times, but only one will be processed. Check the list of cases, and remove the extra one.

Exakat tries to find the value of the case as much as possible, and ignore any dynamic cases (using variables).

<?php

case ($x) {
    case 1 :
        'break;
    case true:    // This is a duplicate of the previous
        'break;
    case 1 + 0:   // This is a duplicate of the previous
        'break;
    case 1.0 :    // This is a duplicate of the previous
        'break;
    case $y  :    // This is not reported.
        'break;
    default:

}
?>
Command Line Structures/MultipleDefinedCase
clearPHP no-duplicate-case
Analyzers Analyze

Multiply By One

Multiplying by 1 is useless.

If it is used to type cast a value to number, then casting (integer) or (real) is clearer. This behavior may change with PHP 7.1, which has unified the behavior of all hidden casts.

<?php

// Still the same value than $m, but now cast to integer or real
$m = $m * 1;

// Still the same value than $m, but now cast to integer or real
$n *= 1;

// make typecasting clear, and merge it with the producing call.
$n = (int) $n;

?>
Command Line Structures/MultiplyByOne
clearPHP no-useless-math
Analyzers Analyze

Must Return Methods

The following methods are expected to return a value that will be used later. Without return, they are useless.

Methods that must return are : ‘__get(), ‘__isset(), ‘__sleep(), ‘__toString(), ‘__set_state(), ‘__invoke(), ‘__debugInfo(). Methods that may not return, but are often expected to : ‘__call(), ‘__callStatic().

<?php

class foo {
    public function '__isset($a) {
        // returning something useful
        return 'isset($this->$var[$a]);
    }

    public function '__get($a) {
        $this->$a++;
        // not returning...
    }

    public function '__call($name, $args) {
        $this->$name(...$args);
        // not returning anything, but that's OK
    }

}
?>
Command Line Functions/MustReturn
Analyzers Analyze

Negative Power

The power operator ‘** has higher precedence than the sign operators + and -.

This means that -2 ‘** 2 == -4. It is in fact, -(2 ‘** 2).

When using negative power, it is clearer to add parenthesis or to use the ‘pow() function, which has no such ambiguity :

<?php

// -2 to the power of 2 (a square)
pow(-2, 2) == 4;

// minus 2 to the power of 2 (a negative square)
-2 '** 2 == -(2 '** 2) == 4;

?>
Command Line Structures/NegativePow
Analyzers Analyze

Nested Ifthen

Three levels of ifthen is too much. The method should be split into smaller functions.

<?php

function foo($a, $b) {
    if ($a == 1) {
        // Second level, possibly too much already
        if ($b == 2) {

        }
    }
}

function bar($a, $b, $c) {
    if ($a == 1) {
        // Second level.
        if ($b == 2) {
            // Third level level.
            if ($c == 3) {
                // Too much
            }
        }
    }
}

?>
Command Line Structures/NestedIfthen
Analyzers Analyze

Nested Ternary

Ternary operators should not be nested too deep.

They are a convenient instruction to apply some condition, and avoid a if() structure. It works best when it is simple, like in a one liner.

However, ternary operators tends to make the syntax very difficult to read when they are nested. It is then recommended to use an if() structure, and make the whole code readable.

<?php

// Simple ternary expression
echo ($a == 1 ? $b : $c) ;

// Nested ternary expressions
echo ($a === 1 ? $d === 2 ? $b : $d : $d === 3 ? $e : $c) ;
echo ($a === 1 ? $d === 2 ? $f ===4 ? $g : $h : $d : $d === 3 ? $e : $i === 5 ? $j : $k) ;

//Previous expressions, written as a if / Then expression
if ($a === 1) {
    if ($d === 2) {
        echo $b;
    } else {
        echo $d;
    }
} else {
    if ($d === 3) {
        echo $e;
    } else {
        echo $c;
    }
}

if ($a === 1) {
    if ($d === 2) {
        if ($f === 4) {
            echo $g;
        } else {
            echo $h;
        }
    } else {
        echo $d;
    }
} else {
    if ($d === 3) {
        echo $e;
    } else {
        if ($i === 5) {
            echo $j;
        } else {
            echo $k;
        }
    }
}

?>
Command Line Structures/NestedTernary
clearPHP no-nested-ternary
Analyzers Analyze

Never Used Properties

Properties that are never used. They are defined, but never actually used.

<?php

class foo {
    public $usedProperty = 1;

    // Never used anywhere
    public $unusedProperty = 2;

    function bar() {
        // Used internally
        ++$this->usedProperty;
    }
}

class foo2  extends foo {
    function bar2() {
        // Used in child class
        ++$this->usedProperty;
    }
}

// Used externally
++$this->usedProperty;

?>
Command Line Classes/PropertyNeverUsed
Analyzers Analyze

New Constants In PHP 7.2

The following constants are now native in PHP 7.2. It is advised to avoid using such names for constant before moving to this new version.

  • PHP_OS_FAMILY
  • PHP_FLOAT_DIG
  • PHP_FLOAT_EPSILON
  • PHP_FLOAT_MAX
  • PHP_FLOAT_MIN
  • SQLITE3_DETERMINISTIC
  • CURLSSLOPT_NO_REVOKE
  • CURLOPT_DEFAULT_PROTOCOL
  • CURLOPT_STREAM_WEIGHT
  • CURLMOPT_PUSHFUNCTION
  • CURL_PUSH_OK
  • CURL_PUSH_DENY
  • CURL_HTTP_VERSION_2TLS
  • CURLOPT_TFTP_NO_OPTIONS
  • CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE
  • CURLOPT_CONNECT_TO
  • CURLOPT_TCP_FASTOPEN
  • DNS_CAA

Note : PHP 7.2 is not out yet (2017-04-10). This list is currently temporary and may undergo changes until the final version is out.

Command Line Php/Php72NewConstants
Analyzers CompatibilityPHP72, CompatibilityPHP73

New Functions In PHP 5.4

PHP introduced new functions in PHP 5.4. If you have already defined functions with such names, you will get a conflict when trying to upgrade. It is advised to change those functions’ name.

Command Line Php/Php54NewFunctions
Analyzers CompatibilityPHP53, CompatibilityPHP71

New Functions In PHP 5.5

PHP introduced new functions in PHP 5.5. If you have already defined functions with such names, you will get a conflict when trying to upgrade. It is advised to change those functions’ name.

Command Line Php/Php55NewFunctions
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP71

New Functions In PHP 5.6

PHP introduced new functions in PHP 5.6. If you have already defined functions with such names, you will get a conflict when trying to upgrade. It is advised to change those functions’ name.

Command Line Php/Php56NewFunctions
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55

New Functions In PHP 7.0

The following functions are now native functions in PHP 7.0. It is advised to change them before moving to this new version.

  • get_resources
  • gc_mem_caches
  • preg_replace_callback_array
  • posix_setrlimit
  • random_bytes
  • random_int
  • intdiv
  • error_clear_last
Command Line Php/Php70NewFunctions
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP71

New Functions In PHP 7.1

The following functions are now native functions in PHP 7.1. It is advised to change them before moving to this new version.

  • curl_share_strerror()
  • curl_multi_errno()
  • curl_share_errno()
  • mb_ord()
  • mb_chr()
  • mb_scrub()
  • is_iterable()
Command Line Php/Php71NewFunctions
Analyzers CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

New Functions In PHP 7.2

The following functions are now native functions in PHP 7.2. It is advised to change them before moving to this new version.

  • mb_ord()
  • mb_chr()
  • mb_scrub()
  • stream_isatty()
  • ‘proc_nice() (Windows only)
Command Line Php/Php72NewFunctions
Analyzers CompatibilityPHP72, CompatibilityPHP73

New Functions In PHP 7.3

This documentation is still empty.

The following functions are now native functions in PHP 7.3. It is advised to change them before moving to this new version.

Command Line Php/Php73NewFunctions
Analyzers CompatibilityPHP73

Next Month Trap

Avoid using +1 month with strtotime().

strtotime() calculates the next month by incrementing the month number. For day number that do not exist from one month to the next, strtotime() fixes them by setting them in the next-next month.

This happens to January, March, May, July, August and October. January is also vulnerable for 29 (not every year), 30 and 31.

Avoid using ‘+1 month’, and rely on ‘first day of next month’ or ‘last day of next month’ to extract the next month’s name.

<?php

// Base date is October 31 => 10/31
// +1 month adds +1 to 10 => 11/31
// Since November 31rst doesn't exists, it is corrected to 12/01.
echo date('F', strtotime('+1 month',mktime(0,0,0,$i,31,2017))).PHP_EOL;

// Base date is October 31 => 10/31
echo date('F', strtotime('first day of next month',mktime(0,0,0,$i,31,2017))).PHP_EOL;

?>

See also It is the 31st again.

Command Line Structures/NextMonthTrap
Analyzers Analyze

No Boolean As Default

Default values should always be set up with constants.

Class constants or constants improve readability when calling the methods.

<?php

const CASE_INSENSITIVE = true;
const CASE_SENSITIVE = false;

function foo($case_insensitive = true) {
    // doSomething()
}

// Readable code
foo(CASE_INSENSITIVE);
foo(CASE_SENSITIVE);


// unreadable code  : is true case insensitive or case sensitive ?
foo(true);
foo(false);

?>
Command Line Functions/NoBooleanAsDefault
Analyzers Analyze

No Choice

A conditional structure is being used, but both alternatives are the same, leading to the illusion of choice.

Either the condition is useless, and may be removed, or the alternatives needs to be distinguished.

<?php

if ($condition == 2) {
    doSomething();
} else {
    doSomething();
}

$condition == 2 ?     doSomething() :     doSomething();

?>
Command Line Structures/NoChoice
Analyzers Analyze

No Class As Typehint

Avoid using whole classes as typehint. Always use interfaces, so that you may use different classes, or versions of classes.

<?php

class X {
    function foo() {}
}

interface i {
    function foo();
}

// X is a class : any update in the code requires changing / subclassing X or the rest of the code.
function bar(X $x) {
    $x->foo();
}

// I is an interface : X may implements this interface without refactoring and pass
// later, newer versions of X may get another name, but still implement I, and still pass
function bar2(I $x) {
    $x->foo();
}

?>

See also Type hinting for interfaces.

Command Line Functions/NoClassAsTypehint
Analyzers Analyze

No Class In Global

Avoid defining structures in Global namespace. Always prefer using a namespace. This will come handy later, either when publishing the code, or when importing a library, or even if PHP reclaims that name.

<?php

// Code prepared for later
namespace Foo {
    class Bar {}
}

// Code that may conflict with other names.
namespace {
    class Bar {}
}

?>
Command Line Php/NoClassInGlobal
Analyzers Analyze

No Count With 0

Comparing ‘count() and strlen() to 0 is a waste of resources. There are three distinct situations situations.

When comparing ‘count() with 0, with ===, ==, !==, !=, it is more efficient to use ‘empty(). Empty() is a language constructs that checks if a value is present, while ‘count() actually load the number of element.

<?php

// Checking if an array is empty
if (count($array) == 0) {
    // doSomething();
}
// This may be replaced with
if (empty($array)) {
    // doSomething();
}

?>

When comparing ‘count() strictly with 0 (>) it is more efficient to use !(‘empty())

<?php

// Checking if an array is empty
if (count($array) > 0) {
    // doSomething();
}
// This may be replaced with
if (!empty($array)) {
    // doSomething();
}

Of course comparing 'count() with negative values, or with >= is useless.

<?php

// Checking if an array is empty
if (count($array) < 0) {
    // This never happens
    // doSomething();
}

?>

Comparing ‘count() and strlen() with other values than 0 cannot be replaced with a comparison with ‘empty().

Note that this is a micro-optimisation : since PHP keeps track of the number of elements in arrays (or number of chars in strings), the total computing time of both operations is often lower than a ms. However, both functions tends to be heavily used, and may even be used inside loops.

Command Line Performances/NotCountNull
Analyzers Performances

No Direct Call To Magic Method

PHP magic methods, such as ‘__get(), ‘__set(), ... are supposed to be used in an object environnement, and not with direct call.

For example,

<?php
  print $x->'__get('a');

//should be written
  print $x->a;
?>

Accessing those methods in a static way is also discouraged.

Command Line Classes/DirectCallToMagicMethod
Analyzers Analyze

No Direct Input To Wpdb

Avoid using incoming variables when building SQL queries with $wpdb->prepare()

(This is quoted directly from Anthony Ferrera blog, link below). In general however, go through and remove all user input from the $query side of ->prepare(). NEVER pass user input to the query side. Meaning, never do this (in any form):

<?php
  $where = $wpdb->prepare(' WHERE foo = %s', $_GET['data']);
  $query = $wpdb->prepare('SELECT * FROM something $where LIMIT %d, %d', 1, 2);
?>

This is known as ‘double-preparing’ and is not a good design. (End of quote).

See also https://blog.ircmaxell.com/2017/10/disclosure-wordpress-wpdb-sql-injection-technical.html.

Command Line Wordpress/NoDirectInputToWpdb
Analyzers Wordpress

No Direct Usage

The results of the following functions shouldn’t be used directly, but checked first.

For example, ‘glob() returns an array, unless some error happens, in which case it returns a boolean (false). In such case, however rare it is, plugging ‘glob() directly in a ‘foreach() loops will yield errors.

<?php
    // Used without check :
    foreach(glob('.') as $file) { /* do Something */ }.

    // Used without check :
    $files = glob('.');
    if (!is_array($files)) {
        foreach($files as $file) { /* do Something */ }.
    }
?>
Command Line Structures/NoDirectUsage
Analyzers Analyze

No Echo In Route Callable

Avoid using echo(), ‘print() or any printable PHP within the route callable method.

echo() works, it is prevents the code from setting any status code. This leads to confusion when the code return the content, but fail to set the right HTTP codes.

Slim 4.0 will require to only use the return method : the route callable is required to return a response.

<?php
$app = new Slim\App();

// This works as expected, with or without status
$app->get('/', function ($request, $response, $args) {
    return new MyResponseInterface ('content');
});

// This works, but only on surface
$app->get('/', function ($request, $response, $args) {
    echo 'content';
});

?>

See PSR7 and PSR 7 and Value Objects.

Command Line Slim/NoEchoInRouteCallable
Analyzers Slim

No Empty Regex

PHP regex don’t accept empty regex, nor regex with alphanumeric delimiter.

Most of those errors happen at execution time, when the regex is build dynamically, but still may end empty. At compile time, such error are made when the code is not tested before commit.

<?php

// No empty regex
preg_match('', $string, $r);

// Delimiter must be non-alphanumerical
preg_replace('1abc1', $string, $r);

// Delimiter must be non-alphanumerical
preg_replace('1'.$regex.'1', $string, $r);

?>

See also PCRE and Delimiters.

Command Line Structures/NoEmptyRegex
Analyzers Analyze

No Global Modification

Avoid modifying directly Wordpress global variables.

It is recommended to use the function API instead.

<?php

my_hook() {
    // Avoid changing those variables, as Wordpress handles them
    $GLOBALS['is_safari'] = true;
}

?>

See also Global Variables

Command Line Wordpress/NoGlobalModification
Analyzers Wordpress

No Hardcoded Hash

Hash should never be hardcoded.

Hashes may be MD5, SHA1, SHA512, Bcrypt or any other. Such values must be easily changed, for security reasons, and the source code is not the safest place to hide it.

<?php

    // Those strings may be sha512 hashes.
    // it is recomemdned to check if they are static or should be put into configuration
    $init512 = array( // initial values for SHA512
        '6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
    );

?>
Command Line Structures/NoHardcodedHash
Analyzers Analyze, Security

No Hardcoded Ip

Do not leave hard coded IP in your code.

It is recommended to move such configuration in external files or databases, for each update. This may also come handy when testing.

<?php

// This IPv4 is hardcoded.
$ip = '183.207.224.50';
// This IPv6 is hardcoded.
$ip = '2001:0db8:85a3:0000:0000:8a2e:0370:7334';

// This looks like an IP
$thisIsNotAnIP = '213.187.99.50';
$thisIsNotAnIP = '2133:1387:9393:5330';

?>

127.0.0.1, ::1 and ::0 are omitted, and not considered as a violation.

Command Line Structures/NoHardcodedIp
Analyzers Analyze, Security

No Hardcoded Path

It is not recommended to have literals when accessing files.

Either use ‘__FILE__ and ‘__DIR__ to make the path relative to the current file; use a DOC_ROOT as a configuration constant that will allow you to move your script later or rely on functions likes ‘sys_get_temp_dir(), to reach special folders.

<?php

    // This depends on the current executed script
    file_get_contents('token.txt');

    // Exotic protocols are ignored
    file_get_contents('jackalope://file.txt');

    // Some protocols are ignored : http, https, ftp, ssh2, php (with memory)
    file_get_contents('http://www.php.net/');
    file_get_contents('php://memory/');

    // 'glob() with special chars * and ? are not reported
    glob('./*/foo/bar?.txt');
    // 'glob() without special chars * and ? are reported
    glob('/foo/bar/');

?>
Command Line Structures/NoHardcodedPath
clearPHP no-hardcoded-path
Analyzers Analyze

No Hardcoded Port

When connecting to a remove serve, port is an important information. It is recommended to make this configurable (with constant or configuration), to as to be able to change this value without changing the code.

Command Line Structures/NoHardcodedPort
Analyzers Analyze, Security

No Implied If

It is possible to emulate a if/then structure by using the operators ‘and’ and ‘or’. Since optimizations will be applied to them : when the left operand of ‘and’ is false, the right one is not executed, as its result is useless; when the left operand of ‘or’ is true, the right one is not executed, as its result is useless;

However, such structures are confusing. It is easy to misread them as conditions, and ignore an important logic step.

<?php

// Either connect, or 'die
mysql_connect('localhost', $user, $pass) or 'die();

// Defines a constant if not found.
defined('SOME_CONSTANT') and define('SOME_CONSTANT', 1);

?>

It is recommended to use a real ‘if then’ structures, to make the condition readable.

Command Line Structures/ImpliedIf
clearPHP no-implied-if
Analyzers Analyze

No Isset With Empty

Empty() actually does the job of Isset() too.

From the manual : No warning is generated if the variable does not exist. That means ‘empty() is essentially the concise equivalent to !`’isset( <http://www.php.net/isset>`_$var) || $var == false.

<?php


// Enough tests
if (i!empty($a)) {
    doSomething();
}

// Too many tests
if ('isset($a) && !empty($a)) {
    doSomething();
}

?>
Command Line Structures/NoIssetWithEmpty
Analyzers Analyze

No List With String

list() can’t be used anymore to access particular offset in a string. This should be done with substr() or $string[$offset] syntax.

<?php

$x = 'abc';
list($a, $b, $c) = $x;

//list($a, $b, $c) = 'abc'; Never works

print $c;
// PHP 5.6- displays 'c'
// PHP 7.0+ displays nothing

?>

See also PHP 7.0 Backward incompatible changes : list() can no longer unpack string variables.

Command Line Php/NoListWithString
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

No Magic With Array

Magic method ‘__get() doesn’t work for array syntax.

When overloading properties, they can only be used for scalar values, excluding arrays. Under the hood, PHP uses ‘__get() to reach for the name of the property, and doesn’t recognize the following index as an array. It yields an error : Indirect modification of overloaded property.

<?php

class c {
    private $a;
    private $o = array();

    function '__get($name) {
        return $this->o[$name];
    }

    function foo() {
        // property b doesn't exists
        $this->b['a'] = 3;

        print_r($this);
    }

    // This method has no impact on the issue
    function '__set($name, $value) {
        $this->o[$name] = $value;
    }
}

$c = new c();
$c->foo();

?>

This is not reported by linting.

In this analysis, only properties that are found to be magic are reported. For example, using the b property outside the class scope is not reported, as it would yield too many false-positives.

See also Overload.

Command Line Classes/NoMagicWithArray
Analyzers Analyze

No Need For Else

Else is not needed when the Then ends with a ‘break. A ‘break may be the following keywords : ‘break, ‘continue, return, goto. Any of these send the execution somewhere in the code. The else block is then executed as the main sequence, only if the condition fails.

<?php

function foo() {
    // Else may be in the main sequence.
    if ($a1) {
        return $a1;
    } else {
        $a++;
    }

    // Same as above, but negate the condition : if (!$a2) { return $a2; }
    if ($a2) {
        $a++;
    } else {
        return $a2;
    }

    // This is OK
    if ($a3) {
        return;
    }

    // This has no 'break
    if ($a4) {
        $a++;
    } else {
        $b++;
    }

    // This has no else
    if ($a5) {
        $a++;
    }
}
?>

See also Object Calisthenics, rule number 2.

Command Line Structures/NoNeedForElse
Analyzers Analyze

No Parenthesis For Language Construct

Some PHP language constructs, such are include, print, echo don’t need parenthesis. They will handle parenthesis, but it is may lead to strange situations.

It it better to avoid using parenthesis with echo, print, return, throw, include and require (and _once).

Command Line Structures/NoParenthesisForLanguageConstruct
clearPHP no-parenthesis-for-language-construct
Analyzers Analyze

No Plus One

Incrementing a variable should be done with the ++ or – operators. Any other way, may be avoided.

<?php

// Best way to increment
++$x; --$y;

// Second best way to increment, if the current value is needed :
echo $x++, $y--;

// Good but slow
$x += 1;
$x -= -1;

$y += -1;
$y -= 1;

// even slower
$x = $x + 1;
$y = $y - 1;

?>
Command Line Structures/PlusEgalOne
Analyzers Coding Conventions

No Public Access

The properties below are declared with public access, but are never used publicly. They can be made protected or private.

<?php

class foo {
    public $bar = 1;            // Public, and used in public space
    public $neverInPublic = 3;  // Public, but never used in outside the class

    function bar() {
        $neverInPublic++;
    }
}

$x = new foo();
$x->bar = 3;
$x->bar();

?>
Command Line Classes/NoPublicAccess
Analyzers Analyze

No Real Comparison

Avoid comparing decimal numbers with ==, ===, !==, !=. Real numbers have an error margin which is random, and makes it very difficult to match even if the compared value is a literal.

PHP uses an internal representation in base 2 : any number difficult to represent with this base (like 0.1 or 0.7) will have a margin of error.

<?php

$a = 1/7;
$b = 2.0;

// 7 * $a is a real, not an integer
var_dump( 7 * $a === 1);

// rounding error leads to wrong comparison
var_dump( (0.1 + 0.7) * 10 == 8);
// although
var_dump( (0.1 + 0.7) * 10);
// displays 8

// precision formula to use with reals. Adapt 0.0001 to your precision needs
var_dump( abs(((0.1 + 0.7) * 10) - 8) < 0.0001);

?>

Use precision formulas with ‘abs() to approximate values with a given precision, or avoid reals altogether.

Command Line Type/NoRealComparison
clearPHP no-real-comparison
Analyzers Analyze

No Reference On Left Side

Do not use references as the right element in an assignation.

<?php

$b = 2;
$c = 3;

$a = &$b + $c;
// $a === 2 === $b;

$a = $b + $c;
// $a === 5

?>

This is the case for most situations : addition, multiplication, bitshift, logical, power, concatenation. Note that PHP won’t compile the code if the operator is a short operator (+=, .=, etc.), nor if the & is on the right side of the operator.

Command Line Structures/NoReferenceOnLeft
Analyzers Analyze

No Return Or Throw In Finally

Avoid using return and throw in a finally block. Both command will interrupt the processing of the try catch block, and any exception that was emitted will not be processed. This leads to unprocessed exceptions, leaving the application in an unstable state.

Note that PHP prevents the usage of goto, ‘break and ‘continue within the finally block at linting phase. This is categorized as a Security problem.

<?php
function foo() {
        try {
            // Exception is thrown here
            throw new \Exception();
        } catch (Exception $e) {
            // This is executed AFTER finally
            return 'Exception';
        } finally {
            // This is executed BEFORE catch
            return 'Finally';
        }
    }
}

// Displays 'Finally'. No exception
echo foo();

function bar() {
        try {
            // Exception is thrown here
            throw new \Exception();
        } catch (Exception $e) {
            // Process the exception.
            return 'Exception';
        } finally {
            // clean the current situation
            // Keep running the current function
        }
        return 'Finally';
    }
}

// Displays 'Exception', with processed Exception
echo bar();

?>

See also Return Inside Finally Block.

Command Line Structures/NoReturnInFinally
Analyzers Security

No Return Used

The return value of the following functions are never used. The return argument may be dropped from the code, as it is dead code.

This analysis supports functions and static methods, when a definition may be found. It doesn’t support method calls.

<?php

function foo($a = 1;) { return 1; }
foo();
foo();
foo();
foo();
foo();
foo();

// This function doesn't return anything.
function foo2() { }

// The following function are used in an expression, thus the return is important
function foo3() {  return 1;}
function foo4() {  return 1;}
function foo5() {  return 1;}

foo3() + 1;
$a = foo4();
foo(foo5());

?>
Command Line Functions/NoReturnUsed
Analyzers Analyze

No Self Referencing Constant

It is not possible to use ‘self’ when defining a constant in a class. It will yield a fatal error at runtime.

<?php
    class a {
        const C1 = 1;
        const C2 = self::C1;
        const C3 = a::C3;
    }
?>

The code needs to reference the full class’s name to do so, without using the current class’s name.

<?php
    class a {
        const C1 = 1;
        const C2 = a::C1;
    }
?>
Command Line Classes/NoSelfReferencingConstant
Analyzers Analyze

No String With Append

PHP 7 doesn’t allow the usage of [] with strings. [] is an array-only oeprator.

<?php

$string = 'abc';

// Not possible in PHP 7
$string[] = 'd';

?>

This was possible in PHP 5.*, but is now forbidden in PHP 7.

Command Line Php/NoStringWithAppend
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

No Substr Minus One

Negative index were introduced in PHP 7.1. This syntax is not compatible with PHP 7.0 and older.

<?php
$string = 'abc';

echo $string[-1]; // c

echo $string[1]; // a

?>

Seel also Generalize support of negative string offsets.

Command Line Php/NoSubstrMinusOne
Analyzers CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

No Substr() One

Use array notation $string[$position] to reach a single byte in a string.

There are two ways to access a byte in a string : substr() and $v[$pos];

The second one is more readable. It may be up to four times faster, though it is a micro-optimization. It is recommended to use it.

PHP 7.1 also introduces the support of negative offsets as string index : negative offset are also reported here.

<?php

$string = ab人cde;

echo substr($string, $pos, 1);
echo $string[$pos];
echo mb_substr($string, $pos, 1);

// $pos = 1
// bbb
// $pos = 2
// ??人

?>

Beware that substr() and $v[$pos] are similar, while ‘mb_substr() is not. The first functions works on bytes, while the latter works on characters.

Command Line Structures/NoSubstrOne
Analyzers Analyze, Performances, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

No array_merge() In Loops

‘array_merge() is memory intensive : every call will duplicate the arguments in memory, before merging them.

To handle arrays that may be quite big, it is recommended to avoid using ‘array_merge() in a loop. Instead, one should use ‘array_merge() with as many arguments as possible, making the merge a on time call.

<?php

// Creating a large multidimensional array
$source = ['a' => ['a', 'b', /*...*/],
           'b' => ['b', 'c', 'd', /*...*/],
           /*...*/
           ];

// Slow way
$b = array();
foreach($source as $key => $values) {
    $b = array_merge($b, $values);
}


// Faster way
$b = array();
foreach($source as $key => $values) {
    //Collect in an array
    $b[] = $values;
}
// One call to array_merge
$b = call_user_func_array('array_merge', $b);
// or with variadic
$b = call_user_func('array_merge', ..$b);

// Fastest way (with above example, without checking nor data pulling)
$b = call_user_func_array('array_merge', array_values($source))
// or
$b = call_user_func('array_merge', ...array_values($source))

?>

Note that ‘array_merge_recursive() and ‘file_put_contents() are also affected and reported.

Command Line Performances/ArrayMergeInLoops
clearPHP no-array_merge-in-loop
Analyzers Analyze, Performances

Non Ascii Variables

PHP supports variables with certain characters. The variable name must only include letters, figures, underscores and ASCII characters from 128 to 255.

In practice, letters outside the scope of a-zA-Z0-9 are rare, and require more care when editing the code or passing it from OS to OS.

<?php

class  {
    // An actual working class in PHP.
    public function '__construct() {
        echo '__CLASS__;
    }
}

$people = new ();

?>

See also Variables.

Command Line Variables/VariableNonascii
Analyzers Analyze

Non Static Methods Called In A Static

Static methods have to be declared as such (using the static keyword). Then, one may call them without instantiating the object.

PHP 7.0, and more recent versions, yield a deprecated error : ‘Non-static method A::B() should not be called statically’ .

PHP 5 and older doesn’t check that a method is static or not : at any point, you may call one method statically :

<?php
    class x {
        static public function sm( ) { echo '__METHOD__.\n; }
        public public sm( ) { echo '__METHOD__.\n; }
    }

    x::sm( ); // echo x::sm
?>

It is a bad idea to call non-static method statically. Such method may make use of special variable $this, which will be undefined. PHP will not check those calls at compile time, nor at running time.

It is recommended to update this situation : make the method actually static, or use it only in object context.

Note that this analysis reports all static method call made on a non-static method, even within the same class or class hierarchy. PHP silently accepts static call to any in-family method.

<?php
    class x {
        public function foo( ) { self::bar() }
        public function bar( ) { echo '__METHOD__.\n; }
    }
?>

See also static keyword.

Command Line Classes/NonStaticMethodsCalledStatic
Analyzers Analyze, CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Non-constant Index In Array

Undefined constants revert as strings in Arrays. They are also called barewords.

In ‘$array[index]’, PHP cannot find index as a constant, but, as a default behavior, turns it into the string ‘index’.

This default behavior raise concerns when a corresponding constant is defined, either using ‘define() or the const keyword (outside a class). The definition of the index constant will modify the behavior of the index, as it will now use the constant definition, and not the ‘index’ string.

<?php

// assign 1 to the element index in $array
// index will fallback to string
$array[index] = 1;
//PHP Notice:  Use of undefined constant index - assumed 'index'

echo $array[index];      // display 1 and the above error
echo $array[index];    // display 1
echo $array[index];  // Syntax error


define('index', 2);

 // now 1 to the element 2 in $array
 $array[index] = 1;

?>

It is recommended to make index a real string (with ‘ or ”), or to define the corresponding constant to avoid any future surprise.

Note that PHP 7.2 removes the support for this feature : PHP RFC: Deprecate and Remove Bareword (Unquoted) Strings.

Command Line Arrays/NonConstantArray
Analyzers Analyze

Non-lowercase Keywords

Usual convention is to write PHP keywords (like as, foreach, switch, case, ‘break, etc.) all in lowercase.

<?php

// usual PHP convention
foreach($array as $element) {
    echo $element;
}

// unusual PHP conventions
Foreach($array AS $element) {
    eCHo $element;
}

?>

PHP do understand them in lowercase, UPPERCASE or WilDCase, so there is nothing compulsory here. Although, it will look strange to many.

Command Line Php/UpperCaseKeyword
Analyzers Coding Conventions

Nonce Creation

Mark the creation of nonce by Wordpress. Nonce may be created with the Wordpress functions wp_nonce_field(), wp_nonce_url() and wp_nonce_create().

<?php

// Create an nonce for a link.
$nonce = wp_create_nonce( 'my-nonce' );

echo '<a href="myplugin.php?do_something=some_action&_wpnonce='.$nonce.'">Do some action</a>';

?>

See also Wordpress Nonce.

Command Line Wordpress/NonceCreation
Analyzers Wordpress

Not Definitions Only

Files should only include definitions (class, functions, traits, interfaces, constants), or global instructions, but not both.

<?php
// This whole script is a file

// It contains definitions and global code
class foo {
    static public $foo = null;
}
//This can be a singleton creation
foo::$foo = new foo();

trait t {}

class bar {}

?>

Within this context, globals, use, and namespaces instructions are not considered a warning.

Command Line Files/NotDefinitionsOnly
Analyzers Analyze

Not Not

Double not makes a boolean, not a true.

This is a wrongly done casting to boolean. PHP supports (boolean) to do the same, faster and cleaner.

<?php
    // Wrong type casting
    $b = !!$x;

    // Explicit code
    $b = (boolean) $x;
?>
Command Line Structures/NotNot
clearPHP no-implied-cast
Analyzers Analyze

Null On New

Until PHP 7, some classes instantiation could yield null, instead of throwing an exception.

After issuing a ‘new’ with those classes, it was important to check if the returned object were null or not. No exception were thrown.

<?php

// Example extracted from the wiki below
$mf = new MessageFormatter('en_US', '{this was made intentionally incorrect}');
if ($mf === null) {
    echo 'Surprise!';
}

?>

This inconsistency has been cleaned in PHP 7 : see See Internal Constructor Behavior

See also PHP RFC: Constructor behaviour of internal classes.

Command Line Classes/NullOnNew
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Objects Don’t Need References

There is no need to create references for objects, as those are always passed by reference when used as arguments.

<?php

    $object = new stdClass();
    $object->name = 'a';

    foo($object);
    print $object->name; // Name is 'b'

    // No need to make $o a reference
    function foo(&$o) {
        $o->name = 'b';
    }

    $array = array($object);
    foreach($array as &$o) { // No need to make this a reference
        $o->name = 'c';
    }

?>

See also Passing by reference.

Command Line Structures/ObjectReferences
clearPHP no-references-on-objects
Analyzers Analyze

Old Style Constructor

PHP classes used to have the method bearing the same name as the class acts as the constructor. That was PHP 4, and early PHP 5.

The manual issues a warning about this syntax : ‘Old style constructors are DEPRECATED in PHP 7.0, and will be removed in a future version. You should always use ‘__construct() in new code.’

<?php

namespace {
    // Global namespace is important
    class foo {
        function foo() {
            // This acts as the old-style constructor, and is reported by PHP
        }
    }

    class bar {
        function '__construct() { }
        function bar() {
            // This doesn't act as constructor, as bar has a '__construct() method
        }
    }
}

namespace Foo\Bar{
    class foo {
        function foo() {
            // This doesn't act as constructor, as bar is not in the global namespace
        }
    }
}

?>

This is no more the case in PHP 5, which relies on ‘__construct() to do so. Having this old style constructor may bring in confusion, unless you are also supporting old time PHP 4.

Note that classes with methods bearing the class name, but inside a namespace are not following this convention, as this is not breaking backward compatibility. Those are excluded from the analyze.

See also Constructors and Destructors ¶.

Command Line Classes/OldStyleConstructor
clearPHP no-php4-class-syntax
Analyzers Analyze

Old Style __autoload()

Avoid __autoload(). Only use spl_register_autoload().

__autoload() will be deprecated in PHP 7.2 and possibly removed in later version.

__autoload() may only be declared once, and cannot be modified later. This creates potential conflicts between libraries that try to set up their own autoloading schema.

On the other hand, spl_register_autoload() allows registering and de-registering multiple autoloading functions or methods.

<?php

// Modern autoloading.
function myAutoload($class){}
spl_register_autoload('myAutoload');

// Old style autoloading.
function __autoload($class){}

?>

Do not use the old __autoload() function, but rather the new spl_register_autoload() function.

See also Autoloading Classe.

Command Line Php/oldAutoloadUsage
clearPHP use-smart-autoload
Analyzers Analyze

One Letter Functions

One letter functions seems to be really short for a meaningful name. This may happens for very high usage functions, so as to keep code short, but such functions should be rare.

<?php

// One letter functions are rarely meaningful
function f($a, $b) {
    return $a + $b;
}

?>
Command Line Functions/OneLetterFunctions
Analyzers Analyze

One Variable String

These strings only contains one variable or property or array.

<?php

$a = 0;
$b = $a; // This is a one-variable string

// Better way to write the above
$b = (string) $a;

// Alternatives :
$b2 = $a[1]; // This is a one-variable string
$b3 = $a->b; // This is a one-variable string
$c = d;
$d = D;
$b4 = "{$$c}";
$b5 = "{$a->foo()}";

?>

If the goal is to convert it to a string, use the type casting (string) operator : it is then clearer to understand the conversion. It is also marginally faster, though very little.

Command Line Type/OneVariableStrings
Analyzers Analyze

Only Variable Passed By Reference

When an argument is expected by reference, it is compulsory to provide a container. A container may be a variable, an array, a property or a static property.

This may be linted by PHP, when the function definition is in the same file as the function usage. This is silently linted if definition and usage are separated, if the call is dynamical or made as a method.

<?php

function foo(&$bar) { /'**/ }

foo(strtolower($string));

?>

This analysis currently covers functioncalls and static methodcalls, but omits methodcalls.

Command Line Functions/OnlyVariablePassedByReference
Analyzers Analyze

Only Variable Returned By Reference

Function can’t return literals by reference.

When a function returns a reference, it is only possible to return variables, properties or static properties.

Anything else, like literals or static expressions, yield a warning at execution time.

<?php

// Can't return a literal number
function &foo() {
    return 3 + 'rand();
}

// bar must return values that are stored in a
function &bar() {
    $a = 3 + 'rand();
    return $a;
}

?>
Command Line Structures/OnlyVariableReturnedByReference
Analyzers Analyze

Or Die

Classic old style failed error management.

<?php

// In case the connexion fails, this kills the current script
mysql_connect('localhost', $user, $pass) or 'die();

?>

Interrupting a script will leave the application with a blank page, will make your life miserable for testing. Just don’t do that.

Command Line Structures/OrDie
clearPHP no-implied-if
Analyzers Analyze

Order Of Declaration

The order used to declare members and methods has a great impact on readability and maintenance. However, practices varies greatly. As usual, being consistent is the most important and useful.

The suggested order is the following : traits, constants, properties, methods. Optional characteristics, like final, static... are not specified. Special methods names are not specified.

<?php

class x {
    use traits;

    const CONSTANTS = 1;
    const CONSTANTS2 = 1;
    const CONSTANTS3 = 1;

    private $property = 2;
    private $property2 = 2;
    private $property3 = 2;

    public function foo() {}
    public function foo2() {}
    public function foo3() {}
    public function foo4() {}
}

?>
Command Line Classes/OrderOfDeclaration
Analyzers Coding Conventions

Overwriting Variable

Replacing the content of a variable by something different is prone to errors. For example, it is not obvious if the $text variable is plain text or HTML text.

<?php

// Confusing
$text = htmlentities($text);

// Better
$textHTML = htmlentities($text);

?>

Besides, it is possible that the source is needed later, for extra processing.

Note that accumulators, like += .= or [] etc., that are meant to collect lots of values with consistent type are OK.

Command Line Variables/Overwriting
Analyzers Analyze

Overwritten Exceptions

In catch blocks, it is good practice not to overwrite the incoming exception, as information about the exception will be lost.

Command Line Exceptions/OverwriteException
Analyzers Analyze

Overwritten Literals

The same variable is assigned a literal twice. It is possible that one of the assignation is too much.

This analysis doesn’t take into account the distance between two assignations : it may report false positives when the variable is actually used for several purposes, and, as such, assigned twice with different values.

<?php

function foo() {
    // Two assignations in a short sequence : one is too many.
    $a = 1;
    $a = 2;

    for($i = 0; $i < 10; $i++) {
        $a += $i;
    }
    $b = $a;

    // New assignation. $a is now used as an array.
    $a = array(0);
}

?>
Command Line Variables/OverwrittenLiterals
Analyzers Analyze

PHP 7.0 New Classes

Those classes are now declared natively in PHP 7.0 and should not be declared in custom code.

Command Line Php/Php70NewClasses
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP71

PHP 7.0 New Interfaces

The following interfaces are introduced in PHP 7.0. They shouldn’t be defined in custom code.

Command Line Php/Php70NewInterfaces
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP71

PHP 7.0 Removed Directives

List of directives that are removed in PHP 7.0.

Command Line Php/Php70RemovedDirective
Analyzers CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

PHP 7.1 Microseconds

PHP 7.1 supports microseconds in DateTime class and date_create() function.

In previous PHP versions, those dates only used seconds, leading to lazy comparisons :

<?php

$now = date_create();
usleep(10);              // wait for 0.001 ms
var_dump($now == date_create());

?>

This code displays true in PHP 7.0 and older, (unless the code was run too close from the next second). In PHP 7.1, this is always false.

This is also true with Datetime :

<?php

$now = new DateTime();
usleep(10);              // wait for 0.001 ms
var_dump((new DateTime())->format('u') == $now->format('u'));

?>

This evolution impacts mostly exact comparisons (== and ===). Non-equality (!= and !==) will probably be always true, and should be reviewed.

Command Line Php/Php71microseconds
Analyzers CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

PHP 7.1 Removed Directives

List of directives that are removed in PHP 7.1.

Command Line Php/Php71RemovedDirective
Analyzers CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

PHP 7.2 Deprecations

PHP 7.2 deprecates several features of the language.

Deprecated functions and extensions are reported in a separate analysis.

Command Line Php/Php72Deprecation
Analyzers CompatibilityPHP72, CompatibilityPHP73

PHP 7.2 Object Keyword

Since PHP 7.2, ‘object’ is a keyword. It can’t be used for class, interface or trait name.

<?php

// Valid until PHP 7.2
class object {}

// Altough it is really weird anyway...

?>

See also List of Keywords.

Command Line Php/Php72ObjectKeyword
Analyzers CompatibilityPHP72, CompatibilityPHP73

PHP 7.2 Removed Functions

The following PHP native functions were removed in PHP 7.2.

  • png2wbmp
  • jpeg2wbmp
Command Line Php/Php72RemovedFunctions
Analyzers CompatibilityPHP72, CompatibilityPHP73

PHP 70 Removed Functions

The following PHP native functions were removed in PHP 7.0.

  • ereg
  • ereg_replace
  • eregi
  • eregi_replace
  • split
  • spliti
  • sql_regcase
  • magic_quotes_runtime
  • set_magic_quotes_runtime
  • call_user_method
  • call_user_method_array
  • set_socket_blocking
  • mcrypt_ecb
  • mcrypt_cbc
  • mcrypt_cfb
  • mcrypt_ofb
  • datefmt_set_timezone_id
  • imagepsbbox
  • imagepsencodefont
  • imagepsextendfont
  • imagepsfreefont
  • imagepsloadfont
  • imagepsslantfont
  • imagepstext
Command Line Php/Php70RemovedFunctions
Analyzers CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

PHP 72 Removed Classes

The following PHP native classes were removed in PHP 7.2.

  • SessionHandler
Command Line Php/Php72RemovedClasses
Analyzers CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

PHP 72 Removed Interfaces

The following PHP native interfaces were removed in PHP 7.2.

  • SessionHandlerInterface
  • SessionIdInterface
  • SessionUpdateTimestampHandlerInterface
Command Line Php/Php72RemovedInterfaces
Analyzers CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

PHP Keywords As Names

PHP has a set of reserved keywords. It is recommended not to use those keywords for names structures.

PHP does check that a number of structures, such as classes, methods, interfaces... can’t be named or called using one of the keywords. However, in a few other situations, no check are enforced. Using keywords in such situation is confusing.

<?php

// This keyword is reserved since PHP 7.2
class object {
    // _POST is used by PHP for the $_POST variable
    // This methods name is probably confusing,
    // and may attract more than its share of attention
    function _POST() {

    }

}

?>
Command Line Php/ReservedNames
Analyzers Analyze

PHP5 Indirect Variable Expression

Indirect variable expressions changes between PHP 5 an 7.

The following structures are evaluated differently in PHP 5 and 7. It is recommended to review them or switch to a less ambiguous syntax.

<?php

// PHP 7
$foo = 'bar';
$bar['bar']['baz'] = 'foobarbarbaz';
echo $$foo['bar']['baz'];
echo ($$foo)['bar']['baz'];

// PHP 5
$foo['bar']['baz'] = 'bar';
$bar = 'foobarbazbar';
echo $$foo['bar']['baz'];
echo ${$foo['bar']['baz']};

?>

See Backward incompatible changes PHP 7.0

Expression PHP 5 interpretation PHP 7 interpretation
$$foo[‘bar’][‘baz’] $foo->$bar[‘baz’] $foo->$bar[‘baz’]() Foo::$bar[‘baz’]() ${$foo[‘bar’][‘baz’]} $foo->{$bar[‘baz’]} $foo->{$bar[‘baz’]}() Foo::{$bar[‘baz’]}() ($$foo)[‘bar’][‘baz’] ($foo->$bar)[‘baz’] ($foo->$bar)[‘baz’]() (Foo::$bar)[‘baz’]()
Command Line Variables/Php5IndirectExpression
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

PHP7 Dirname

With PHP 7, dirname has a second argument that represents the number of parent folder to follow. This prevent us from using nested ‘dirname() calls to reach an grand-parent direct.

<?php
$path = '/a/b/c/d/e/f';

// PHP 7 syntax
$threeFoldersUp = dirname($path, 3);

// PHP 5 syntax
$threeFoldersUp = dirname(dirname(dirname($path)));

?>
Command Line Structures/PHP7Dirname
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Parent, Static Or Self Outside Class

Parent, static and self keywords must be used within a class or a trait. They make no sens outside a class or trait scope, as self and static refers to the current class and parent refers to one of parent above.

PHP 7.0 and later detect their usage at compile time, and emits a fatal error.

<?php

class x {
    const Y = 1;

    function foo() {
        // self is \x
        echo self::Y;
    }
}

const Z = 1;
// This doesn't compile anymore
echo self::Z;

?>

Static may be used in a function or a closure, but not globally.

Command Line Classes/PssWithoutClass
Analyzers Analyze

Parenthesis As Parameter

Using parenthesis around parameters used to silent some internal check. This is not the case anymore in PHP 7, and should be fixed by removing the parenthesis and making the value a real reference.

<?php

// PHP 7 sees through parenthesis
$d = foo(1, 2, $c);

// Avoid parenthesis in arguments
$d = foo(1, 2, ($c));

?>
Command Line Php/ParenthesisAsParameter
Analyzers CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Pathinfo() Returns May Vary

‘pathinfo() function returns an array whose content may vary. It is recommended to collect the values after check, rather than directly.

<?php

$file = '/a/b/.c';
//$extension may be missing, leading to empty $filename and filename in $extension
list( $dirname, $basename, $extension, $filename ) = array_values( pathinfo($file) );

//Use PHP 7.1 list() syntax to assign correctly the values, and skip 'array_values()
//This emits a warning in case of missing index
['dirname'   => $dirname,
 'basename'  => $basename,
 'extension' => $extension,
 'filename'  => $filename ] = pathinfo($file);

//This works without warning
$details = pathinfo($file);
$dirname   = $details['dirname'] ?? getpwd();
$basename  = $details['basename'] ?? '';
$extension = $details['extension'] ?? '';
$filename  = $details['filename'] ?? '';

?>

The same applies to ‘parse_url(), which returns an array with various index.

Command Line Php/PathinfoReturns
Analyzers Analyze

Performances/timeVsstrtotime

time() is actually faster than strtotime(‘now’).

<?php

// Faster version
$a = time();

// Slower version
$b = strtotime('now');

?>

This is a micro-optimisation. Gain is real, but small unless the function is used many times.

Command Line Performances/timeVsstrtotime
Analyzers Performances

Php 7 Indirect Expression

Those are variable indirect expressions that are interpreted differently in PHP 5 and PHP 7.

You should check them so they don’t behave strangely.

<?php

// Ambiguous expression :
$b = $$foo['bar']['baz'];
echo $b;

$foo = array('bar' => array('baz' => 'bat'));
$bat = 'PHP 5.6';

// In PHP 5, the expression above means :
$b = $\{$foo['bar']['baz']};
$b = 'PHP 5.6';

$foo = 'a';
$a = array('bar' => array('baz' => 'bat'));

// In PHP 7, the expression above means :
$b = ($$foo)['bar']['baz'];
$b = 'bat';

?>

See also Changes to variable handling.

Command Line Variables/Php7IndirectExpression
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70

Php 71 New Classes

New classes, introduced in PHP 7.1. If classes where created with the same name, in current code, they have to be moved in a namespace, or removed from code to migrate safely to PHP 7.1.

The new class is : ReflectionClassConstant. The other class is ‘Void’ : this is forbidden as a classname, as Void is used for return type hint.

<?php

class ReflectionClassConstant {
    // Move to a namespace, do not leave in global
    // or, remove this class
}

?>
Command Line Php/Php71NewClasses
Analyzers CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP71, CompatibilityPHP73

Php7 Relaxed Keyword

Most of the traditionnal PHP keywords may be used inside classes, trait or interfaces.

<?php

// Compatible with PHP 7.0 +
class foo {
    // as is a PHP 5 keyword
    public function as() {

    }
}

?>

This was not the case in PHP 5, and will yield parse errors.

Command Line Php/Php7RelaxedKeyword
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Phpinfo

‘phpinfo() is a great function to learn about the current configuration of the server.

<?php

if (DEBUG) {
    'phpinfo();
}

?>

If left in the production code, it may lead to a critical leak, as any attacker gaining access to this data will know a lot about the server configuration. It is advised to never leave that kind of instruction in a production code.

Command Line Structures/PhpinfoUsage
Analyzers Analyze

Pre-increment

When possible, use the pre-increment operator (++$i or –$i) instead of the post-increment operator ($i++ or $i–).

The latter needs an extra memory allocation that costs about 10% of performances.

<?php

// ++$i should be preferred over $i++, as current value is not important
for($i = 0; $i <10; ++$i) {
    // do Something
}

// ++$b and $b++ have different impact here, since $a will collect $b + 1 or $b, respectively.
$a = $b++;

?>

This is a micro-optimisation. However, its usage is so widespread, including within loops, that it may eventually be visible. As such, it is recommended to adopt this rule, and only consider changing legacy code as they are refactored for other reasons.

Command Line Performances/PrePostIncrement
Analyzers Analyze, Performances

Prepare Placeholder

$wpdb->prepare() only allows %d, %s and %F as placeholder. All others are not available. They are even enforced since Wordpress 4.8.3.

In particular, absolute references are not allowed anymore, due to an injection vulnerability.

<?php

// valid place holders
  $query = $wpdb->prepare('SELECT * FROM table WHERE col = %s and col2 = %1$s and col3 = %F', 'string', 1, 1.2);

// valid place holders : invalid Wordpress placeholder
// This may be a valid vsprintf placeholder.
  $query = $wpdb->prepare('SELECT * FROM table WHERE col = %b', $integerDisplayedAsBinary);

// valid place holders : absolute reference. $var is used twice
  $query = $wpdb->prepare('SELECT * FROM table WHERE col = %s and %1$s', $var);

?>

See also ‘vprintf() and Disclosure: WordPress WPDB SQL Injection - Technical.

Command Line Wordpress/PreparePlaceholder
Analyzers Wordpress

Preprocess Arrays

Using long list of assignations for initializing arrays is significantly slower than the declaring them as an array.

<?php

// Slow way
$a = []; // also with $a = array();
$a[1] = 2;
$a[2] = 3;
$a[3] = 5;
$a[4] = 7;
$a[5] = 11;

// Faster way
$a = [1 => 2,
      2 => 3,
      3 => 5,
      4 => 7,
      5 => 11];

// Even faster way if indexing is implicit
$a = [2, 3, 5, 7, 11];

?>

If the array has to be completed rather than created, it is also faster to use += when there are more than ten elements to add.

<?php

// Slow way
$a = []; // also with $a = array();
$a[1] = 2;
$a[2] = 3;
$a[3] = 5;
// some expressions to get $seven and $eleven
$a[4] = $seven;
$a[5] = $eleven;

// Faster way
$a = [1 => 2,
      2 => 3,
      3 => 5];
// some expressions to get $seven and $eleven
$a += [4 => $seven,
       5 => $eleven];

// Even faster way if indexing is implicit
$a = [2, 3, 5];
// some expressions to get $seven and $eleven
$a += [$seven, $eleven];

?>
Command Line Arrays/ShouldPreprocess
Analyzers none

Preprocessable

The following expression are made of literals or already known values : they may be fully calculated before running PHP.

<?php

// Building an array from a string
$name = 'PHP'.' '.'7.2';

// Building an array from a string
$list = explode(',', 'a,b,c,d,e,f');

// Calculating a power
$kbytes = $bytes / pow(2, 10);

// This will never change
$name = ucfirst(strtolower('PARIS'));

?>

By doing so, this will reduce the amount of work of PHP.

Command Line Structures/ShouldPreprocess
Analyzers Analyze, Analyze

Printf Number Of Arguments

The number of arguments provided to ‘printf() or ‘vprintf() doesn’t match the format string.

Extra arguments are ignored, and are dead code as such. Missing arguments are reported with a warning, and nothing is displayed.

<?php

// not enough
printf(' a %s ', $a1);
// OK
printf(' a %s ', $a1, $a2);
// too many
printf(' a %s ', $a1, $a2, $a3);

// not enough
sprintf(' a %s ', $a1);
// OK
\sprintf(' a %s ', $a1, $a2);
// too many
sprintf(' a %s ', $a1, $a2, $a3);

?>
Command Line Structures/PrintfArguments
Analyzers Analyze

Property Could Be Private Method

The following method are never used outside their class of definition. Given the analyzed code, they could be set as private.

<?php

class foo {
    public function couldBePrivate() {}
    public function cantdBePrivate() {}

    function bar() {
        // couldBePrivate is used internally.
        $this->couldBePrivate();
    }
}

class foo2 extends foo {
    function bar2() {
        // cantdBePrivate is used in a child class.
        $this->cantdBePrivate();
    }
}

//couldBePrivate() is not used outside
$foo = new foo();

//cantdBePrivate is used outside the class
$foo->cantdBePrivate();

?>

Note that dynamic properties (such as $x->$y) are not taken into account.

Command Line Classes/CouldBePrivateMethod
Analyzers Analyze

Property Could Be Private Property

The following properties are never used outside their class of definition Given the analyzed code, they could be set as private.

<?php

class foo {
    public $couldBePrivate = 1;
    public $cantdBePrivate = 1;

    function bar() {
        // couldBePrivate is used internally.
        $this->couldBePrivate = 3;
    }
}

class foo2 extends foo {
    function bar2() {
        // cantdBePrivate is used in a child class.
        $this->cantdBePrivate = 3;
    }
}

//$couldBePrivate is not used outside
$foo = new foo();

//$cantdBePrivate is used outside the class
$foo->cantdBePrivate = 2;

?>

Note that dynamic properties (such as $x->$y) are not taken into account.

Command Line Classes/CouldBePrivate
Analyzers Analyze

Property Used In One Method Only

Properties should be used in several methods. When a property is used in only one method, this should have be of another shape.

Properties used in one method only may be used several times, and read only. This may be a class constant. Such properties are meant to be overwritten by an extending class, and that’s possible with class constants.

Properties that read and written may be converted into a variable, static to the method. This way, they are kept close to the method, and do not pollute the object’s properties.

<?php

class foo {
    private $once = 1;
    const ONCE = 1;
    private $counter = 0;

    function bar() {
        // $this->once is never used anywhere else.
        someFunction($this->once);
        someFunction(self::ONCE);   // Make clear that it is a
    }

    function bar2() {
        static $localCounter = 0;
        $this->counter++;

        // $this->once is only used here, for distinguising calls to someFunction2
        if ($this->counter > 10) { // $this->counter is used only in bar2, but it may be used several times
            return false;
        }
        someFunction2($this->counter);

        // $localCounter keeps track for all the calls
        if ($localCounter > 10) {
            return false;
        }
        someFunction2($localCounter);
    }
}

?>

Note : properties used only once are not returned by this analysis. They are omitted, and are available in the analysis Used Once Property.

Command Line Classes/PropertyUsedInOneMethodOnly
Analyzers Analyze

Property Variable Confusion

Within a class, there is both a property and variables bearing the same name.

<?php
class Object {
    private $x;

    function SetData( ) {
        $this->x = $x + 2;
    }
}
?>

The property and the variable may easily be confused one for another and lead to a bug.

Sometimes, when the property is going to be replaced by the incoming argument, or data based on that argument, this naming schema is made on purpose, indicating that the current argument will eventually end up in the property. When the argument has the same name as the property, no warning is reported.

Command Line Structures/PropertyVariableConfusion
Analyzers Analyze

Queries In Loops

Avoid querying databases in a loop.

Querying an external database in a loop usually leads to performances problems. This is also called the ‘n + 1 problem’.

This problem applies also to prepared statement : when such statement are called in a loop, they are slower than one-time large queries.

It is recommended to reduce the number of queries by making one query, and dispatching the results afterwards. This is true with SQL databases, graph queries, LDAP queries, etc.

<?php

// Typical N = 1 problem : there will be as many queries as there are elements in $array
$ids = array(1,2,3,5,6,10);

$db = new SQLite3('mysqlitedb.db');

// all the IDS are merged into the query at once
$results = $db->query('SELECT bar FROM foo WHERE id  in ('.implode(',', $id).')');
while ($row = $results->fetchArray()) {
    var_dump($row);
}


// Typical N = 1 problem : there will be as many queries as there are elements in $array
$ids = array(1,2,3,5,6,10);

$db = new SQLite3('mysqlitedb.db');

foreach($ids as $id) {
    $results = $db->query('SELECT bar FROM foo WHERE id = '.$id);
    while ($row = $results->fetchArray()) {
        var_dump($row);
    }
}

?>

This optimisation is not always possible : for example, some SQL queries may not be prepared, like ‘DROP TABLE’, or ‘DESC’. ‘UPDATE’ commands often update one row at a time, and grouping such queries may be counter-productive or unsafe.

Command Line Structures/QueriesInLoop
Analyzers Analyze

Random Without Try

random_int() and random_bytes() require a try/catch structure around them.

random_int() and random_bytes() emit Exceptions if they meet a problem. This way, failure can’t be mistaken with returning an empty value, which leads to lower security.

<?php

try {
    $salt = random_bytes($length);
} catch (TypeError $e) {
    // Error while reading the provided parameter
} catch (Exception $e) {
    // Insufficient random data generated
} catch (Error $e) {
    // Error with the provided parameter : <= 0
}

?>
Command Line Structures/RandomWithoutTry
Analyzers Security

Randomly Sorted Arrays

Those literals arrays are written in several places, but in various orders.

This may reduce the reading and proofing of the arrays, and induce confusion.

Unless order is important, it is recommended to always use the same order when defining literal arrays.

<?php

// an array
$set = [1,3,5,9,10];

function foo() {
    // an array, with the same values but different order, in a different context
    $list = [1,3,5,10,9,];
}

// an array, with the same order than the initial one
$inits = [1,3,5,9,10];

?>
Command Line Arrays/RandomlySortedLiterals
Analyzers Analyze

Redeclared PHP Functions

Function that bear the same name as a PHP function, and that are declared.

This is possible when managing some backward compatibility, like emulating an old function, or preparing for newer PHP version, like emulating new upcoming function.

<?php

if (version_compare(PHP_VERSION, 7.0) > 0) {
    function split($separator, $string) {
        return explode($separator, $string);
    }
}

print_r( split(' ', '2 3'));

?>
Command Line Functions/RedeclaredPhpFunction
Analyzers Analyze

Redefined Class Constants

Redefined class constants.

Class constants may be redefined, though it is prone to errors when using them, as it is now crucial to use the right class name to access the right value.

<?php

class a {
    const A = 1;
}

class b extends a {
    const A = 2;
}

class c extends c { }

echo a::A, ' ', b::A, ' ', c::A;
// 1 2 2

?>

It is recommended to use distinct names.

Command Line Classes/RedefinedConstants
Analyzers Analyze

Redefined Default

Classes allows properties to be set with a default value. When those properties get, unconditionally, another value at constructor time, then one of the default value are useless. One of those definition should go : it is better to define properties outside the constructor.

<?php

class foo {
    public $redefined = 1;

    public function '__construct( ) {
        $this->redefined = 2;
    }
}

?>
Command Line Classes/RedefinedDefault
Analyzers Analyze

Register Globals

register_globals was a PHP directive that dumped all incoming variables from GET, POST, COOKIE and FILES as global variables in the called scripts. This lead to security failures, as the variables were often used but not filtered.

Though it is less often found in more recent code, register_globals is sometimes needed in legacy code, that haven’t made the move to eradicate this style of coding. Backward compatible pieces of code that mimic the register_globals features usually create even greater security risks by being run after scripts startup. At that point, some important variables are already set, and may be overwritten by the incoming call, creating confusion in the script.

Mimicking register_globals is achieved with variables variables, ‘extract(), ‘parse_str() and ‘import_request_variables() (Up to PHP 5.4).

<?php

// Security warning ! This overwrites existing variables.
extract($_POST);

// Security warning ! This overwrites existing variables.
foreach($_REQUEST as $var => $value) {
    $$var = $value;
}

?>
Command Line Security/RegisterGlobals
Analyzers Security

Relay Function

Relay function only hand workload to another one.

Relay functions (or methods) are delegating the actual work to another function or method. They do not have any impact on the results, besides exposing another name for the same feature.

<?php

function myStrtolower($string) {
    return \strtolower($string);
}

?>

Relay functions are typical of transition API, where an old API have to be preserved until it is fully migrated. Then, they may be removed, so as to reduce confusion, and unclutter the API.

Command Line Functions/RelayFunction
Analyzers Analyze

Repeated Regex

Repeated regex should be centralized.

When a regex is repeatedly used in the code, it is getting harder to update.

<?php

// Regex used several times, at least twice.
preg_match('/^abc_|^square$/i', $_GET['x']);

//.......

preg_match('/^abc_|^square$/i', $row['name']);

// This regex is dynamically built, so it is not reported.
preg_match('/^circle|^'.$x.'$/i', $string);

// This regex is used once, so it is not reported.
preg_match('/^circle|^square$/i', $string);

?>

Regex that are repeated at least once (aka, used twice or more) are reported. Regex that are dynamically build are not reported.

Command Line Structures/RepeatedRegex
Analyzers Analyze

Repeated print()

Always merge several print or echo in one call.

It is recommended to use echo with multiple arguments, or a concatenation with print, instead of multiple calls to print echo, when outputting several blob of text.

<?php

//Write :
  echo 'a', $b, 'c';
  print 'a' . $b . 'c';

//Don't write :
  print 'a';
  print $b;
  print 'c';
?>
Command Line Structures/RepeatedPrint
clearPHP no-repeated-print
Analyzers Analyze

Reserved Keywords In PHP 7

Php reserved names for class/trait/interface. They won’t be available anymore in user space starting with PHP 7.

For example, string, float, false, true, null, resource,... are not acceptable as class name.

<?php

// This doesn't compile in PHP 7.0 and more recent
class null { }

?>

See also List of other reserved words.

Command Line Php/ReservedKeywords7
Analyzers CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Results May Be Missing

preg_match() may return empty values, if the search fails. It is important to check for the existence of results before assigning them to another variable, or using it.

<?php
    preg_match('/PHP ([0-9\.]+) /', $res, $r);
    $s = $r[1];
    // $s may end up null if preg_match fails.
?>
Command Line Structures/ResultMayBeMissing
Analyzers Analyze

Rethrown Exceptions

Throwing a caught exception is usually useless and dead code.

When exceptions are caught, they should be processed or transformed, but not rethrown as is.

Those issues often happen when a catch structure was positioned for debug purposes, but lost its usage later.

<?php

try {
    doSomething();
} catch (Exception $e) {
    throw $e;
}

?>
Command Line Exceptions/Rethrown
Analyzers Dead code

Return True False

These conditional expressions return true/false, depending on the condition. This may be simplified by dropping the control structure alltogether.

<?php

if (version_compare($a, $b) >= 0) {
    return true;
} else {
    return false;
}

?>

This may be simplified with :

<?php

return version_compare($a, $b) >= 0;

?>

This may be applied to assignations and ternary operators too.

<?php

if (version_compare($a, $b) >= 0) {
    $a = true;
} else {
    $a = false;
}

$a = version_compare($a, $b) >= 0 ? false : true;

?>
Command Line Structures/ReturnTrueFalse
Analyzers Analyze

Return With Parenthesis

PHP tolerates parenthesis for the argument of a return statement, but it is recommended not to use them.

Command Line Php/ReturnWithParenthesis
Analyzers Coding Conventions

Safe Curl Options

It is advised to always use CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST when requesting a SSL connexion.

With those tests (by default), the certificate is verified, and if it isn’t valided, the connexion fails : this is a safe behavior.

<?php
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, https://www.php.net/);

// To be safe, always set this to true
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);

curl_exec($ch);
curl_close($ch);
?>
Command Line Security/CurlOptions
Analyzers Security

Same Conditions

Several If then else structures are chained, and some conditions are identical. The latter will be ignored.

<?php

if ($a == 1) { doSomething(); }
elseif ($b == 1) { doSomething(); }
elseif ($c == 1) { doSomething(); }
elseif ($a == 1) { doSomething(); }
else {}

// Also works on if then else if chains
if ($a == 1) { doSomething(); }
else if ($b == 1) { doSomething(); }
else if ($c == 1) { doSomething(); }
else if ($a == 1) { doSomething(); }
else {}

?>
Command Line Structures/SameConditions
Analyzers Analyze

Scalar Or Object Property

Property shouldn’t use both object and scalar syntaxes. When a property may be an object, it is recommended to implement the Null Object pattern : instead of checking if the property is scalar, make it always object.

<?php

class x {
    public $display = 'echo';

    function foo($string) {
        if (is_string($this->display)) {
            echo $this->string;
        } elseif ($this->display 'instanceof myDisplayInterface) {
            $display->display();
        } else {
            print Error when displaying\n;
        }
    }
}

interface myDisplayInterface {
    public function display($string); // does the display in its own way
}

class nullDisplay implements myDisplayInterface {
    // implements myDisplayInterface but does nothing
    public function display($string) {}
}

class x2 {
    public $display = null;

    public function '__construct() {
        $this->display = new nullDisplay();
    }

    function foo($string) {
        // Keep the check, as $display is public, and may get wrong values
        if ($this->display 'instanceof myDisplayInterface) {
            $display->display();
        } else {
            print Error when displaying\n;
        }
    }
}

// Simple class for echo
class echoDisplay implements myDisplayInterface {
    // implements myDisplayInterface but does nothing
    public function display($string) {
        echo $string;
    }
}

?>

See also Null Object Pattern. and The Null Object Pattern.

Command Line Classes/ScalarOrObjectProperty
Analyzers Analyze

Scalar Typehint Usage

Spot usage of scalar type hint : int, float, boolean and string.

Scalar typehint are PHP 7.0 and more recent. Some, like object, is 7.2.

Scalar typehint were not supported in PHP 5 and older. Then, the typehint is treated as a classname.

<?php

function withScalarTypehint(string $x) {}

function withoutScalarTypehint(someClass $x) {}

?>

See also PHP RFC: Scalar Type Hints and Type declarations.

Command Line Php/ScalarTypehintUsage
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Security/SessionLazyWrite

Classes that implements SessionHandlerInterface must also implements SessionUpdateTimestampHandlerInterface.

The two extra methods are used to help lazy loading : the first actually checks if a sessionId is available, and the seconds updates the time of last usage of the session data in the session storage.

This was spotted by Nicolas Grekas, and fixed in Symfony [HttpFoundation] Make sessions secure and lazy #24523.

<?php

interface SessionUpdateTimestampHandlerInterface {
    // returns a boolean to indicate that valid data is available for this sessionId, or not.
    function validateId($sessionId);

    //called to change the last time of usage for the session data.
    //It may be a file's touch or full write, or a simple update on the database
    function updateTimestamp($sessionId, $sessionData);
}

?>

See also ` <https://wiki.php.net/rfc/session-read_only-lazy_write>`_ and the Sessions.

Command Line Security/SessionLazyWrite
Analyzers Security

Sequences In For

For() instructions allows several instructions in each of its parameters. Then, the instruction separator is comma ‘,’, not semi-colon, which is used for separating the 3 arguments.

<?php
   for ($a = 0, $b = 0; $a < 10, $b < 20; $a++, $b += 3) {
    // For loop
   }
?>

This loop will simultaneously increment $a and $b. It will stop only when the last of the central sequence reach a value of false : here, when $b reach 20 and $a will be 6.

This structure is often unknown, and makes the for instruction quite difficult to read. It is also easy to oversee the multiples instructions, and omit one of them. It is recommended not to use it.

Command Line Structures/SequenceInFor
Analyzers Analyze

Setlocale() Uses Constants

setlocal() don’t use strings.

The first argument of ‘setlocale() must be one of the valid constants, LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES.

<?php

// Use constantes for setlocale first argument
setlocale(LC_ALL, 'nl_NL');
setlocale(\LC_ALL, 'nl_NL');

// Don't use string for setlocale first argument
setlocale('LC_ALL', 'nl_NL');
setlocale('LC_'.'ALL', 'nl_NL');

?>

The PHP 5 usage of strings (same name as above, enclosed in ‘ or ”) is not legit anymore in PHP 7 and later.

Command Line Structures/SetlocaleNeedsConstants
Analyzers CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Several Instructions On The Same Line

Usually, instructions do not share their line : one instruction, one line.

This is good for readability, and help at understanding the code. This is especially important when fast-reading the code to find some special situation, where such double-meaning line way have an impact.

<?php

switch ($x) {
    // Is it a fallthrough or not ?
    case 1:
        doSomething(); 'break;

    // Easily spotted 'break.
    case 1:
        doSomethingElse();
        'break;

    default :
        doDefault();
        'break;
}

?>

See also Object Calisthenics.

Command Line Structures/OneLineTwoInstructions
Analyzers Analyze

Short Open Tags

Usage of short open tags is discouraged. The following files were found to be impacted by the short open tag directive at compilation time. They must be reviewed to ensure no &lt;? tags are found in the code.

Command Line Php/ShortOpenTagRequired
Analyzers Analyze

Short Syntax For Arrays

Arrays written with the new PHP 5.4 short syntax.

PHP 5.4 introduced the new short syntax, with square brackets. The previous syntax, based on the array() keyword is still available.

<?php

// All PHP versions array
$a = array(1, 2, 3);

// PHP 5.4+ arrays
$a = [1, 2, 3];

?>

See also Array.

Command Line Arrays/ArrayNSUsage
Analyzers CompatibilityPHP53

Should Be Single Quote

Use single quote for simple strings.

Static content inside a string, that has no single quotes nor escape sequence (such as n or t), should be using single quote delimiter, instead of double quote.

<?php

$a = abc;

// This one is using a special sequence
$b = cde\n;

// This one is using two special sequences
$b = \x03\u{1F418};

?>

If you have too many of them, don’t loose your time switching them all. If you have a few of them, it may be good for consistence.

Command Line Type/ShouldBeSingleQuote
clearPHP no-double-quote
Analyzers Coding Conventions

Should Chain Exception

Chain exception to provide more context.

When catching an exception and rethrowing another one, it is recommended to chain the exception : this means providing the original exception, so that the final recipiend has a chance to track the origin of the problem. This doesn’t change the thrown message, but provides more information.

Note : Chaining requires PHP > 5.3.0.

<?php
    try {
        throw new Exception('Exception 1', 1);
    } catch (\Exception $e) {
        throw new Exception('Exception 2', 2, $e);
        // Chaining here.

    }
?>
Command Line Structures/ShouldChainException
Analyzers Analyze

Should Make Alias

Long names should be aliased.

Aliased names are easy to read at the beginning of the script; they may be changed at one point, and update the whole code at the same time. Finally, short names makes the rest of the code readable.

<?php

namespace x\y\z;

use a\b\c\d\e\f\g as Object;

// long name, difficult to read, prone to change.
new a\b\c\d\e\f\g();

// long name, difficult to read, prone to silent dead code if namespace change.
if ($o 'instanceof a\b\c\d\e\f\g) {

}

// short names Easy to update all at once.
new Object();
if ($o 'instanceof Object) {

}

?>
Command Line Namespaces/ShouldMakeAlias
Analyzers Analyze, ZendFramework

Should Make Ternary

Ternary operators are the best when assigning values to a variable.

This way, they are less verbose, compatible with assignation and easier to read.

<?php
    // verbose if then structure
    if ($a == 3) {
        $b = 2;
    } else {
        $b = 3;
    }

    // compact ternary call
    $b = ($a == 3) ? 2 : 3;

    // verbose if then structure
    // Works with short assignations and simple expressions
    if ($a != 3) {
        $b += 2 - $a * 4;
    } else {
        $b += 3;
    }

    // compact ternary call
    $b += ($a != 3) ? 2 - $a * 4 : 3;

?>
Command Line Structures/ShouldMakeTernary
Analyzers Analyze

Should Regenerate Session Id

No mention of ZendSession::regenerateId() method found.

When using ZendSession, or PHP session, a session ID is assigned to the user. It is a random number, used to connect the user and its data on the server. Actually, anyone with the session ID may have access to the data. This is why those session ID are so long and complex.

A good approach to protect the session ID is to reduce its lifespan : the shorter the time of use, the better. While changing the session ID at every hit on the page may no be possible, a more reasonable approach is to change the session id when an important action is about to take place. What important means is left to the application to decide.

Based on this philopsophy, a code source that uses ZendSession but never uses ZendSession::regenerateId() has to be updated.

<?php

    //Getting the session manager from the application
   $session = $e->getApplication()
                ->getServiceManager()
                ->get('Zend\Session\SessionManager');

?>

See Zend Session, ZendSessionSessionManager

Command Line ZendF/ShouldRegenerateSessionId
Analyzers ZendFramework

Should Typecast

When typecasting, it is better to use the casting operator, such as (int) or (bool).

Functions such as ‘intval() or ‘settype() are always slower.

<?php

$int = intval($_GET['x']);

// Quicker version
$int = (int) $_GET['x'];

?>

This is a micro-optimisation, although such conversion may be use multiple time, leading to a larger performance increase.

Command Line Type/ShouldTypecast
Analyzers Analyze

Should Use Coalesce

PHP 7 introduced the ?? operator, that replaces longer structures to set default values when a variable is not set.

<?php

// Fetches the request parameter user and results in 'nobody' if it doesn't exist
$username = $_GET['user'] ?? 'nobody';
// equivalent to: $username = 'isset($_GET['user']) ? $_GET['user'] : 'nobody';

// Calls a hypothetical model-getting function, and uses the provided default if it fails
$model = Model::get($id) ?? $default_model;
// equivalent to: if (($model = Model::get($id)) === NULL) { $model = $default_model; }

?>

Sample extracted from PHP docs Isset Ternary

Command Line Php/ShouldUseCoalesce
Analyzers Analyze

Should Use Constants

The following functions have related constants that should be used as arguments, instead of scalar literals, such as integers or strings.

For example, $lines = file(‘file.txt’, 2); is less readable than $lines = file(‘file.txt’, FILE_IGNORE_NEW_LINES)

Command Line Functions/ShouldUseConstants
Analyzers Analyze

Should Use Foreach

Use foreach instead of for when traversing an array.

Foreach() is the modern loop : it maps automatically every element of the array to a blind variable, and loop over it. This is faster and safer.

<?php

// Foreach version
foreach($array as $element) {
    doSomething($element);
}

// The above case may even be upgraded with array_map and a callback,
// for the simplest one of them
$array = array_map('doSomething', $array);

// For version (one of various alternatives)
for($i = 0; $i < count($array); $i++) {
    $element = $array[$i];
    doSomething($element);
}

?>
Command Line Structures/ShouldUseForeach
Analyzers Analyze

Should Use Function Use

Functioncalls that fall back to global scope should be using ‘use function’ or be fully namespaced.

PHP searches for functions in the local namespaces, and in case it fails, makes the same search in the global scope. Anytime a native function is referenced this way, the search (and fail) happens. This slows down the scripts.

The speed bump range from 2 to 8 %, depending on the availability of functions in the local scope. The overall bump is about 1 µs per functioncall, which makes it a micro optimisation until a lot of function calls are made.

Based on one of Marco Pivetta tweet and this blog post

<?php

namespace X {
    use function strtolower as strtolower_aliased;

    // PHP searches for strtolower in X, fails, then falls back to global scope, succeeds.
    $a = strtolower($b);

    // PHP searches for strtolower in global scope, succeeds.
    $a = \strtolower($b);

    // PHP searches for strtolower_aliased in global scope, succeeds.
    $a = \strtolower_aliased($b);
}

?>
Command Line Php/ShouldUseFunction
Analyzers Performances

Should Use Local Class

Methods in a class should use the class, or be functions.

Methods should use $this with another method or a property, or call parent::. Static methods should call another static method, or a static property. Methods which are overwritten by a child class are omitted : the parent class act as a default value for the children class, and this is correct.

<?php

class foo {
    public function '__construct() {
        // This method should do something locally, or be removed.
    }
}

class bar extends foo {
    private $a = 1;

    public function '__construct() {
        // Calling parent:: is sufficient
        parent::'__construct();
    }

    public function barbar() {
        // This is acting on the local object
        $this->a++;
    }

    public function barfoo($b) {
        // This has no action on the local object. It could be a function or a closure where needed
        return 3 + $b;
    }
}

?>

Note that a method using a class constant is not considered as using the local class, for this analyzer.

Command Line Classes/ShouldUseThis
clearPHP not-a-method
Analyzers Analyze

Should Use Prepared Statement

Modern databases provides support for prepared statement : it separates the query from the processed data and highten significantly the security.

Building queries with concatenations is not recommended, though not always avoidable. When possible, use prepared statements.

Command Line Security/ShouldUsePreparedStatement
Analyzers Analyze, Security

Should Use SetCookie()

Use ‘setcookie() or ‘setrawcookie(). Avoid using ‘header() to do so, as the PHP native functions are more convenient and easier to spot during a refactoring.

‘setcookie() applies some encoding internally, for the value of the cookie and the date of expiration. Rarely, this encoding has to be skipped : then, use setrawencoding().

Both functions help by giving a checklist of important attributes to be used with the cookie.

<?php

// same as below
setcookie(myCookie, 'chocolate', time()+3600, /, , true, true);

// same as above. Slots for path and domain are omitted, but should be used whenever possible
header('Set-Cookie: myCookie=chocolate; Expires='.date('r', (time()+3600)).'; Secure; HttpOnly');

?>

See also : Set-Cookie.

Command Line Php/UseSetCookie
Analyzers Analyze

Should Use array_column()

Should use ‘array_column().

‘array_column() is a native PHP function, that extract a property or a index from a array of object, or a multidimensional array. This prevents the usage of foreach to collect those values.

<?php

$a = array(array('b' => 1),
           array('b' => 2, 'c' => 3),
           array(          'c' => 4)); // b doesn't always exists

$bColumn = array_column($a, 'b');

// Slow and cumbersome code
$bColumn = array();
foreach($a as $k => $v) {
    if ('isset($v['b'])) {
        $bColumn[] = $v['b'];
    }
}

?>

‘array_column() is faster than ‘foreach() (with or without the ‘isset() test) with 3 elements or more, and it is significantly faster beyond 5 elements. Memory consumption is the same.

See also [blog] `’array_column() <https://benramsey.com/projects/array-column/>`_.

Command Line Php/ShouldUseArrayColumn
Analyzers Analyze, Performances

Should Use session_regenerateid()

session_regenerateid() should be used when sessions are used.

When using sessions, a session ID is assigned to the user. It is a random number, used to connect the user and its data on the server. Actually, anyone with the session ID may have access to the data. This is why those session ID are so long and complex.

A good approach to protect the session ID is to reduce its lifespan : the shorter the time of use, the better. While changing the session ID at every hit on the page may no be possible, a more reasonable approach is to change the session id when an important action is about to take place. What important means is left to the application to decide.

Based on this philopsophy, a code source that uses ZendSession but never uses ZendSession::regenerateId() has to be updated.

<?php

    session_start();

    $id = (int) $_SESSION['id'];
    // no usage of session_regenerateid() anywhere triggers the analysis

    // basic regeneration every 20 hits on the page.
    if (++$_SESSION['count'] > 20) {
        session_regenerateid();
    }

?>

See session_regenerateid() and PHP Security Guide: Sessions.

Command Line Security/ShouldUseSessionRegenerateId
Analyzers Security

Silently Cast Integer

Those are integer literals that are cast to a float when running PHP. They are simply too big for the current PHP version, and PHP resorts to cast them into a float, which has a much larger capacity but a lower precision.

Compare your literals to PHP_MAX_INT (typically 9223372036854775807) and PHP_MIN_INT (typically -9223372036854775808). This applies to binary (0b10101...), octals (0123123...) and hexadecimals (0xfffff...) too.

<?php

echo 0b1010101101010110101011010101011010101011010101011010101011010111;
//6173123008118052203
echo 0b10101011010101101010110101010110101010110101010110101010110101111;
//1.2346246016236E+19

echo 0123123123123123123123;
//1498121094048818771
echo 01231231231231231231231;
//1.1984968752391E+19

echo 0x12309812311230;
//5119979279159856
echo 0x12309812311230fed;
//2.0971435127439E+19

echo 9223372036854775807; //PHP_MAX_INT
//9223372036854775807
echo 9223372036854775808;
9.2233720368548E+18

?>
Command Line Type/SilentlyCastInteger
Analyzers Analyze

Simple Global Variable

The global keyword should only be used with simple variables. Since PHP 7, it cannot be used with complex or dynamic structures.

<?php

// Forbidden in PHP 7
global $normalGlobal;

// Forbidden in PHP 7
global $$variable->global ;

// Tolerated in PHP 7
global ${$variable->global};

?>
Command Line Php/GlobalWithoutSimpleVariable
Analyzers CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73

Simple Switch

Switches are faster when relying only on integers or strings.

Since PHP 7.2, simple switches that use only strings or integers are optimized. The gain is as great as the switch is big.

<?php

// Optimized switch.
switch($b) {
    case a:
        'break;
    case b:
        'break;
    case c:
        'break;
    case d:
        'break;
    default :
        'break;
}

// Unoptimized switch.
// Try moving the foo() call in the default, to keep the rest of the switch optimized.
switch($c) {
    case a:
        'break;
    case foo($b):
        'break;
    case c:
        'break;
    case d:
        'break;
    default :
        'break;
}

?>

See also PHP 7.2’s switch optimisations.

Command Line Performances/SimpleSwitch
Analyzers Performances

Simplify Regex

PRCE regex are a powerful way to search inside strings, but they also come at the price of performance. When the query is simple enough, try using ‘strpos() or ‘stripos() instead.

<?php

// simple preg calls
if (preg_match('/a/', $string))  {}
if (preg_match('/b/i', $string)) {} // case insensitive

// light replacements
if( strpos('a', $string)) {}
if( stripos('b', $string)) {}       // case insensitive

?>
Command Line Structures/SimplePreg
Analyzers Performances

SlimPHP 1.0.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 1.0.0 of SlimPHP.

SlimPHP 1.0.0 has 22 classes, no traits and no interfaces;

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp10
Analyzers Slim

SlimPHP 1.1.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 1.1.0 of SlimPHP.

SlimPHP 1.1.0 has 33 classes, no traits and no interfaces;

11 new classes.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp11
Analyzers Slim

SlimPHP 1.2.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 1.2.0 of SlimPHP.

SlimPHP 1.2.0 has 35 classes, no traits and no interfaces;

14 new classes. 12 removed classes.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp12
Analyzers Slim

SlimPHP 1.3.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 1.3.0 of SlimPHP.

SlimPHP 1.3.0 has 33 classes, no traits and no interfaces;

4 new classes. 6 removed classes.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp13
Analyzers Slim

SlimPHP 1.5.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 1.5.0 of SlimPHP.

SlimPHP 1.5.0 has 33 classes, no traits and no interfaces;

1 new class. 1 removed class.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp15
Analyzers Slim

SlimPHP 1.6.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 1.6.0 of SlimPHP.

SlimPHP 1.6.0 has 45 classes, no traits and no interfaces;

25 new classes. 13 removed classes.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp16
Analyzers Slim

SlimPHP 2.0.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 2.0.0 of SlimPHP.

SlimPHP 2.0.0 has 44 classes, no traits and no interfaces;

21 new classes. 22 removed classes.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp20
Analyzers Slim

SlimPHP 2.1.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 2.1.0 of SlimPHP.

SlimPHP 2.1.0 has 44 classes, no traits and no interfaces;

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp21
Analyzers Slim

SlimPHP 2.2.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 2.2.0 of SlimPHP.

SlimPHP 2.2.0 has 45 classes, no traits and no interfaces;

2 new classes. 1 removed class. See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp22
Analyzers Slim

SlimPHP 2.3.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 2.3.0 of SlimPHP.

SlimPHP 2.3.0 has 48 classes, no traits and no interfaces;

5 new classes. 2 removed classes.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp23
Analyzers Slim

SlimPHP 2.4.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 2.4.0 of SlimPHP.

SlimPHP 2.4.0 has 48 classes, no traits and no interfaces;

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp24
Analyzers Slim

SlimPHP 2.5.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 2.5.0 of SlimPHP.

SlimPHP 2.5.0 has 50 classes, no traits and no interfaces;

2 new classes.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp25
Analyzers Slim

SlimPHP 2.6.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 2.6.0 of SlimPHP.

SlimPHP 2.6.0 has 50 classes, no traits and no interfaces;

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp26
Analyzers Slim

SlimPHP 3.0.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 3.0.0 of SlimPHP.

SlimPHP 3.0.0 has 55 classes, 2 traits and 9 interfaces;

49 new classes, 9 new interfaces, 2 new traits. 44 removed classes.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp30
Analyzers Slim

SlimPHP 3.1.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 3.1.0 of SlimPHP.

SlimPHP 3.1.0 has 55 classes, 2 traits and 9 interfaces;

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp31
Analyzers Slim

SlimPHP 3.2.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 3.2.0 of SlimPHP.

SlimPHP 3.2.0 has 59 classes, 2 traits and 9 interfaces;

4 new classes.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp32
Analyzers Slim

SlimPHP 3.3.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 3.3.0 of SlimPHP.

SlimPHP 3.3.0 has 60 classes, 2 traits and 9 interfaces;

1 new classe.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp33
Analyzers Slim

SlimPHP 3.4.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 3.4.0 of SlimPHP.

SlimPHP 3.4.0 has 64 classes, 2 traits and 9 interfaces;

4 new classes.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp34
Analyzers Slim

SlimPHP 3.5.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 3.5.0 of SlimPHP.

SlimPHP 3.5.0 has 67 classes, 2 traits and 9 interfaces;

3 new classes.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp35
Analyzers Slim

SlimPHP 3.6.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 3.6.0 of SlimPHP.

SlimPHP 3.6.0 has 67 classes, 2 traits and 9 interfaces;

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp36
Analyzers Slim

SlimPHP 3.7.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 3.7.0 of SlimPHP.

SlimPHP 3.7.0 has 67 classes, 2 traits and 9 interfaces;

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp37
Analyzers Slim

SlimPHP 3.8.0 Undefined Classes

SlimPHP classes, interfaces and traits that are not defined in version 3.8.0 of SlimPHP.

SlimPHP 3.8.0 has 68 classes, 2 traits and 9 interfaces;

1 new classe.

See also : SlimPHP and SlimPHP/slim.

Command Line Slim/Slimphp38
Analyzers Slim

Slow Functions

Avoid using those slow native PHP functions, and replace them with alternatives.

<?php

$array = source();

// Slow extraction of distinct values
$array = array_unique($array);

// Much faster extraction of distinct values
$array = array_keys(array_count_values($array));

?>
Slow Function Faster
‘array_diff() ‘array_intersect() ‘array_key_exists() ‘array_map() ‘array_search() ‘array_udiff() ‘array_uintersect() ‘array_unshift() ‘array_walk() ‘in_array() ‘preg_replace() ‘strstr() ‘uasort() ‘uksort() ‘usort() ‘array_unique() ‘foreach() ‘foreach() ‘isset() ‘foreach() ‘array_flip() and ‘isset() Use another way Use another way Use another way ‘foreach() ‘isset() ‘strpos() ‘strpos() Use another way Use another way Use another way ‘array_keys() and ‘array_count_values()

‘array_unique() has been accelerated in PHP 7.2 and may be used directly.

Command Line Performances/SlowFunctions
clearPHP avoid-those-slow-functions
Analyzers Performances

Static Loop

Static loop may be preprocessed.

It looks like the following loops are static : the same code is executed each time, without taking into account loop variables.

<?php

// Static loop
$total = 0;
for($i = 0; $i < 10; $i++) {
    $total += $i;
}

// Non-Static loop (the loop depends on the size of the array)
$n = count($array);
for($i = 0; $i < $n; $i++) {
    $total += $i;
}

?>

It is possible to create loops that don’t use any blind variables, though this is fairly rare.

Command Line Structures/StaticLoop
Analyzers Analyze

Static Methods Called From Object

Static methods may be called without instantiating an object. As such, they never interact with the special variable ‘$this’, as they do not depend on object existence.

Besides this, static methods are normal methods that may be called directly from object context, to perform some utility task.

To maintain code readability, it is recommended to call static method in a static way, rather than within object context.

<?php
    class x {
        static function y( ) {}
    }

    $z = new x( );

    $z->y( ); // Readability : no one knows it is a static call
    x::y( );  // Readability : here we know
?>
Command Line Classes/StaticMethodsCalledFromObject
Analyzers Analyze

Static Methods Can’t Contain $this

Static methods are also called ‘class methods’ : they may be called even if the class has no instantiated object. Thus, the local variable $this won’t exist, PHP will set it to NULL as usual.

<?php

class foo {
    // Static method may access other static methods, or property, or none.
    static function staticBar() {
        // This is not possible in a static method
        return self::otherStaticBar() . static::$staticProperty;
    }

    static function bar() {
        // This is not possible in a static method
        return $this->property;
    }

}

?>

Either, this is not a static method (simply remove the static keyword), or replace all $this mention by static properties Class::$property.

Command Line Classes/StaticContainsThis
clearPHP no-static-this
Analyzers Analyze

Strange Name For Constants

Those constants looks like a typo from other names.

<?php

// This code looks OK : DIRECTORY_SEPARATOR is a native PHP constant
$path = $path . DIRECTORY_SEPARATOR . $file;

// Strange name DIRECOTRY_SEPARATOR
$path = $path . DIRECOTRY_SEPARATOR . $file;

?>
Command Line Constants/StrangeName
Analyzers none

Strange Name For Variables

Those variables looks like a typo from other names.

<?php

class foo {
    function bar() {
        // Strange name $tihs
        return $tihs;
    }
}

?>
Command Line Variables/StrangeName
Analyzers Wordpress, Analyze, Analyze

Strange Names For Methods

Those methods should have another name.

Ever wondered why the ‘__constructor’ is never called? Or the ‘__consturct’ ?

Those errors most often originate from typos, or quick fixes that ‘don’t require testing’. Some other times, they were badly chosen, or ran into PHP’s own reservations.

<?php

class foo {
    // The real constructor
    function '__construct() {}

    // The fake constructor
    function __constructor() {}

    // The 'typo'ed' constructor
    function __consturct() {}

    // This doesn't clone
    function clone() {}
}

?>
Command Line Classes/StrangeName
Analyzers none

Strict Comparison With Booleans

Booleans may be easily mistaken with other values, especially when the function may return integer or boolean as a normal course of action.

It is encouraged to use strict comparison === or !== when booleans are involved in a comparison.

Command Line Structures/BooleanStrictComparison
Analyzers Analyze

String May Hold A Variable

Those strings looks like holding a variable.

Single quotes and Nowdoc syntax may include $ signs that are treated as literals, and not replaced with a variable value.

However, there are some potential variables in those strings, making it possible for an error : the variable was forgotten and will be published as such. It is worth checking the content and make sure those strings are not variables.

<?php

$a = 2;

// Explicit variable, but literal effect is needed
echo '$a is '.$a;

// One of the variable has been forgotten
echo '$a is $a';

// $CAD is not a variable, rather a currency unit
$total = 12;
echo $total.' $CAD';

// $CAD is not a variable, rather a currency unit
$total = 12;

// Here, $total has been forgotten
echo <<<'TEXT'
$total $CAD
TEXT;

?>
Command Line Type/StringHoldAVariable
Analyzers Analyze

Strings With Strange Space

An invisible space may be mistaken for a normal space.

However, PHP does straight comparisons, and may fail at recognizing. This analysis reports when it finds such strange spaces inside strings.

PHP doesn’t mistake space and tables for whitespace when tokenizing the code.

This analysis doesn’t report Unicode Codepoint Notation : those are clearly visible in the code.

<?php

// PHP 7 notation,
$a = \u{3000};
$b = ;

// Displays false
var_dump($a === $b);

?>

See also Unicode spaces, and disallow irregular whitespace (no-irregular-whitespace).

Command Line Type/StringWithStrangeSpace
Analyzers Analyze

Strpos Comparison

‘strpos(), and several PHP native functions, returns a string position, starting at 0, or false, in case of failure.

<?php

// This is the best comparison
if (strpos($string, 'a') === false) { }

// This is OK, as 2 won't be mistaken with false
if (strpos($string, 'a') == 2) { }

// strpos is one of the 26 functions that may behave this way
if (preg_match($regex, $string)) { }

// This works like above, catching the value for later reuse
if ($a = strpos($string, 'a')) { }

// This misses the case where 'a' is the first char of the string
if (strpos($string, 'a')) { }

// This misses the case where 'a' is the first char of the string, just like above
if (strpos($string, 'a') == 0) { }

?>

It is recommended to check the reslt of strpos with === or !==, so as to avoid confusing 0 and false.

This analyzer list all the ‘strpos()-like functions that are directly compared with == or !=. preg_match(), when its first argument is a literal, is omitted : this function only returns NULL in case of regex error.

Command Line Structures/StrposCompare
clearPHP strict-comparisons
Analyzers Analyze

Substr First

Always start by reducing a string before applying some transformation on it. The shorter string will be processed faster.

<?php

// fast version
$result = strtolower(substr($string, $offset, $length));

// slower version
$result = substr(strtolower($string), $offset, $length);
?>

The gain produced here is greater with longer strings, or greater reductions. They may also be used in loops. This is a micro-optimisation when used on short strings and single string reductions.

Command Line Performances/SubstrFirst
Analyzers Performances

Suspicious Comparison

The comparison seems to be misplaced.

A comparison happens in the the last argument, while the actual function expect another type : this may be the case of a badly placed parenthesis.

<?php

// trim expect a string, a boolean is given.
if (trim($str === '')){

}

// Just move the first closing parenthesis to give back its actual meaning
if (trim($str === '')){

}

?>

Original idea by Vladimir Reznichenko.

Command Line Structures/SuspiciousComparison
Analyzers Analyze

Switch Fallthrough

A switch with fallthrough is prone to errors.

A fallthrough happens when a case or default clause in a switch statement is not finished by a ‘break (or equivalent); CWE report this as a security concern, unless well documented.

A fallthrough may be used as a feature. It is undistinguisable from an error.

<?php
switch($variable) {
    case 1 :

        'break ;
    case 2 :
        'break ;
    default:
        ++$a;
    case 4 :
        'break ;
}
?>

This analysis cannot take into account comments abouts the fallthough.

See also CWE-484: Omitted Break Statement in Switch.

Command Line Structures/Fallthrough
Analyzers Security

Switch To Switch

The following structures are based on if / elseif / else. Since they have more than three conditions (not withstanding the final else), it is recommended to use the switch structure, so as to make this more readable.

On the other hand, ‘switch() structures will less than 3 elements should be expressed as a if / else structure.

Note that if condition that uses strict typing (=== or !==) can’t be converted to ‘switch() as the latter only performs == or != comparisons.

<?php

if ($a == 1) {

} elseif ($a == 2) {

} elseif ($a == 3) {

} elseif ($a == 4) {

} else {

}

// Better way to write long if/else lists
switch ($a) {
    case 1 :
        doSomething(1);
        'break 1;

    case 2 :
        doSomething(2);
        'break 1;

    case 3 :
        doSomething(3);
        'break 1;

    case 4 :
        doSomething(4);
        'break 1;

    default :
        doSomething();
        'break 1;
}

?>
Command Line Structures/SwitchToSwitch
Analyzers Analyze

Switch With Too Many Default

Switch statements should only hold one default, not more. Check the code and remove the extra default.

PHP 7.0 won’t compile a script that allows for several default cases.

Multiple default happens often with large ‘switch().

<?php

switch($a) {
    case 1 :
        'break;
    default :
        'break;
    case 2 :
        'break;
    default :  // This default is never reached
        'break;
}

?>
Command Line Structures/SwitchWithMultipleDefault
Analyzers CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56

Switch Without Default

Always use a default statement in ‘switch().

Switch statements hold a number of ‘case’ that cover all known situations, and a ‘default’ one which is executed when all other options are exhausted.

<?php

// Missing default
switch($format) {
    case 'gif' :
        processGif();
        'break 1;

    case 'jpeg' :
        processJpeg();
        'break 1;

    case 'bmp' :
        throw new UnsupportedFormat($format);
}
// In case $format is not known, then switch is ignored and no processing happens, leading to preparation errors


// switch with default
switch($format) {
    case 'text' :
        processText();
        'break 1;

    case 'jpeg' :
        processJpeg();
        'break 1;

    case 'rtf' :
        throw new UnsupportedFormat($format);

    default :
        throw new UnknownFileFormat($format);
}
// In case $format is not known, an exception is thrown for processing

?>

Most of the time, ‘switch() do need a default case, so as to catch the odd situation where the ‘value is not what it was expected’. This is a good place to catch unexpected values, to set a default behavior.

Command Line Structures/SwitchWithoutDefault
clearPHP no-switch-without-default
Analyzers Analyze

Ternary In Concat

Ternary operator has higher priority than dot ‘.’ for concatenation. This means that :

<?php
  print 'B'.$b.'C'. $b > 1 ? 'D' : 'E';
?>

prints actually ‘E’, instead of the awaited ‘B0CE’.

To be safe, always add parenthesis when using ternary operator with concatenation.

Command Line Structures/TernaryInConcat
Analyzers Analyze

Throw Functioncall

The throw keyword is excepted to use an exception. Calling a function to prepare that exception before throwing it is possible, but forgetting the new keyword is also possible.

<?php

// Forgotten new
throw \RuntimeException('error!');

// Code is OK, function returns an exception
throw getException(ERROR_TYPE, 'error!');

function getException(ERROR_TYPE, $message) {
    return new \RuntimeException($messsage);
}

?>

When the new keyword is forgotten, then the class construtor is used as a functionname, and now exception is emited, but an ‘Undefined function’ fatal error is emited.

Command Line Exceptions/ThrowFunctioncall
Analyzers Analyze

Throw In Destruct

According to the manual, ‘Attempting to throw an exception from a destructor (called in the time of script termination) causes a fatal error.’

The destructor may be called during the lifespan of the script, but it is not certain. If the exception is thrown later, the script may end up with a fatal error. Thus, it is recommended to avoid throwing exceptions within the ‘__destruct method of a class.

<?php

// No exception thrown
class Bar {
    function '__construct() {
        throw new Exception(''__construct');
    }

    function '__destruct() {
        $this->cleanObject();
    }
}

// Potential crash
class Foo {
    function '__destruct() {
        throw new Exception(''__destruct');
    }
}

?>
Command Line Classes/ThrowInDestruct
Analyzers Analyze

Thrown Exceptions

All Zend Framework thrown exceptions.

<?php

//All directly thrown exceptions are reported
throw new \RuntimeException('Error while processing');

// Zend exceptions are also reported, thrown or not
$w = new \Zend\Filter\Exception\ExtensionNotLoadedException();
throw $w;

?>
Command Line ZendF/ThrownExceptions
Analyzers ZendFramework

Throws An Assignement

It is possible to throw an exception, and, in the same time, assign this exception to a variable.

<?php

    // $e is useful, though not by much
    $e = new() Exception();
    throw $e;

    // $e is useless
    throw $e = new() Exception();

?>

However, $e will never be used, as the exception is thrown, and any following code is not executed.

The assignement should be removed.

Command Line Structures/ThrowsAndAssign
Analyzers Analyze

Timestamp Difference

time() and ‘microtime() shouldn’t be used to calculate duration or with durations.

time() and ‘microtime() are subject to variations, depending on system clock variations, such as daylight saving time difference (every spring and fall, one hour variation), or leap seconds, happening on June, 30th or december 31th, as announcec by IERS.

<?php

// Calculating tomorow, same hour, the wrong way
// tomorrow is not always in 86400s, especially in countries with daylight saving
$tomorrow = time()  + 86400;

// Good way to calculate tomorrow
$datetime = new DateTime('tomorrow');

?>

When the difference may be rounded to a larger time unit (rounding the difference to days, or several hours), the variation may be ignored safely.

When the difference is very small, it requires a better way to mesure time difference, such as ticks, ext/hrtime, or including a check on the actual time zone (‘ini_get() with ‘date.timezone’).

Command Line Structures/TimestampDifference
Analyzers Analyze

Too Complex Expression

Long expressions should be broken in small chunks, to limit complexity.

Really long expressions tends to be error prone : either by typo, or by missing details. They are even harder to review, once the initialy build of the expression is gone.

As a general rule, it is recommended to keep expressions short. The analysis include any expression that is more than 15 tokens large : variable and operaors counts as one, properties, arrays count as two. Parenthesis are also counted.

PHP has no specific limit to expression size, so long expression are legal and valid. It is possible that the business logic requires a complex equation.

<?php

// Why not calculate wordwrap size separatedly ?
$a = explode(\n, wordwrap($this->message, floor($this->width / imagefontwidth($this->fontsize)), \n));

// Longer but easier to read
$width = floor($this->width / imagefontwidth($this->fontsize)), \n);
$a = explode(\n, wordwrap($this->message, $width);

// Here, some string building, including error management with @, is making the data quite complex.
fwrite($fp, 'HEAD ' . @$url['path'] . @$url['query'] . ' HTTP/1.0' . \r\n . 'Host: ' . @$url['host'] . \r\n\r\n)

// Better validation of data.
$http_header = 'HEAD ';
if ('isset($url['path'])) {
    $http_header .= $url['path'];
}
if ('isset($url['query'])) {
    $http_header .= $url['query'];
}

$http_header .=  \r\n;
if ('isset($url['host'])) {
    $http_header .= 'Host: ' . $url['host'] . \r\n\r\n;
}

fwrite($fp, $http_header);

?>
Command Line Structures/ComplexExpression
Analyzers Analyze

Too Many Finds

Too many methods called ‘find*’ in this class. It is may be time to consider the Specification pattern.

<?php

// quite a fishy interface
interface UserInterface {
    public function findByEmail($email);
    public function findByUsername($username);
    public function findByFirstName($firstname);
    public function findByLastName($lastname);
    public function findByName($name);
    public function findById($id);

    public function insert($user);
    public function update($user);
}

?>

See also On Taming Repository Classes in Doctrine , On Taming Repository Classes in Doctrine… Among other things., specifications.

Command Line Classes/TooManyFinds
Analyzers Analyze

Too Many Injections

When a class is constructed with more than four dependencies, it should be split into smaller classes.

<?php

// This class relies on 5 other instances.
// It is probably doing too much.
class Foo {
    public function '__construct(
            A $a,
            B $b,
            C $c,
            D $d
            E $e ) {
        $this->a = $a;
        $this->b = $b;
        $this->d = $d;
        $this->d = $d;
        $this->e = $e;
    }
}

?>

See also Dependency Injection Smells.

Command Line Classes/TooManyInjections
Analyzers Analyze

Too Many Local Variables

Too many local variables were found in the methods. When over 15 variables are found in such a method, a violation is reported.

Local variables exclude globals (imported with global) and arguments.

When too many variables are used in a function, it is a code smells. The function is trying to do too much and needs extra space for juggling. Beyond 15 variables, it becomes difficult to keep track of their name and usage, leading to confusion, overwritting or hijacking.

<?php

// This function is OK : 3 vars are arguments, 3 others are globals.
function a20a3g3($a1, $a2, $a3) {
    global $a4, $a5, $a6;

    $a1  = 1;
    $a2  = 2;
    $a3  = 3 ;
    $a4  = 4 ;
    $a5  = 5 ;
    $a6  = 6 ;
    $a7  = 7 ;
    $a8  = 8 ;
    $a9  = 9 ;
    $a10 = 10;
    $a11  = 11;
    $a12  = 12;
    $a13  = 13 ;
    $a14  = 14 ;
    $a15  = 15 ;
    $a16  = 16 ;
    $a17  = 17 ;
    $a18  = 18 ;
    $a19  = 19 ;
    $a20 = 20;

}

// This function has too many variables
function a20() {

    $a1  = 1;
    $a2  = 2;
    $a3  = 3 ;
    $a4  = 4 ;
    $a5  = 5 ;
    $a6  = 6 ;
    $a7  = 7 ;
    $a8  = 8 ;
    $a9  = 9 ;
    $a10 = 10;
    $a11  = 11;
    $a12  = 12;
    $a13  = 13 ;
    $a14  = 14 ;
    $a15  = 15 ;
    $a16  = 16 ;
    $a17  = 17 ;
    $a18  = 18 ;
    $a19  = 19 ;
    $a20 = 20;

}

?>
Command Line Functions/TooManyLocalVariables
Analyzers Analyze

Uncaught Exceptions

The following exceptions are thrown in the code, but are never caught.

<?php

// This exception is throw, but not caught. It will lead to a fatal error.
if ($message = check_for_error()) {
    throw new My\Exception($message);
}

// This exception is throw, and caught.
try {
    if ($message = check_for_error()) {
        throw new My\Exception($message);
    }
} catch (\Exception $e) {
    doSomething();
}

?>

Either they will lead to a fatal error, or they have to be caught by a larger application.

Command Line Exceptions/UncaughtExceptions
Analyzers Analyze

Unchecked Resources

Resources are created, but never checked before being used. This is not safe.

Always check that resources are correctly created before using them.

<?php

// always check that the resource is created correctly
$fp = fopen($d,'r');
if ($fp === false) {
    throw new Exception('File not found');
}
$firstLine = fread($fp);

// This directory is not checked : the path may not exist and return false
$uncheckedDir = opendir($pathToDir);
while(readdir($uncheckedDir)) {
    // do something()
}

// This file is not checked : the path may not exist or be unreadable and return false
$fp = fopen($pathToFile);
while($line = freads($fp)) {
    $text .= $line;
}

// quick unsafe one-liner : using bzclose on an unchecked resource
bzclose(bzopen('file'));

?>

See also resources.

Command Line Structures/UncheckedResources
clearPHP no-unchecked-resources
Analyzers Analyze

Unconditional Break In Loop

An unconditional ‘break was found in a loop. Since the ‘break is directly in the body of the loop, it is always executed, creating a strange loop that can only run once.

Here, ‘break may also be a return, a goto or a ‘continue. They all branch out of the loop. Such statement are valid, but should be moderated with a condition.

<?php

// return in loop should be in
function summAll($array) {
    $sum = 0;

    foreach($array as $a) {
        // Stop at the first error
        if (is_string($a)) {
            return $sum;
        }
        $sum += $a;
    }

    return $sum;
}

// foreach loop used to collect first element in array
function getFirst($array) {
    foreach($array as $a) {
        return $a;
    }
}

?>
Command Line Structures/UnconditionLoopBreak
Analyzers Analyze

Undefined Caught Exceptions

Those are exceptions that are caught in the code, but are not defined in the application.

They may be externally defined, such as in core PHP, extensions or libraries. Make sure those exceptions are usefull to your application : otherwise, they are dead code.

<?php

try {
    library_function($some, $args);

} catch (LibraryException $e) {
    // This exception is not defined, and probably belongs to Library
    print Library failed\n;

} catch (OtherLibraryException $e) {
    // This exception is not defined, and probably do not belongs to this code
    print Library failed\n;

} catch (\Exception $e) {
    // This exception is a PHP standard exception
    print Something went wrong, but not at Libary level\n;
}

?>
Command Line Exceptions/CaughtButNotThrown
Analyzers Dead code

Undefined Class 2.0

Mark classes, interfaces and traits when they are not available in Zend Framework 2.0.

<?php

// 2.0 only class
$a = new Zend\Authentication\Adapter\Digest();

// Not a 2.0 class (2.1+)
$b = $d 'instanceof Zend\Authentication\Adapter\Callback;

?>

See Zend Framework 2.0.

Command Line ZendF/UndefinedClass20
Analyzers ZendFramework

Undefined Class 2.1

Mark classes, interfaces and traits when they are not available in Zend Framework 2.0.

<?php

// 2.0 only class
$a = new Zend\Authentication\Adapter\Digest();

// Not a 2.0 class (2.1+)
$b = $d 'instanceof Zend\Authentication\Adapter\Callback;

?>

See Zend Framework 2.1.

Command Line ZendF/UndefinedClass21
Analyzers ZendFramework

Undefined Class 2.2

Mark classes, interfaces and traits when they are not available in Zend Framework 2.2.

<?php

// 2.2 class (may be other versions)
$a = new Zend\Authentication\Adapter\DbTable\AbstractAdapter();

// Not a 2.2 class (2.2+)
$b = $d 'instanceof Zend\Authentication\Adapter\Callback;

?>

See Zend Framework 2.2.

Command Line ZendF/UndefinedClass22
Analyzers ZendFramework

Undefined Class 2.3

Mark classes, interfaces and traits when they are not available in Zend Framework 2.3.

<?php

// 2.3 class
$a = new Zend\Authentication\Adapter\DbTable\CredentialTreatmentAdapter();

// Not a 2.3 class
$b = $d 'instanceof Zend\Cache\Module;

?>

See Zend Framework 2.3.

Command Line ZendF/UndefinedClass23
Analyzers ZendFramework

Undefined Class 2.4

Mark classes, interfaces and traits when they are not available in Zend Framework 2.4.

<?php
// 2.4 class
$a = new Zend\Authentication\Adapter\DbTable\AbstractAdapter();

// Not a 2.4 class
$b = $d 'instanceof Zend\Cache\Service\StorageAdapterPluginManagerFactory;

?>

See Zend Framework 2.4.

Command Line ZendF/UndefinedClass24
Analyzers ZendFramework

Undefined Class 2.5

Mark classes, interfaces and traits when they are not available in Zend Framework 2.5.

<?php
// 2.5 class
$a = new Zend\Authentication\Adapter\DbTable\AbstractAdapter();

// Not a 2.5 class
$b = $d 'instanceof Zend\Cache\Service\PatternPluginManagerFactory;

?>
Command Line ZendF/UndefinedClass25
Analyzers ZendFramework

Undefined Class 3.0

Mark classes, interfaces and traits when they are not available in Zend Framework 2.5.

<?php
// 3.0 class
$a = new Zend\Authentication\Adapter\DbTable\CallbackCheckAdapter();

// Not a 3.0 class
$b = $d 'instanceof Zend\EventManager\GlobalEventManager;

?>
Command Line ZendF/UndefinedClass30
Analyzers ZendFramework

Undefined Class Constants

Class constants that are used, but never defined. This should yield a fatal error upon execution, but no feedback at compile level.

<?php

class foo {
    const A = 1;
    define('B', 2);
}

// here, C is not defined in the code and is reported
echo foo::A.foo::B.foo::C;

?>
Command Line Classes/UndefinedConstants
Analyzers Analyze, Analyze

Undefined Classes

Those classes are used in the code, but there are no definition for them.

This may happens under normal conditions, if the application makes use of an unsupported extension, that defines extra classes; or if some external libraries, such as PEAR, are not provided during the analysis.

<?php

// FPDF is a classic PDF class, that is usually omitted by Exakat.
$o = new FPDF();

// Exakat reports undefined classes in 'instanceof
// PHP ignores them
if ($o 'instanceof SomeClass) {
    // doSomething();
}

// Classes may be used in typehint too
function foo(TypeHintClass $x) {
    // doSomething();
}

?>
Command Line Classes/UndefinedClasses
Analyzers Analyze

Undefined Constants

Constants definition can’t be located.

Those constants are not defined in the code, and will raise errors, or use the fallback mechanism of being treated like a string.

<?php

const A = 1;
define('B', 2);

// here, C is not defined in the code and is reported
echo A.B.C;

?>

It is recommended to define them all, or to avoid using them.

Command Line Constants/UndefinedConstants
Analyzers none

Undefined Functions

Those functions are not defined in the code. This means that the functions are probably defined in a missing library, or in an extension. If not, this will yield a Fatal error at execution.

<?php

// Undefined function
foo($a);

// valid function, as it belongs to the ext/yaml extension
$parsed = yaml_parse($yaml);

// This function is not defined in the a\b\c namespace, nor in the global namespace
a\b\c\foo();

?>
Command Line Functions/UndefinedFunctions
Analyzers Analyze

Undefined Interfaces

Typehint or ‘instanceof that are relying on undefined interfaces (or classes) : they will always return false. Any condition based upon them are dead code.

<?php

class var implements undefinedInterface {
    // If undefinedInterface is undefined, this code lints but doesn't run
}

if ($o 'instanceof undefinedInterface) {
    // This is silent dead code
}

function foo(undefinedInterface $a) {
    // This is dead code
    // it will probably be discovered at execution
}

?>
Command Line Interfaces/UndefinedInterfaces
Analyzers Analyze

Undefined Parent

List of properties and methods that are accessed using ‘parent’ keyword but are not defined in the parent class.

This will be compilable but will yield a fatal error during execution.

Note that if the parent is defined (extends someClass) but someClass is not available in the tested code (it may be in composer, another dependency, or just not there) it will not be reported.

Command Line Classes/UndefinedParentMP
Analyzers Analyze

Undefined Properties

List of properties that are not explicitely defined in the class, its parents or traits.

Command Line Classes/UndefinedProperty
clearPHP no-undefined-properties
Analyzers Analyze

Undefined Trait

Those traits are undefined.

When the using class or trait is instantiated, PHP emits a a fatal error.

<?php

use Composer/Component/someTrait as externalTrait;

trait t {
    function foo() {}
}

// This class uses trait that are all known
class hasOnlyDefinedTrait {
    use t, externalTrait;
}

// This class uses trait that are unknown
class hasUndefinedTrait {
    use unknownTrait, t, externalTrait;
}
?>
Command Line Traits/UndefinedTrait
Analyzers Analyze

Undefined Zend 1.10

List of undefined classes or interfaces in Zend 1.10.

See Zend Framework 1.10.

Command Line ZendF/UndefinedClass110
Analyzers ZendFramework

Undefined Zend 1.11

List of undefined classes or interfaces in Zend 1.11

See Zend Framework 1.11.

Command Line ZendF/UndefinedClass111
Analyzers ZendFramework

Undefined Zend 1.12

List of undefined classes or interfaces in Zend 1.12.

See Zend Framework 1.12.

Command Line ZendF/UndefinedClass112
Analyzers ZendFramework

Undefined Zend 1.8

List of undefined classes or interfaces in Zend 1.8.

See Zend Framework 1.8.

Command Line ZendF/UndefinedClass18
Analyzers ZendFramework</