9. Rules list

9.1. Introduction

9.2. $HTTP_RAW_POST_DATA Usage

$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 prepare code to this lack of feature 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 variable.

9.2.1. Suggestions

  • Use php://input with fopen() instead.
Short name Php/RawPostDataUsage
Rulesets CompatibilityPHP56
Severity Major
Time To Fix Slow (1 hour)

9.3. $php_errormsg Usage

$php_errormsg is removed since PHP 8.0. $php_errormsg tracks the last error message, with the directive track_errors. All was removed in PHP 8.0, and shall be replaced with error_get_last().

<?php

function foo() {
    global $php_errormsg;

    echo 'Last error: '.$php_errormsg;

    echo 'Also, last error: '.error_get_last();
}

?>

9.3.1. Suggestions

  • Use error_get_last() instead.
Short name Php/PhpErrorMsgUsage
Rulesets CompatibilityPHP80
Php Version 8.0-
Severity Minor
Time To Fix Quick (30 mins)

9.4. $this Belongs To Classes Or Traits

$this variable represents the current object, inside a class or trait scope.

It is a pseudo-variable, and should be used within class’s or trait’s methods and not outside. It should also not be used in static methods.

PHP 7.1 is stricter and check for $this at several situations. 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
    }

}
?>

9.4.1. Suggestions

  • Do not use $this as a variable name, except for the current object, in a class, trait or closure.
Short name Classes/ThisIsForClasses
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)
Examples OpenEMR

9.5. $this Is Not An Array

$this variable represents the current object and it is not an array.

This is unless the class (or its parents) has the ArrayAccess interface, or extends ArrayObject or SimpleXMLElement.

<?php

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

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

?>

See also ArrayAccess, ArrayObject and The Basics.

9.5.1. Suggestions

  • Extends ArrayObject, or a class that extends it, to use $this as an array too.
  • Implements ArrayAccess to use $this as an array too.
  • Use a property in the current class to store the data, instead of $this directly.
Short name Classes/ThisIsNotAnArray
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)

9.6. $this Is Not For Static Methods

Static methods shouldn’t use $this variable.

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

While executing a static method, $this is actually set to NULL.

<?php

class foo {
    static $staticProperty = 1;

    // Static methods should use static properties
    static public function count() {
        return self::$staticProperty++;
    }

    // Static methods can't use $this
    static public function bar() {
        return $this->a;   // No $this usage in a static method
    }
}

?>

See also Static Keyword <https://www.php.net/manual/en/language.oop5.`static.php>`_.

9.6.1. Suggestions

  • Remove the static keyword on the method, and update all calls to this method to use $this
  • Remove the usage of $this in the method, replacing it with static properties
  • Make $this an argument (and change its name) : then, make the method a function
Short name Classes/ThisIsNotForStatic
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)
ClearPHP no-static-this

9.7. ** For Exponent

The operator ** calculates exponents, also known as power.

Use it instead of the slower function pow(). This operator was introduced in PHP 5.6.

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

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

Be aware the the ‘-‘ operator has lower priority than the ** operator : this leads to the following confusing result.

<?php
    echo -3 ** 2;
    // displays -9, instead of 9
?>

This is due to the parser that processes separately - and the following number. Since ** has priority, the power operation happens first.

Being an operator, ** is faster than pow(). This is a microoptimisation.

See also Arithmetic Operators.

9.7.1. Suggestions

  • Use the ** operator
  • For powers of 2, use the bitshift operators
  • For literal powers of 2, consider using the 0xFFFFFFFFF syntax.
Short name Php/NewExponent
Rulesets Suggestions, php-cs-fixable
Php Version With PHP 5.6 and more recent
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Examples Traq, TeamPass

9.8. ::class

PHP has a special class constant to hold the name of the class : class keyword. It represents the class name that is used in the left part of the operator.

Using \:\:class is safer than relying on a string. It does adapt if the class’s name or its namespace is changed’. It is also faster, though it is a micro-optimisation.

It is introduced in PHP 5.5.

<?php

use A\B\C as UsedName;

class foo {
    public function bar( ) {
        echo ClassName::class;
        echo UsedName::class;
    }
}

$f = new Foo( );
$f->bar( );
// displays ClassName
// displays A\B\C

?>

Be aware that \:\:class is a replacement for __CLASS__ magic constant.

See also Class Constant.

9.8.1. Suggestions

  • Use ::class whenever possible. That exclude any dynamic call.
Short name Php/StaticclassUsage
Rulesets CompatibilityPHP53, CompatibilityPHP54
Php Version With PHP 5.5 and more recent
Severity Major
Time To Fix Slow (1 hour)
Precision Very high

9.9. @ Operator

@ is the ‘no scream’ operator : it suppresses 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().

See also Error Control Operators and Five reasons why the shut-op operator should be avoided.

9.9.1. Suggestions

  • Remove the @ operator by default
Name Default Type Description
authorizedFunctions noscream_functions.json data Functions that are authorized to sports a @.
Short name Structures/Noscream
Rulesets Analyze, Performances, CI-checks
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
ClearPHP no-noscream
Examples Phinx, PhpIPAM

9.10. Abstract Away

Avoid using PHP native functions that produce data direcly in the code. For example, date() or random_int(). They should be abstracted away in a method, that will be replaced later for testing purposes, or even debugging.

To abstract such calls, place them in a method, and add an interface to this method. Then, create and use those objects.

<?php

// abstracted away date
$today = new MyDate();
echo 'Date : '.$today->date('r');

// hard coded date of today : it changes all the time.
echo 'Date : '.date('r');

interface MyCalendar{
    function date($format) : string ;
}

class MyDate implements MyCalendar {
    function date($format) : string { return date('r'); }
}

// Valid implementation, reserved for testing purpose
// This prevents from waiting 4 years for a test.
class MyDateForTest implements MyCalendar {
    function date($format) : string { return date('r', strtotime('2016-02-29 12:00:00')); }
}

?>

This analysis targets two API for abstraction : time and random values. Time and date related functions may be replaced by Carbon, Clock, Chronos. Random values may be replaced with RandomLib or a custome interface.

See also Being in control of time in PHP and How to test non-deterministic code.

9.10.1. Suggestions

  • Abstract away the calls to native PHP functions, and upgrade the unit tests
Name Default Type Description
abstractableCalls   ini_hash Functions that shouldn’t be called directly, unless in a method.
abstractableClasses   ini_hash Classes that shouldn’t be instantiated directly, unless in a method.
Short name Patterns/AbstractAway
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)

9.11. Abstract Or Implements

A class must implements all abstract methods of it parent, or be abstract too.

While PHP lints this code, it won’t execute it and stop with a Fatal Error : Class BA contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (A\:\:aFoo).

<?php

abstract class Foo {
    abstract function FooBar();
}

// This is in another file : php -l would detect it right away

class FooFoo extends Foo {
    // The method is not defined.
    // The class must be abstract, just like Foo
}

?>

See also Class Abstraction.

9.11.1. Suggestions

  • Implements all the abstract methods of the class
  • Make the class abstract
Short name Classes/AbstractOrImplements
Rulesets Analyze, LintButWontExec
Severity Major
Time To Fix Quick (30 mins)
Examples Zurmo

9.12. 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, it is necessary 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 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 independent.

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? <https://stackoverflow.com/questions/999066/why-does-php-5-2-disallow-abstract-static-class-methods>`_.

9.12.1. Suggestions

  • Remove abstract keyword from the method
  • Remove static keyword from the method
  • Remove the method
Short name Classes/AbstractStatic
Rulesets Analyze
Php Version With PHP 7.0 and older
Severity Minor
Time To Fix Quick (30 mins)

9.13. Access Protected Structures

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

<?php

class foo {
    protected $bar = 1;
}

$foo = new Foo();
$foo->bar = 2;

?>

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

9.13.1. Suggestions

  • Change ‘protected’ to ‘public’ to relax the constraint
  • Add a getter method to reach the target value
  • Remove the access to the protected value and find it another way
Short name Classes/AccessProtected
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)

9.14. 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;
    }
}

?>
Short name Classes/AccessPrivate
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)

9.15. Add Default Value

Parameter in methods definition may receive a default value. This allows the called method to set a value when the parameter is omitted.

<?php

function foo($i) {
    if (!is_integer($i)) {
        $i = 0;
    }
}

?>

See also Function arguments.

9.15.1. Suggestions

  • Add a default value for parameters
Short name Functions/AddDefaultValue
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)
Examples Zurmo, Typo3

9.16. Adding Zero

Adding 0 is useless, as 0 is the neutral element for addition. Besides, when one of the argument is an integer, PHP triggers a cast to integer.

It is recommended to make the cast explicit with (int).

<?php

// Explicit cast
$a = (int) foo();

// Useless addition
$a = foo() + 0;
$a = 0 + foo();

// 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;

?>

Adding zero is also reported when the zero is a defined constants.

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

9.16.1. Suggestions

  • Remove the +/- 0, may be the whole assignation
  • Use an explicit type casting operator (int)
Short name Structures/AddZero
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Instant (5 mins)
ClearPHP no-useless-math
Examples Thelia, OpenEMR

9.17. 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.

9.17.1. Suggestions

  • Always use PHP recommended functions
Short name Functions/AliasesUsage
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Quick (30 mins)
ClearPHP no-aliases
Examples Cleverstyle, phpMyAdmin

9.18. 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'];

?>

See also Predefined Variables.

Short name Variables/VariableUppercase
Rulesets Coding Conventions
Severity Minor
Time To Fix Slow (1 hour)

9.19. 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

?>

9.19.1. Suggestions

  • Keep the implements call in the class that do implements the methods. Remove it from the children classes.
Short name Interfaces/AlreadyParentsInterface
Rulesets Analyze, Suggestions
Severity Minor
Time To Fix Instant (5 mins)
Examples WordPress, Thelia

9.20. Already Parents Trait

Trait is already used a parent’s class or trait. There is no use to include it a second time.

<?php

trait ta {
    use tb;
}

trait t1 {
    use ta;
    use tb; // also used by ta
}

class b {
    use t1; // also required by class c
    use ta; // also required by trait t1
}

class c extends b {
    use t1;
}

?>

See also Traits.

9.20.1. Suggestions

  • Eliminate one of the trait request
Short name Traits/AlreadyParentsTrait
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.21. 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.

See also foreach.

9.21.1. Suggestions

  • Add the reference on the modified blind variable, and avoid accessing the source array
Short name Structures/AlteringForeachWithoutReference
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Quick (30 mins)
ClearPHP use-reference-to-alter-in-foreach
Examples Contao, WordPress

9.22. 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;

?>
Short name Structures/AlternativeConsistenceByFile
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)

9.23. Always Anchor Regex

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

Without ^ and $, the regex searches for any pattern that satisfies the criteria, leaving any unused part of the string available for arbitrary 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.

9.23.1. Suggestions

  • Add an anchor to the beginning and ending of the string
Short name Security/AnchorRegex
Rulesets Security
Severity Major
Time To Fix Instant (5 mins)

9.24. 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);

?>

9.24.1. Suggestions

  • Compare count() to non-zero values
  • Use empty()
Short name Structures/NeverNegative
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Instant (5 mins)
Examples Magento

9.25. Always Use Function With array_key_exists()

array_key_exists() has been granted a special VM opcode, and is much faster. This applies to PHP 7.4 and more recent.

It requires that array_key_exists() is statically resolved, either with an initial \, or a use function expression. This doesn’t affect the global namespace.

<?php

namespace my/name/space;

// do not forget the 'function' keyword, or it will apply to classes.
use function array_key_exists as foo; // the alias is not necessary, and may be omitted.

// array_key_exists is aliased to foo :
$c = foo($a, $b);

// This call requires a fallback to global, and will be slow.
$c = array_key_exists($a, $b);

?>

This analysis is related to Php/ShouldUseFunction, and is a special case, that only concerns array_key_exists().

See also Add array_key_exists to the list of specialy compiled functions.

9.25.1. Suggestions

  • Use the use command for arrray_key_exists(), at the beginning of the script
  • Use an initial before array_key_exists()
  • Remove the namespace
Short name Performances/Php74ArrayKeyExists
Rulesets Performances
Php Version 7.4+
Severity Minor
Time To Fix Quick (30 mins)

9.26. Ambiguous Array Index

Indexes should not be defined with different types than int or string.

Array indices only accept integers and strings, so any other type of literal is reported. In fact, null is turned into an empty string, booleans are turned into an integer, and real numbers are truncated (not rounded).

<?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.

See also array.

9.26.1. Suggestions

  • Only use string or integer as key for an array.
  • Use transtyping operator (string) and (int) to make sure of the type
Short name Arrays/AmbiguousKeys
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)
Examples PrestaShop, Mautic

9.27. Ambiguous Static

Methods or properties with the same name, are defined static in one class, and not static in another. This is error prone, as it requires a good knowledge of the code to make it static or not.

Try to keep the methods simple and unique. Consider renaming the methods and properties to distinguish them easily. A method and a static method have probably different responsibilities.

<?php

class a {
    function mixedStaticMethod() {}
}

class b {
    static function mixedStaticMethod() {}
}

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

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

?>
Short name Classes/AmbiguousStatic
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)

9.28. Ambiguous Visibilities

The properties have the same name, but have different visibilities, across different classes.

While it is legit to have a property with the same name in different classes, it may easily lead to confusion. As soon as the context is need to understand if the property is accessible or not, the readability suffers.

It is recommended to handle the same properties in the same way across classes, even when the classes are not related.

<?php

class person {
    public $name;
    private $address;
}

class gangster {
    private $name;
    public $nickname;
    private $address;
}

$someone = Human::load(123);
echo 'Hello, '.$someone->name;

?>

9.28.1. Suggestions

  • Sync visibilities for both properties, in the different classes
  • Use different names for properties with different usages
Short name Classes/AmbiguousVisibilities
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)
Examples Typo3

9.29. Anonymous Classes

Anonymous classes.

<?php

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

?>
Short name Classes/Anonymous
Rulesets CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.0 and more recent
Severity Major
Time To Fix Slow (1 hour)

9.30. Argument Should Be Typehinted

When a method expects objects as argument, those arguments should be typehinted. This way, it provides early warning that a wrong object is being sent to the method.

The analyzer will detect situations where a class, or the keywords ‘array’ or ‘callable’.

<?php

// What are the possible classes that have a 'foo' method?
function foo($bar) {
    return $bar->foo();
}

?>

Closure arguments are omitted.

See also Type declarations.

9.30.1. Suggestions

  • Add the typehint to the function arguments
Short name Functions/ShouldBeTypehinted
Rulesets Typechecks
Severity Minor
Time To Fix Slow (1 hour)
ClearPHP always-typehint
Examples Dolphin, Mautic

9.31. Array_Fill() With Objects

array_fill() fills an array with identical objects, not copies nor clones. This means that all the filled objects are a reference to the same object. Changing one of them will change any of them.

Make sure this is the intended effect in the code.

<?php

$x = new StdClass();
$array = array_fill(0, 10, $x);

$array[3]->y = Set in object #3;

// displays Set in object #3;
echo $array[5]->y;

?>

This applies to array_pad() too. It doesn’t apply to array_fill_keys(), as objects will be cast to a string before usage in this case.

9.31.1. Suggestions

  • Use a loop to fill in the array with cloned() objects.
Short name Structures/ArrayFillWithObjects
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.32. Array_merge Needs Array Of Arrays

When collecting data to feed array_merge(), use an array of array as default value. `array(`array()) <https://www.php.net/array>`_` is the neutral value for array_merge();

This analysis also reports when the used types are not an array : array_merge() does not accept scalar values, but only arrays.

<?php

// safe default value
$a = array(array());

// when $list is empty, it is
foreach($list as $l) {
    $a[] = $l;
}
$b = array_merge($a);

?>

Since PHP 7.4, it is possible to call array_merge() without an argument : this means the default value may an empty array. This array shall not contain scalar values.

See also array_merge.

9.32.1. Suggestions

  • Use `array(array())` or `[[]]` as default value for array_merge()
  • Remove any non-array value from the values in the default array
Short name Structures/ArrayMergeArrayArray
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.33. Assert Function Is Reserved

Avoid defining an assert function in namespaces.

While they work fine when the assertions are active (zend.assertions=1), calls to unqualified assert are optimized away when assertions are not active.

Since PHP 7.3, a fatal error is emitted : Defining a custom `assert() <https://www.php.net/assert>`_ function is deprecated, as the function has special semantics.

<?php
//      Run this with zend.assertions=1 and
// Then run this with zend.assertions=0

namespace Test {
    function assert() {
        global $foo;

        $foo = true;
    }
}

namespace Test {
    assert();

    var_dump(isset($foo));
}

?>

See also assert and User-defined assert function is optimized away with zend.assertions=-1.

9.33.1. Suggestions

  • Rename the custom function with another name
Short name Php/AssertFunctionIsReserved
Rulesets Analyze, CompatibilityPHP73
Severity Critical
Time To Fix Slow (1 hour)

9.34. Assign And Compare

Assignation has a lower precedence than comparison. As such, the assignation always happens after the comparison. This leads to the comparison being stored in the variable, and not the value being compared.

<?php

if ($id = strpos($string, $needle) !== false) {
    // $id now contains a boolean (true or false), but not the position of the $needle.
}

// probably valid comparison, as $found will end up being a boolean
if ($found = strpos($string, $needle) === false) {
    doSomething();
}

// always valid comparison, with parenthesis
if (($id = strpos($string, $needle)) !== false) {
    // $id now contains a boolean (true or false), but not the position of the $needle.
}

// Being a lone instruction, this is always valid : there is no double usage with if condition
$isFound = strpos($string, $needle) !== false;


?>

See also Operator Precedence.

9.34.1. Suggestions

  • Use parenthesis
  • Separate assignation and comparison
  • Drop assignation or comparison
Short name Structures/AssigneAndCompare
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Quick (30 mins)

9.35. 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.

9.35.1. Suggestions

  • Add a default value whenever possible. This is easy for scalars, and array()
Short name Classes/MakeDefault
Rulesets Analyze
Severity Minor
Time To Fix Instant (5 mins)
ClearPHP use-properties-default-values
Examples LiveZilla, phpMyAdmin

9.36. Assign With And

The lettered logical operators yield to assignation. It may collect less information than expected.

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.

9.36.1. Suggestions

  • Always use symbol && rather than letter and
  • To be safe, add parenthesis to enforce priorities
Short name Php/AssignAnd
Rulesets Analyze, CI-checks
Severity Critical
Time To Fix Quick (30 mins)
Examples xataface

9.37. 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');
}

?>
Short name Variables/AssignedTwiceOrMore
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.38. Assumptions

Assumptions in the code, that leads to possible bugs.

Some conditions may be very weak, and lead to errors. For example, the code below checks that the variable $a is not null, then uses it as an array. There is no relationship between ‘not null’ and ‘being an array’, so this is an assumption.

<?php

// Assumption : if $a is not null, then it is an array. This is not always the case.
function foo($a) {
    if ($a !== null) {
        echo $a['name'];
    }
}

// Assumption : if $a is not null, then it is an array. Here, the typehint will ensure that it is the case.
// Although, a more readable test is is_array()
function foo(?array $a) {
    if ($a !== null) {
        echo $a['name'];
    }
}

?>

See also From assumptions to assertions.

9.38.1. Suggestions

Short name Php/Assumptions
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.39. Autoappend

Appending a variable to itself leads to enormous usage of memory.

<?php

// Always append a value to a distinct variable
foreach($a as $b) {
    $c[] = $b;
}

// This copies the array to itself, and double the size each loop
foreach($a as $b) {
    $c[] = $c;
}
?>

9.39.1. Suggestions

  • Change the variable in the append, on the left
  • Change the variable in the append, on the right
Short name Performances/Autoappend
Rulesets Performances
Severity Minor
Time To Fix Quick (30 mins)

9.40. 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.

See also PHP 7 performance improvements (3/5): Encapsed strings optimization.

9.40.1. Suggestions

  • Collect all pieces in an array, then implode() the array in one call.
Short name Performances/NoConcatInLoop
Rulesets Performances, Top10
Severity Major
Time To Fix Slow (1 hour)
Examples SuiteCrm, ThinkPHP

9.41. Avoid Large Array Assignation

Avoid setting large arrays to local variables. This is done every time 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);
    }
}

?>
Short name Structures/NoAssignationInFunction
Rulesets Performances
Severity Minor
Time To Fix Slow (1 hour)

9.42. Avoid Optional Properties

Avoid optional properties, to prevent littering 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 and a lot of checks added to the code.

Either make sure the property is set with an actual object rather than with null, or use a null object. A null 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.

9.42.1. Suggestions

  • Use a null object to fill any missing value
  • Make sure the property is set at constructor time
Short name Classes/AvoidOptionalProperties
Rulesets Analyze
Severity Major
Time To Fix Slow (1 hour)
Examples ChurchCRM, Dolibarr

9.43. 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 comfort, 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.

Short name Structures/PrintWithoutParenthesis
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Quick (30 mins)

9.44. Avoid Real

PHP has two float data type : real and double. real is rarely used, and might be deprecated in PHP 7.4.

To prepare code, avoid using is_real() and the (real) typecast.

<?php

// safe way to check for float
if (!is_float($a)) {
    $a = (float) $a;
}

// Avoid doing that
if (!is_real($a)) {
    $a = (real) $a;
}

?>

See also PHP RFC: Deprecations for PHP 7.4.

9.44.1. Suggestions

  • Replace is_real() by is_float()
  • Replace (real) by (float)
Short name Php/AvoidReal
Rulesets Suggestions, Top10
Severity Minor
Time To Fix Quick (30 mins)

9.45. Avoid Self In Interface

Self and Parent are tricky when used in an interface.

self refers to the current interface or its extended parents : as long as the constant is defined in the interface family, this is valid. On the other hand, when self refers to the current class, the resolution of names will happen at execution time, leading to confusing results.

parent has the same behavior than self, except that it doesn’t accept to be used inside an interface, as it will yield an error. This is one of those error that lint but won’t execute in certain conditions.

Static can’t be used in an interface, as it needs to be resolved at call time anyway.

<?php

interface i extends ii {
    // This 'self' is valid : it refers to the interface i
    public const I = self::I2 + 2;

    // This 'self' is also valid, as it refers to interface ii, which is a part of interface i
    public const I2 = self::IP + 4;

    // This makes interface i dependant on the host class
    public const I3 = parent::A;
}

?>

See also Scope Resolution Operator (::).

9.45.1. Suggestions

  • Use a fully qualified namespace instead of self
  • Use a locally defined constant, so self is a valid reference
Short name Interfaces/AvoidSelfInInterface
Rulesets ClassReview
Severity Critical
Time To Fix Slow (1 hour)

9.46. Avoid 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 style 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.

<?php

$string = 'ab人cde';

echo substr($string, $pos, 1);
echo $string[$pos];

echo mb_substr($string, $pos, 1);

// when $pos = 1
// displays bbb
// when $pos = 2
// displays ??人

?>

Beware that substr() and $v[$pos] are similar, while mb_substr() is not. The first function works on bytes, while the latter works on characters.

9.46.1. Suggestions

  • Replace substr() with the array notations for strings.
  • Replace substr() with a call to mb_substr().
Short name Structures/NoSubstrOne
Rulesets Analyze, Performances, CompatibilityPHP71, Suggestions, Top10, CI-checks
Severity Minor
Time To Fix Instant (5 mins)
Examples ChurchCRM, LiveZilla

9.47. Avoid Those Hash Functions

The following cryptography algorithms are considered insecure, 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 cryptography is 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 Secure Hash Algorithms.

9.47.1. Suggestions

  • Keep the current crypto, and add a call to a stronger one.
  • Change the crypto for a more modern one and update the related databases
Short name Security/AvoidThoseCrypto
Rulesets Security
Severity Major
Time To Fix Quick (30 mins)

9.48. 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.

9.48.1. Suggestions

  • Create a custom class to handle the properties
Short name Php/UseStdclass
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)

9.49. 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.

9.49.1. Suggestions

  • Use the [] operator
Short name Performances/AvoidArrayPush
Rulesets Performances
Severity Minor
Time To Fix Instant (5 mins)

9.50. Avoid array_unique()

The native function array_unique() is much slower than using other alternatives, 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;
    }
}

?>

See also array_unique.

9.50.1. Suggestions

  • Upgrade to PHP 7.2
  • Use an alternative way to make values unique in an array, using array_count_values(), for example.
Short name Structures/NoArrayUnique
Rulesets Performances
Php Version 7.2-
Severity Minor
Time To Fix Quick (30 mins)

9.51. Avoid get_class()

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

get_class() only compares the full namespace name of the object’s class, while instanceof actually resolves 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()
        }
    }
?>

See also get_class and Instanceof.

Short name Structures/UseInstanceof
Rulesets Analyze, Analyze, CI-checks, CI-checks
Severity Minor
Time To Fix Quick (30 mins)

9.52. Avoid glob() Usage

glob() and scandir() sorts results by default. When that kind of sorting is not needed, 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 expliciely configured with flags (aka, sorting is explicitly needed).

glob() accepts wildchar, such as *, that may not easily replaced with scandir() or opendir().

See also Putting glob to the test, How to list files recursively in a directory with PHP iterators and glob://.

9.52.1. Suggestions

  • Use FilesystemIterator, DirectoryIterator classes.
  • Use RegexIterator to filter any unwanted results from FilesystemIterator.
  • Use glob protocol for files : $it = new DirectoryIterator(‘glob://path/to/examples/*.php’);
Short name Performances/NoGlob
Rulesets Performances
Severity Major
Time To Fix Quick (30 mins)
Examples Phinx, NextCloud

9.53. Avoid mb_dectect_encoding()

mb_dectect_encoding() is bad at guessing encoding.

For example, UTF-8 and ISO-8859-1 share some common characters : when a string is build with them it is impossible to differentiate the actual encoding.

<?php

$encoding = mb_encoding_detect($_GET['name']);

?>

See also mb_encoding_detect, PHP vs. The Developer: Encoding Character Sets, DPC2019: Of representation and interpretation: A unified theory - Arnout Boks.

9.53.1. Suggestions

  • Store and transmit the data format
Short name Php/AvoidMbDectectEncoding
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.54. Avoid option arrays in constructors

Avoid option arrays in constructors. Use one parameter per injected element.

<?php

class Foo {
    // Distinct arguments, all typehinted if possible
    function __constructor(A $a, B $b, C $c, D $d) {
        $this->a = $a;
        $this->b = $b;
        $this->c = $c;
        $this->d = $d;
    }
}

class Bar {
    // One argument, spread over several properties
    function __constructor(array $options) {
        $this->a = $options['a'];
        $this->b = $options['b'];
        $this->c = $options['c'];
        $this->d = $options['d'];
    }
}

?>

See also Avoid option arrays in constructors.

9.54.1. Suggestions

  • Spread the options in the argument list, one argument each
  • Use a configuration class, that hold all the elements with clear names, instead of an array
Short name Classes/AvoidOptionArrays
Rulesets Analyze, ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.55. Avoid set_error_handler $context Argument

Avoid configuring set_error_handler() with a method that accepts 5 arguments. The last argument, $errcontext, is deprecated since PHP 7.2, and will be removed later.

<?php

// setting error_handler with an incorrect closure
set_error_handler(function($errno, $errstr, $errfile, $errline) {});

// setting error_handler with an incorrect closure
set_error_handler(function($errno, $errstr, $errfile, $errline, $errcontext) {});

?>

See also set_error_handler();

9.55.1. Suggestions

  • Remove the 6th argument of registered handlers.
Short name Php/AvoidSetErrorHandlerContextArg
Rulesets CompatibilityPHP72
Severity Major
Time To Fix Slow (1 hour)
Examples shopware, Vanilla

9.56. 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 resources 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).

9.56.1. Suggestions

  • Add a deadline of usage in the session, and wait past this deadline to start serving again. Until then, abort immediately.
  • Use element in the GUI to delay or slow usage.
Short name Security/NoSleep
Rulesets Security
Severity Minor
Time To Fix Quick (30 mins)

9.57. Bad Constants Names

PHP’s manual recommends that developer do not use constants with the convention __NAME__. Those are reserved for PHP future use.

For example, __TRAIT__ recently appeared in PHP, as a magic constant. In the future, other may appear.

<?php

const __MY_APP_CONST__ = 1;

const __MY_APP_CONST__ = 1;

define('__MY_OTHER_APP_CONST__', 2);

?>

The analyzer will report any constant which name is __.*.__, or even _.*_ (only one underscore).

See also Constants.

9.57.1. Suggestions

  • Avoid using names that doesn’t comply with PHP’s convention
Short name Constants/BadConstantnames
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)
Examples PrestaShop, Zencart

9.58. Bad Typehint Relay

A bad typehint relay happens where a type hinted argument is relayed to a parameter with another typehint. This will lead to a Fatal error, and stop the code. This is possibly a piece of dead code.

<?php

// the $i argument is relayed to bar, which is expecting a string.
function foo(int $i) : string {
    return bar($i);
}

// the return value for the bar function is not compatible with the one from foo;
function bar(string $s) : int {
    return (int) $string + 1;
}

?>

It is recommended to harmonize the typehint, so the two functions are still compatible.

9.58.1. Suggestions

  • Harmonize the typehint so they match one with the other.
  • Remove dead code
  • Apply type casting before calling the next function, or return value
Short name Functions/BadTypehintRelay
Rulesets Typechecks
Severity Minor
Time To Fix Quick (30 mins)

9.59. Bail Out Early

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

The main benefit is to make clear the method applies a condition, and stop immediately when it is not satisfied. The main sequence is then focused on the actual 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;
}

// Make a return early, and make the condition visible.
function foo3($a) {
    if ($a < 0) {
        $a++;
        methodcall();
        functioncall();
    }
}

?>

See also Avoid nesting too deeply and return early (part 1) and Avoid nesting too deeply and return early (part 2).

9.59.1. Suggestions

  • Detect errors, and then, return as soon as possible.
  • When a if…then branches are unbalanced, test for the small branch, finish it with return. Then keep the other branch as the main code.
Short name Structures/BailOutEarly
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)
Examples OpenEMR, opencfp

9.60. Binary Glossary

List of all the integer values using the binary format.

<?php

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

?>
Short name Type/Binary
Rulesets CompatibilityPHP53
Php Version With PHP 5.4 and more recent
Severity Major
Time To Fix Quick (30 mins)

9.61. 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-negligible 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.

Short name Structures/Bracketless
Rulesets Coding Conventions
Severity Minor
Time To Fix Instant (5 mins)

9.62. Break Outside Loop

Starting with PHP 7, break or continue that are outside a loop (for, foreach(), do…`while() <https://www.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
        }
    }
?>
Short name Structures/BreakOutsideLoop
Rulesets Analyze, CompatibilityPHP70
Php Version With PHP 7.0 and older
Severity Major
Time To Fix Slow (1 hour)

9.63. 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;
        }
    }

?>
Short name Structures/Break0
Rulesets CompatibilityPHP53
Php Version With PHP 5.4 and older
Severity Minor
Time To Fix Quick (30 mins)

9.64. 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;
        }
    }

?>
Short name Structures/BreakNonInteger
Rulesets CompatibilityPHP54
Php Version With PHP 5.4 and older
Severity Minor
Time To Fix Quick (30 mins)

9.65. 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);

// Display property p immeiately, but also, keeps the object for later
echo ($o = new x)->p;

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

}
?>

9.65.1. Suggestions

  • Extract the assignation and set it on its own line, prior to the current expression.
  • Check if the local variable is necessary
Short name Structures/BuriedAssignation
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)
Examples XOOPS, Mautic

9.66. Cache Variable Outside Loop

Avoid recalculating constant values inside the loop.

Do the calculation once, outside the loops, and then reuse the value each time.

One of the classic example if doing count($array) in a for loop : since the source is constant during the loop, the result of count() is always the same.

<?php

$path = '/some/path';
$fullpath = realpath("$path/more/dirs/");
foreach($files as $file) {
    // Only moving parts are used in the loop
    copy($file, $fullpath.$file);
}

$path = '/some/path';
foreach($files as $file) {
    // $fullpath is calculated each loop
    $fullpath = realpath("$path/more/dirs/");
    copy($file, $fullpath.$file);
}

?>

Depending on the load of the called method, this may increase the speed of the loop from little to enormously.

9.66.1. Suggestions

  • Avoid using blind variables outside loops.
  • Store blind variables in local variables or properties for later reuse.
Short name Performances/CacheVariableOutsideLoop
Rulesets Performances

9.67. Callback Needs Return

When used with array_map() functions, the callback must return something. This return may be in the form of a return statement, a global variable or a parameter with a reference. All those solutions extract information from the callback.

<?php

// This filters each element
$filtered = array_filter($array, function ($x) {return $x == 2; });

// This return void for every element
$filtered = array_filter($array, function ($x) {return ; });

// costly array_sum()
$sum = 0;
$filtered = array_filter($array, function ($x) use (&$sum) {$sum += $x; });

// costly array_sum()
global $sum = 0;
$filtered = array_filter($array, function () {global $sum; $sum += $x; });

// register_shutown_function() doesn't require any return
register_shutown_function(my_shutdown);

?>

The following functions are omitted, as they don’t require the return :

See also array_map.

9.67.1. Suggestions

  • Add an explicit return to the callback
  • Use null to unset elements in an array without destroying the index
Short name Functions/CallbackNeedsReturn
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Instant (5 mins)
Examples Contao, Phpdocumentor

9.68. 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);

?>

9.68.1. Suggestions

  • Make the signature of the called method accept references
  • Remove the reference from the method call
  • Use an object instead of a scalar
Short name Structures/CalltimePassByReference
Rulesets CompatibilityPHP54
Php Version With PHP 5.4 and older
Severity Minor
Time To Fix Quick (30 mins)

9.69. Can’t Count Non-Countable

Count() emits an error when it tries to count scalars or objects what don’t implement Countable interface.

<?php

// Normal usage
$a = array(1,2,3,4);
echo count($a).items\n;

// Error emiting usage
$a = '1234';
echo count($a).chars\n;

// Error emiting usage
echo count($unsetVar).elements\n;

?>

See also Warn when counting non-countable types.

Short name Structures/CanCountNonCountable
Rulesets CompatibilityPHP72
Severity Major
Time To Fix Quick (30 mins)

9.70. 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 {

    }
?>

See also Final Keyword.

9.70.1. Suggestions

  • Remove the final keyword
  • Remove the extending class
Short name Classes/CantExtendFinal
Rulesets Analyze, Dead code
Severity Critical
Time To Fix Instant (5 mins)

9.71. Can’t Throw Throwable

Classes extending Throwable can’t be thrown. The same applies to interfaces.

Although this code lints, PHP throws a Fatal error when executing or including it : Class fooThrowable cannot implement interface `Throwable <https://www.php.net/manual/en/class.throwable.php>`_, extend Exception or Error instead.

<?php

// This is the way to go
class fooException extends \Exception { }

// This is not possible and a lot of work
class fooThrowable implements \throwable { }

?>

See also Throwable, Exception and Error.

9.71.1. Suggestions

  • Extends the Exception class
  • Extends the Error class
Short name Exceptions/CantThrow
Rulesets Analyze, LintButWontExec
Severity Minor
Time To Fix Slow (1 hour)

9.72. Cancel Common Method

A parent method’s is too little used in children.

The parent class has a method, which is customised in children classes, though most of the time, those are empty : hence, cancelled.

<?php

class x {
    abstract function foo();
    abstract function bar();
}

class y1 extends x {
    function foo() { doSomething(); }
    function bar() { doSomething(); };
}

class y2 extends x {
    // foo is cancelled : it must be written, but has no use.
    function foo() {  }
    function bar() { doSomething(); };
}

?>

A threshold of cancelThreshold % of the children methods have to be cancelled to report the parent class. By default, it is 75 (or 3 out of 4).

9.72.1. Suggestions

  • Drop the common method, and the cancelled methods in the children
  • Fill the children’s methods with actual code
Name Default Type Description
cancelThreshold 75 integer Minimal number of cancelled methods to suggest the cancellation of the parent.
Short name Classes/CancelCommonMethod
Rulesets Suggestions, ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.73. Cant Implement Traversable

It is not possible to implement the Traversable``interface. The alternative is to implement ``Iterator or IteratorAggregate.

Traversable may be useful when used with instanceof.

<?php

// This lints, but doesn't run
class x implements Traversable {

}

if( $argument instanceof Traversable ) {
    // doSomething
}

?>

See also Traversable, Iterator and IteratorAggregate..

9.73.1. Suggestions

  • Implement Iterator or IteratorAggregate
Short name Interfaces/CantImplementTraversable
Rulesets Analyze, LintButWontExec, CI-checks
Severity Minor
Time To Fix Quick (30 mins)

9.74. Cant Inherit Abstract Method

Inheriting abstract methods was made available in PHP 7.2. In previous versions, it emitted 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.

9.74.1. Suggestions

  • Avoid inheriting abstract methods for compatibility beyond 7.2 (and older)
Short name Classes/CantInheritAbstractMethod
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.2 and more recent
Severity Critical
Time To Fix Quick (30 mins)

9.75. Cant Instantiate Class

When constructor is not public, it is not possible to instantiate such a class. Either this is a conception choice, or there are factories to handle that. Either way, it is not possible to call new on such class.

PHP reports an error similar to this one : ‘Call to private Y::__construct() from invalid context’.

<?php

//This is the way to go
$x = X::factory();

//This is not possible
$x = new X();

class X {
    //This is also the case with proctected __construct
    private function __construct() {}

    static public function factory() {
        return new X();
    }
}

?>

See also In a PHP5 class, when does a private constructor get called?, Named Constructors in PHP and PHP Constructor Best Practices And The Prototype Pattern.

Short name Classes/CantInstantiateClass
Rulesets Analyze
Severity Critical
Time To Fix Quick (30 mins)
Examples WordPress

9.76. Cant Use Return Value In Write Context

empty() used to work only on data containers, such as variables. Until PHP 5.5, it was not possible to use directly expressions, such as functioncalls, 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.

See also Cant Use Return Value In Write Context.

Short name Php/CantUseReturnValueInWriteContext
Rulesets CompatibilityPHP53, CompatibilityPHP54
Php Version With PHP 5.5 and more recent
Severity Major
Time To Fix Quick (30 mins)

9.77. Case Insensitive Constants

PHP constants may be case insensitive, when defined with define() and the third argument.

This feature is deprecated since PHP 7.3 and will be removed in PHP 8.0.

<?php

// case sensitive
define('A', 1);

// case insensitive
define('B', 1, true);

echo A;
// This is not possible
//echo a;

// both possible
echo B;
echo b;

?>

See also define.

Short name Constants/CaseInsensitiveConstants
Rulesets CompatibilityPHP73
Severity Critical
Time To Fix Slow (1 hour)

9.78. 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';

// This also works with straight booleans expressions
$variable = $condition == 'met' ? true : false;
// Same as
$variable = $condition == 'met';

?>

9.78.1. Suggestions

  • Remove the old expression and use (bool) operator instead
  • Change the target values from true/false, or 0/1 to non-binary values, like strings or integers beyond 0 and 1.
  • Complete the current branches with other commands
Short name Structures/CastToBoolean
Rulesets Analyze
Severity Minor
Time To Fix Instant (5 mins)
Examples MediaWiki, Dolibarr

9.79. Cast Unset Usage

Usage of the (unset) cast operator. It is removed in PHP 8.0, and was deprecated since PHP 7.2.0.

<?php

$a = 1;
(unset) $a;

// functioncall is OK
unset($a);

?>

See also Unset casting.

9.79.1. Suggestions

  • Replace (unset) with a call to unset().
  • Remove the unset call altogether.
Short name Php/CastUnsetUsage
Rulesets CompatibilityPHP80
Php Version 8.0-
Severity Minor
Time To Fix Quick (30 mins)

9.80. Casting Ternary

Type casting has a precedence over ternary operator, and is applied first. When this happens, the condition is cast, although it is often useless as PHP will do it if needed.

This applies to the ternary operator, the coalesce operator ?: and the null-coalesce operator ??.

<?php
    $a = (string) $b ? 3 : 4;
    $a = (string) $b ?: 4;
    $a = (string) $b ?? 4;
?>

The last example generates first an error Undefined variable: b, since $b is first cast to a string. The result is then an empty string, which leads to an empty string to be stored into $a. Multiple errors cascade.

See also Operators Precedence.

9.80.1. Suggestions

  • Add parenthesis around the ternary operator
  • Skip the casting
  • Cast in another expression
Short name Structures/CastingTernary
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Quick (30 mins)

9.81. Catch Overwrite Variable

The try/catch structure uses some variables that are 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.

9.81.1. Suggestions

  • Use a standard : only use $e (or else) to catch exceptions. Avoid using them for anything else, parameter, property or local variable.
  • Change the variable, and keep the caught exception
Short name Structures/CatchShadowsVariable
Rulesets Analyze
Severity Minor
Time To Fix Instant (5 mins)
ClearPHP no-catch-overwrite
Examples PhpIPAM, SuiteCrm

9.82. Catch Undefined Variable

Always initialize variable before the try block, when they are used in a catch block. If the exception is raised before the variable is defined, the catch block may have to handle an undefined variable, leading to more chaos.

<?php

$a = 1;
try {
    mayThrowAnException();
    $b = 2;
} catch (\Exception $e) {
    // $a is already defined, as it was done before the try block
    // $b may not be defined, as it was initialized after the exception-throwing expression
    echo $a + $b;
}

?>

9.82.1. Suggestions

  • Always define the variable used in the catch clause, before the try block.
Short name Exceptions/CatchUndefinedVariable
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.83. Check All Types

When checking for time, avoid using else. Mention explicitly 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_string() / 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.

9.83.1. Suggestions

  • Include a default case to handle all unknown situations
  • Include and process explicit types as much as possible
Short name Structures/CheckAllTypes
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)
Examples Zend-Config, Vanilla

9.84. Check Crypto Key Length

Each cryptography algorithm requires a reasonable length. Make sure an up-to-date length is used.

This rule use the following recommendations :

  • OPENSSL_KEYTYPE_RSA’ => 3072
  • OPENSSL_KEYTYPE_DSA’ => 2048
  • OPENSSL_KEYTYPE_DH’ => 2048
  • OPENSSL_KEYTYPE_EC’ => 512

The values above are used with the openssl PHP extension.

<?php

// Extracted from the documentation

// Generates a new and strong key
$private_key = openssl_pkey_new(array(
    private_key_type => OPENSSL_KEYTYPE_EC,
    private_key_bits => 1024,
));

// Generates a new and weak key
$private_key = openssl_pkey_new(array(
    private_key_type => OPENSSL_KEYTYPE_EC,
    private_key_bits => 256,
));

?>

See also The Definitive 2019 Guide to Cryptographic Key Sizes and Algorithm Recommendations and Cryptographic Key Length Recommendation.

9.84.1. Suggestions

Short name Security/CryptoKeyLength
Rulesets Security
Severity Minor
Time To Fix Quick (30 mins)

9.85. Check JSON

Check errors whenever JSON is encoded or decoded.

In particular, NULL is a valid decoded JSON response. If you want to avoid mistaking NULL for an error, it is recommended to call json_last_error.

<?php

$encoded = json_encode($incoming);
// Unless JSON must contains some non-null data, this mistakes NULL and error
if(json_last_error() != JSON_ERROR_NONE) {
    die('Error when encoding JSON');
}

$decoded = json_decode($incoming);
// Unless JSON must contains some non-null data, this mistakes NULL and error
if($decoded === null) {
    die('ERROR');
}

?>

See also Option to make json_encode and json_decode throw exceptions on errors, json_last_error.

9.85.1. Suggestions

  • Always check after JSON operation : encoding or decoding.
  • Add a call to json_last_error()
  • Configure operations to throw an exception upon error (JSON_THROW_ON_ERROR), and catch it.
Short name Structures/CheckJson
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Quick (30 mins)
Examples Woocommerce

9.86. Check On __Call Usage

When using the magic methods __call() and __staticcall(), make sure the method exists before calling it.

If the method doesn’t exists, then the same method will be called again, leading to the same failure. Finally, it will crash PHP.

<?php

class safeCall {
    function __class($name, $args) {
        // unsafe call, no checks
        if (method_exists($this, $name)) {
            $this->$name(...$args);
        }
    }
}

class unsafeCall {
    function __class($name, $args) {
        // unsafe call, no checks
        $this->$name(...$args);
    }
}

?>

See also Method overloading and ``Magical PHP: __call <https://www.garfieldtech.com/index.php/blog/magical-php-call>`_.

9.86.1. Suggestions

  • Add a call to method_exists() before using any method name
  • Relay the call to another object that doesn’t handle __call() or __callStatic()
Short name Classes/CheckOnCallUsage
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Quick (30 mins)

9.87. Child Class Removes Typehint

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

<?php

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

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

?>
Short name Classes/ChildRemoveTypehint
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, Typechecks
Php Version With PHP 7.2 and more recent
Severity Major
Time To Fix Quick (30 mins)

9.88. 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.

Short name Php/ClassConstWithArray
Rulesets CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55
Php Version With PHP 5.5 and more recent
Severity Critical
Time To Fix Slow (1 hour)

9.89. Class Could Be Final

Any class that has no extension should be final by default.

As stated by Matthias Noback : If a class is not marked final, it has at least one subclass.

Prevent your classes from being subclassed by making them final. Sometimes, classes are not meant or thought to be derivable.

<?php

class x {}            // This class is extended
class y extends x {}  // This class is extended
class z extends y {}  // This class is not extended

final class z2 extends y {}  // This class is not extended

?>

See also Negative architecture, and assumptions about code.

9.89.1. Suggestions

  • Make the class final
  • Extends the class
Short name Classes/CouldBeFinal
Rulesets Analyze, ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.90. 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();

?>

9.90.1. Suggestions

  • Use a naming convention to distinguish functions and classes
  • Rename the class or the function (or both)
  • Use an alias with a use expression
Short name Php/ClassFunctionConfusion
Rulesets Semantics
Severity Minor
Time To Fix Slow (1 hour)

9.91. 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 Ocramius (Marco Pivetta).

<?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 () {}
}

?>

See also When to declare classes final.

Short name Classes/FinalByOcramius
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)

9.92. Class Without Parent

Classes should not refer to parent when it is not extending another class.

In PHP 7.4, it is a Deprecated warning. In PHP 7.3, it was a Fatal error, when the code was finally executed.

<?php

class x {
    function foo() {
        parent::foo();
    }
}
?>

9.92.1. Suggestions

  • Update the class and make it extends another class
  • Change the parent mention with a fully qualified name
  • Remove the call to the parent altogether
Short name Classes/NoParent
Rulesets Analyze, ClassReview, CI-checks
Php Version 7.4-
Severity Minor
Time To Fix Quick (30 mins)

9.93. Class, Interface Or Trait With Identical Names

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

<?php
    class a     { /* some definitions */ }
    interface a { /* some definitions */ }
    trait a     { /* some definitions */ }
?>

Even if they are in different namespaces, identical names makes classes easy to confuse. This is often solved by using alias at import time : this leads to more confusion, as a class suddenly changes its name.

Internally, PHP use the same list for all classes, interfaces and traits. As such, it is not allowed to have both a trait and a class with the same name.

In PHP 4, and PHP 5 before namespaces, it was not possible to have classes with the same name. They were simply included after a check.

9.93.1. Suggestions

  • Use distinct names for every class, trait and interface.
  • Keep eponymous classes, traits and interfaces in distinct files, for definition but also for usage. When this happens, rename one of them.
Short name Classes/CitSameName
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)
Examples shopware, NextCloud

9.94. 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 { }

?>
Short name Classes/MutualExtension
Rulesets LintButWontExec, ClassReview
Severity Major
Time To Fix Quick (30 mins)

9.95. Clone With Non-Object

The clone keyword must be used on variables, properties or results from a function or method call.

clone cannot be used with constants or literals.

<?php

class x { }
$x = new x();

// Valid clone
$y = clone $x;

// Invalid clone
$y = clone x;

?>

Cloning a non-object lint but won’t execute.

See also Object cloning.

9.95.1. Suggestions

  • Only clone containers (like variables, properties…)
  • Add typehint to injected properties, so they are checked as objects.
Short name Classes/CloneWithNonObject
Rulesets Analyze, LintButWontExec
Severity Minor
Time To Fix Quick (30 mins)

9.96. 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.

Short name Php/CloseTags
Rulesets Coding Conventions
Severity Minor
Time To Fix Instant (5 mins)
ClearPHP leave-last-closing-out

9.97. Closure Could Be A Callback

Closure or arrowfunction could be simplified to a callback. Callbacks are strings or arrays.

A simple closure that only returns arguments relayed to another function or method, could be reduced to a simpler expression. They

Closure may be simplified with a string, for functioncall, with an array for methodcalls and static methodcalls.

Performances : simplifying a closure tends to reduce the call time by 50%.

<?php

// Simple and faster call to strtoupper
$filtered = array_map('strtoupper', $array);

// Here the closure doesn't add any feature over strtoupper
$filtered = array_map(function ($x) { return strtoupper($x);}, $array);

// Methodcall example : no fix
$filtered = array_map(function ($x) { return $x->strtoupper() ;}, $array);

// Methodcall example  : replace with array($y, 'strtoupper')
$filtered = array_map(function ($x) use ($y) { return $y->strtoupper($x) ;}, $array);

// Static methodcall example
$filtered = array_map(function ($x) { return $x::strtoupper() ;}, $array);

// Static methodcall example   : replace with array('A', 'strtoupper')
$filtered = array_map(function ($x) { return A::strtoupper($x) ;}, $array);

?>

See also Closure class and Callbacks / Callables.

9.97.1. Suggestions

  • Replace the closure by a string, with the name of the called function
  • Replace the closure by an array, with the name of the called method and the object as first element
Short name Functions/Closure2String
Rulesets Suggestions, Performances
Severity Minor
Time To Fix Quick (30 mins)
Examples Tine20, NextCloud

9.98. Closure May Use $this

$this is automatically accessible to closures.

When closures 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 Anonymous functions.

Short name Php/ClosureThisSupport
Rulesets CompatibilityPHP53
Php Version With PHP 5.4 and older
Severity Minor
Time To Fix Quick (30 mins)

9.99. Coalesce And Concat

The concatenation operator dot has precedence over the coalesce operator ??.

<?php

// Parenthesis are the right solution when in doubt
echo a . ($b ?? 'd') . $e;

// 'a' . $b is evaluated first, leading ot a useless ?? operator
'a' . $b ?? $c;

// 'd' . 'e' is evaluated first, leading to $b OR 'de'.
echo $b ?? 'd' . 'e';

?>

9.99.1. Suggestions

  • Add parenthesis around ?? operator to avoid misbehavior
  • Do not use dot and ?? together in the same expression
Short name Structures/CoalesceAndConcat
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Quick (30 mins)

9.100. Coalesce Equal

Usage of coalesce assignement operator. The operator is available in PHP since PHP 7.4.

<?php

// Coalesce operator, since PHP 5.3
$a ??= 'default value';

// Equivalent to $a = $a ?? 'default value';

?>

See also Ternary Operator.

Short name Php/CoalesceEqual
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.4 and more recent

9.101. 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++;
}

?>

9.101.1. Suggestions

  • Collect common expressions, and move them before of after the if/then expression.
  • Move a prefix and suffixes to a third-party method
Short name Structures/CommonAlternatives
Rulesets Analyze
Severity Major
Time To Fix Instant (5 mins)
Examples Dolibarr, NextCloud

9.102. Compact Inexistant Variable

Compact() doesn’t warn when it tries to work on an inexistant variable. It just ignores the variable.

This behavior changed in PHP 7.3, and compact() now emits a warning when the compacted variable doesn’t exist.

<?php

function foo($b = 2) {
    $a = 1;
    // $c doesn't exists, and is not compacted.
    return compact('a', 'b', 'c');
}
?>

For performances reasons, this analysis only works inside methods and functions.

See also compact and PHP RFC: Make compact function reports undefined passed variables.

9.102.1. Suggestions

  • Fix the name of variable in the compact() argument list
  • Remove the name of variable in the compact() argument list
Short name Php/CompactInexistant
Rulesets Suggestions, CompatibilityPHP73
Severity Major
Time To Fix Quick (30 mins)

9.103. Compare Hash

When comparing hash values, it is important to use the strict comparison : hash_equals(), === 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.'.PHP_EOL;
}

// hash returns a string, that is mistaken with 0 by PHP
// The strength of the hashing algorithm is not a problem
if (hash('ripemd160','20583002034',false) == '0') {
  print 'Matched.'.PHP_EOL;
}

if (hash('md5','240610708',false) !== $hashed_password) {
  print 'NOT Matched.'.PHP_EOL;
}

// Display true
var_dump(md5('240610708') == md5('QNKCDZO') );

?>

You may also use password_hash() and password_verify() : they work together without integer conversion problems, and they can’t be confused with a number.

See also Magic Hashes What is the best way to compare hashed strings? (PHP) and md5(‘240610708’) == md5(‘QNKCDZO’).

9.103.1. Suggestions

  • Use dedicated functions for hash comparisons
  • Use identity operators (===), and not equality operators (==) to compare hashes
  • Compare hashes in the database (or external system), where such confusion is not possible
Short name Security/CompareHash
Rulesets Security
Severity Major
Time To Fix Quick (30 mins)
ClearPHP strict-comparisons
Examples Traq, LiveZilla

9.104. 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.

<?php

if ($a === strpos($string, $needle) > 2) {}

// the expression above apply precedence :
// it is equivalent to :
if (($a === strpos($string, $needle)) > 2) {}

?>

See also Operators Precedence.

Short name Structures/ComparedComparison
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)

9.105. Complex Dynamic Names

Avoid using expressions as names for variables or methods.

There are no place for checks or flow control, leading to any rogue value to be used as is. Besides, the expression is often overlooked, and not expected there : this makes the code less readable.

It is recommended to build the name in a separate variable, apply the usual checks for existence and validity, and then use the name.

<?php

$a = new foo();

// Code is more readable
$name = strolower($string);
if (!property_exists($a, $name)) {
    throw new missingPropertyexception($name);
}
echo $a->$name;

// This is not check
echo $a->{strtolower($string)};

?>

This analysis only accept simple containers, such as variables, properties.

See also Dynamically Access PHP Object Properties with `$this <https://drupalize.me/blog/201508/dynamically-access-php-object-properties>`_.

9.105.1. Suggestions

  • Extract the expression from the variable syntax, and make it a separate variable.
Short name Variables/ComplexDynamicNames
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)

9.106. Concat And Addition

Precedence between addition and concatenation has changed. In PHP 7.4, addition has precedence, and before, addition and concatenation had the same precedence.

From the RFC : Currently the precedence of '.', '+' and '-' operators are equal. Any combination of these operators are simply evaluated left-to-right.

This is counter-intuitive though: you rarely want to add or subtract concatenated strings which in general are not numbers. However, given PHP’s capability of seamlessly converting an integer to a string, concatenation of these values is desired.``

<?php
// Extracted from the RFC
echo sum: . $a + $b;

// current behavior: evaluated left-to-right
echo (sum: . $a) + $b;

// desired behavior: addition and subtraction have a higher precendence
echo sum : . ($a + $b);

?>

This analysis reports any addition and concatenation that are mixed, without parenthesis. Addition also means substraction here, aka using + or -.

The same applies to bitshift operations, << and >>. There is no RFC for this change.

See also Change the precedence of the concatenation operator.

9.106.1. Suggestions

  • Add parenthesis around the addition to ensure its expected priority
  • Move the addition outside the concatenation
Short name Php/ConcatAndAddition
Rulesets Analyze, CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73, CompatibilityPHP54, CompatibilityPHP74, CompatibilityPHP80, CompatibilityPHP55, CompatibilityPHP56, Top10, CI-checks
Severity Minor
Time To Fix Quick (30 mins)

9.107. Concat Empty String

Using a concatenation to make a value a string should be replaced with a type cast.

Type cast to a string is done with (string) operator. There is also the function strval(), although it is less recommended.

<?php

$a = 3;

// explicite way to cast a value
$b = (string) $a; // $b is a string with the content 3

// Wrong way to cast a value
$c = $a . ''; // $c is a string with the content 3
$c = '' . $a; // $c is a string with the content 3
$a .= '';     // $a is a string with the content 3

// Wrong way to cast a value
$c = $a . '' . $b; // This is not reported. The empty string is useless, but not meant to type cast

?>

See also Type Casting and PHP Type Casting.

9.107.1. Suggestions

  • Avoid concatenating with empty strings
  • Use (string) operator to cast to string
  • Remove any concatenated empty string
Short name Structures/ConcatEmpty
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.108. Concrete Visibility

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

PHP does lint this, unless the interface and the class are in the same file. At execution, it stops immediately 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.

9.108.1. Suggestions

  • Always set interface methods to public.
Short name Interfaces/ConcreteVisibility
Rulesets Analyze, LintButWontExec
Severity Major
Time To Fix Instant (5 mins)

9.109. Configure Extract

The extract() function overwrites local variables when left unconfigured.

Extract imports variables from an array into the local scope. In case of a conflict, that is when a local variable already exists, it overwrites the previous variable.

In fact, extract() may be configured to handle the situation differently : it may skip the conflicting variable, prefix it, prefix it only if it exists, only import overwriting variables… It may also import them as references to the original values.

This analysis reports extract() when it is not configured explicitly. If overwriting is the intended objective, it is not reported.

<?php

// ignore overwriting variables
extract($array, EXTR_SKIP);

// prefix all variables explicitly variables with 'php_'
extract($array, EXTR_PREFIX_ALL, 'php_');

// overwrites explicitly variables
extract($array, EXTR_OVERWRITE);

// overwrites implicitely variables : do we really want that?
extract($array, EXTR_OVERWRITE);

?>

Always avoid using extract() on untrusted sources, such as $_GET, $_POST, $_FILES, or even databases records.

See also extract.

9.109.1. Suggestions

  • Always use the second argument of extract(), and avoid using EXTR_OVERWRITE
Short name Security/ConfigureExtract
Rulesets Security
Severity Minor
Time To Fix Instant (5 mins)
Examples Zurmo, Dolibarr

9.110. Const Visibility Usage

Visibility for class constant controls the accessibility to class constant.

A public constant may be used anywhere in the code; a protected constant usage is restricted to the class and its relatives; a private constant is restricted to itself.

This feature was introduced in PHP 7.1. It is recommended to use explicit visibility, and, whenever possible, make the visibility private.

<?php

class x {
    public const a = 1;
    protected const b = 2;
    private const c = 3;
    const d = 4;
}

interface i {
    public const a = 1;
      const d = 4;
}

?>

See also Class Constants and PHP RFC: Support Class Constant Visibility.

9.110.1. Suggestions

  • Add constant visibility, at least ‘public’.
Short name Classes/ConstVisibilityUsage
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.1 and more recent
Severity Minor
Time To Fix Slow (1 hour)

9.111. Const With Array

The const keyword supports array. This feature was added in PHP 5.6.

The array must be filled with other constants. It may also be build using the ‘+’ operator.

<?php

const PRIMES = [2, 3, 5, 7];

class X {
    const TWENTY_THREE = 23;
    const MORE_PRIMES = PRIMES + [11, 13, 17, 19];
    const EVEN_MORE_PRIMES = self::MORE_PRIMES + [self::TWENTY_THREE];
}

?>

See also Class Constants and Constants Syntax.

Short name Php/ConstWithArray
Rulesets CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55
Php Version With PHP 5.5 and more recent
Severity Major
Time To Fix Slow (1 hour)

9.112. 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.

<?php

class ConstantClass {
    const KBIT = 1000;
    const MBIT = self::KBIT * 1000;
    const GBIT = self::MBIT * 1000;
    const PBIT = self::GBIT * 1000;
}

?>

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

See also PHP Classes containing only constants.

9.112.1. Suggestions

  • Make the class an interface
  • Make the class an abstract class, to avoid its instantiation
Short name Classes/ConstantClass
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Slow (1 hour)

9.113. 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) {}

?>
Short name Structures/ConstantComparisonConsistance
Rulesets Coding Conventions
Severity Minor
Time To Fix Quick (30 mins)

9.114. Constant Scalar Expressions

Define constant with the result of static expressions. This means that constants may be defined with the const keyword, with the help of various operators but without any functioncalls.

This feature was introduced in PHP 5.6. It also supports array(), and expressions in arrays.

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

<?php

// simple definition
const A = 1;

// constant scalar expression
const B = A * 3;

// constant scalar expression
const C = [A ** 3, '3' => B];

?>

See also Constant Scalar Expressions.

Short name Structures/ConstantScalarExpression
Rulesets CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55
Php Version With PHP 5.6 and more recent
Severity Major
Time To Fix Quick (30 mins)

9.115. 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.

Short name Constants/CreatedOutsideItsNamespace
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)

9.116. 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

?>

See also PHP Constants.

Short name Constants/ConstantStrangeNames
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Slow (1 hour)

9.117. Continue Is For Loop

break and continue are very similar in PHP : they both break out of loop or switch. Yet, continue should be reserved for loops.

Since PHP 7.3, the execution will emit a warning when finding a continue inside a switch inside a loop : ‘”continue” targeting switch is equivalent to “break”. Did you mean to use “continue 2”?’

<?php

while ($foo) {
    switch ($bar) {
        case 'baz':
            continue; // In PHP: Behaves like 'break;'
                      // In C:   Behaves like 'continue 2;'
    }
}

?>

See also Deprecate and remove `continue targeting switch <https://wiki.php.net/rfc/continue_on_switch_deprecation>`_.

9.117.1. Suggestions

  • Replace break by continue
Short name Structures/ContinueIsForLoop
Rulesets Analyze, CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP73
Severity Minor
Time To Fix Quick (30 mins)
Examples XOOPS

9.118. Could Be Abstract Class

An abstract class is never instantiated, and has children class that are. As such, a ‘parent’ class that is never instantiated by itself, but has its own children instantiated could be marked as abstract.

That will prevent new code to try to instantiate it.

<?php

// Example code would actually be split over multiple files.


// That class could be abstract
class motherClass {}

// Those classes shouldn't be abstract
class firstChildren extends motherClass {}
class secondChildren extends motherClass {}
class thirdChildren extends motherClass {}

new firstChildren();
new secondChildren();
new thirdChildren();

//Not a single : new motherClass()

?>

See also Class Abstraction Abstract classes and methods.

9.118.1. Suggestions

  • Make this class an abstract class
Short name Classes/CouldBeAbstractClass
Rulesets Analyze, ClassReview
Severity Minor
Time To Fix Quick (30 mins)
Examples Edusoho, shopware

9.119. Could Be Callable

Mark arguments and return types that can be set to callable.

<?php

// Accept a callable as input
function foo($b) {
    // Returns value as return
    return $b();
}

?>

9.119.1. Suggestions

  • Add callable typehint to the code.
Short name Typehints/CouldBeCallable
Rulesets Typechecks
Severity Major
Time To Fix Quick (30 mins)
Precision High

9.120. 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.

Short name Classes/CouldBeClassConstant
Rulesets ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.121. Could Be Constant

Literals may be replaced by an existing constant.

Constants makes the code easier to read, as they may bear a meaningful name. They also hide implementation values, with a readable name, such as const READABLE= true;. Later, upgrading constant values is easier than scouring the code with a new literal.

Not all literal can be replaced by a constant values : sometimes, literal may have the same literal value, but different meanings. Check with your application semantics before changing any literal with a constant.

<?php

const A = 'abc';
define('B', 'ab');

class foo {
    const X = 'abcd';
}

// Could be replaced by B;
$a = 'ab';

// Could be replaced by A;
$a = 'abc';

// Could be replaced by foo::X;
$a = 'abcd';

?>

This analysis currently doesn’t support arrays.

This analysis also skips very common values, such as boolean, 0 and 1. This prevents too many false positive.

9.121.1. Suggestions

  • Turn the literal into an existing constant
Short name Constants/CouldBeConstant
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)

9.122. 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;
}

?>

9.122.1. Suggestions

  • Merge the two conditions into one structure
  • Check if the second condition is still applicable
Short name Structures/CouldBeElse
Rulesets Analyze
Severity Minor
Time To Fix Instant (5 mins)
Examples SugarCrm, OpenEMR

9.123. Could Be Float

Mark arguments, properties and return types that can be set to float.

<?php

// Accept an int as input
function foo($b) {
    // Returns a float (cubic root of $b);
    return pow($b, 1 / 3);
}

?>

9.123.1. Suggestions

  • Add float typehint to the code.
Short name Typehints/CouldBeFloat
Rulesets Typechecks
Severity Major
Time To Fix Quick (30 mins)
Precision High

9.124. Could Be Integer

Mark arguments, properties and return types that can be set to int.

<?php

// Accept an int as input
function foo($b) {
    // Returns an int
    return $b + 8;
}

?>

9.124.1. Suggestions

  • Add int typehint to the code.
Short name Typehints/CouldBeInt
Rulesets Typechecks
Severity Major
Time To Fix Quick (30 mins)
Precision High

9.125. Could Be Iterable

Mark arguments, properties and return types that can be set to iterable.

<?php

// Accept an array or a traversable Object as input
function foo($b) {
    foreach($b as $c) {

    }

    // Returns an array
    return [$b];
}

?>

9.125.1. Suggestions

  • Add iterable typehint to the code (PHP 8.0+).
Short name Typehints/CouldBeIterable
Rulesets Typechecks
Severity Major
Time To Fix Quick (30 mins)
Precision High

9.126. Could Be Null

Mark arguments and return types that can be null.

<?php

// Accept null as input, when used as third argument of file_get_contents
function foo($b) {
    $s = file_get_contents(URL, false, $b);

    // Returns a string
    return shell_exec($s);
}

?>

9.126.1. Suggestions

  • Add null typehint to the code (PHP 8.0+).
  • Add ? typehint to the code.
Short name Typehints/CouldBeNull
Rulesets Typechecks
Severity Major
Time To Fix Quick (30 mins)
Precision High

9.127. Could Be Parent

Mark arguments, return types and properties that can be set to parent.

This analysis works when typehints have already been configured.

<?php

class x extends w {
    // Accept a w object as input
    function foo(w $b) : w {
        // Returns a w object
        return $b;
    }
}

?>

9.127.1. Suggestions

  • Add parent typehint to the code.
  • Add the literal class/type typehint to the code.
Short name Typehints/CouldBeParent
Rulesets Typechecks
Severity Major
Time To Fix Quick (30 mins)
Precision High

9.128. Could Be Parent Method

A method is defined in several children, but not in a the parent class. It may be worth checking if this method doesn’t belong the parent class, as an abstraction.

<?php

// The parent class
class x { }

// The children class
class y1 extends x {
    // foo is common to y1 and y2, so it shall be also a method in x
    function foo() {}
    // fooY1 is specific to y1
    function fooY1() {}
}

class y2 extends x {
    function foo() {}
    // fooY2 is specific to y1
    function fooY2() {}
}

?>

Only the name of the method is used is for gathering purposes. If the code has grown organically, the signature (default values, typehint, argument names) may have followed different path, and will require a refactorisation.

9.128.1. Suggestions

  • Create an abstract method in the parent
  • Create an concrete method in the parent, and move default behavior there by removing it in children classes
Name Default Type Description
minChildren 4 integer Minimal number of children using this method.
Short name Classes/CouldBeParentMethod
Rulesets ClassReview
Severity Minor
Time To Fix Slow (1 hour)

9.129. 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;
}

?>

Constant shall stay public when the code has to be compatible with PHP 7.0 and older.

They also have to be public in the case of component : some of those constants have to be used by external actors, in order to configure the component.

See also Class Constants.

Short name Classes/CouldBePrivateConstante
Rulesets ClassReview
Severity Minor
Time To Fix Quick (30 mins)
Examples Phinx

9.130. 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;
}

?>
Short name Classes/CouldBeProtectedConstant
Rulesets ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.131. 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.

Short name Classes/CouldBeProtectedMethod
Rulesets ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.132. 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.

Short name Classes/CouldBeProtectedProperty
Rulesets ClassReview
Severity Minor
Time To Fix Slow (1 hour)

9.133. Could Be Self

Mark arguments, return types and properties that can be set to self.

This analysis works when typehints have already been configured.

<?php

class x {
    // Accept a x object as input
    function foo(x $b) : x {
        // Returns a x object
        return $b;
    }
}

?>

9.133.1. Suggestions

  • Add self typehint to the code.
  • Add the literal class/type typehint to the code.
Short name Typehints/CouldBeSelf
Rulesets Typechecks
Severity Major
Time To Fix Quick (30 mins)
Precision High

9.134. 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
}
?>
Short name Structures/CouldBeStatic
Rulesets Analyze, ClassReview, Analyze, ClassReview
Severity Major
Time To Fix Quick (30 mins)
Examples Dolphin, Contao

9.135. Could Be Static Closure

Closure may be static, and prevent the import of $this.

By preventing the useless import of $this, you avoid useless work.

This also has the added value to prevent the usage of $this from the closure. This is a good security practice.

<?php

class Foo
{
    function __construct()
    {

        // Not possible to use $this
        $func = static function() {
            var_dump($this);
        };
        $func();

        // Normal import of $this
        $closure = function() {
            var_dump($this);
        };
    }
};
new Foo();

?>

This is a micro-optimisation. Apply it in case of intensive usage.

See also Anonymous functions, GeneratedHydrator and Static anonymous functions <https://www.php.net/manual/en/functions.anonymous.php#functions.anonymous-functions.`static>`_.

9.135.1. Suggestions

  • Add the static keyword to the closure.
  • Make actual usage of $this in the closure.
Short name Functions/CouldBeStaticClosure
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)
Examples Piwigo

9.136. Could Be String

Mark arguments and return types that can be set to string.

<?php

// Accept a string as input
function foo($a) {
    // Returns a string
    return $a . 'string';
}

?>

9.136.1. Suggestions

  • Choose the string typehint, and add it to the code.
Short name Typehints/CouldBeString
Rulesets Typechecks
Severity Major
Time To Fix Quick (30 mins)
Precision High

9.137. Could Be Stringable

Stringable is an interface that mark classes as string-castable. It is introduced in PHP 8.0.

Classes that defined a __toString() magic method may be turned into a string when the typehint, argument, return or property, requires it. This is not the case when strict_types is activated. Yet, until PHP 8.0, there was nothing to identify a class as such.

<?php

// This class may implement Stringable
class x {
    function __tostring() {
        return 'asd';
    }
}

echo (new x);

?>

See also PHP RFC: Add Stringable interface.

9.137.1. Suggestions

Short name Classes/CouldBeStringable
Rulesets Analyze, LintButWontExec
Php Version 8.0+
Severity Minor
Time To Fix Quick (30 mins)

9.138. Could Be Void

Mark return types that can be set to void.

<?php

// No return, this should be void.
function foo() {
    ++$a; // Not useful
}

?>

All abstract methods (in classes or in interfaces) are omitted here.

9.138.1. Suggestions

  • Add the void typehint to the code.
Short name Typehints/CouldBeVoid
Rulesets Typechecks
Severity Major
Time To Fix Quick (30 mins)
Precision High

9.139. 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);
}

?>

See also Don’t repeat yourself (DRY).

9.139.1. Suggestions

  • Create a constant for common pieces of data
  • Create a function based on context-free repeated elements
  • Create a class based on repeated elements with dependent values
Name Default Type Description
centralizeThreshold 8 integer Minimal number of calls of the function with one common argument.
Short name Functions/CouldCentralize
Rulesets Analyze, Suggestions
Severity Minor
Time To Fix Slow (1 hour)

9.140. Could Use Alias

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

This applies to classes (as full name or prefix), and to constants and functions.

<?php

use a\b\c;
use function a\b\c\foo;
use const a\b\c\D;

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

// This may be reduced to c\d\e\f
new a\b\c\d\e\f();

// This may be reduced to c()
new a\b\c();

// This may be reduced to D
echo a\b\c\D;

// This may be reduced to D
a\b\c\foo();

// This can't be reduced : it is an absolute name
\a\b\c\foo();

// This can't be reduced : it is no an alias nor a prefix
a\b\d\foo();

?>

9.140.1. Suggestions

  • Use all your aliases so as to make the code shorter and more readable
  • Add new aliases for missing path
  • Make class names absolute and drop the aliases
Short name Namespaces/CouldUseAlias
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)

9.141. Could Use Compact

Compact() turns a group of variables into an array. It may be used to simplify expressions.

<?php

$a = 1;
$b = 2;

// Compact call
$array = compact('a', 'b');

$array === [1, 2];

// Detailing all the keys and their value
$array = ['a' => $a, 'b' => $b];

?>

Note that compact accepts any string, and any undefined variable is not set, without a warning.

See also compact.

9.141.1. Suggestions

  • Replace the array() call with a compact() call.
Short name Structures/CouldUseCompact
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)
Examples WordPress

9.142. Could Use Promoted Properties

Promoted properties reduce PHP code at __construct() time. This feature is available in PHP 8.0.

<?php

class x {
    function __construct($a, $b) {
        // $a argument may be promoted to property $c
        $this->c = $a;

        // $b argument cannot be upgraded to property, as it is updated.
        // Move the addition to the new call, or keep the syntax below
        $this->d = $b + 2;
    }
}

?>

See also PHP 8: Constructor property promotion and PHP RFC: Constructor Property Promotion.

9.142.1. Suggestions

  • Update the constructor syntax, and remove the property specification.
Short name Php/CouldUsePromotedProperties
Rulesets Suggestions
Php Version 8.0+
Severity Minor
Time To Fix Quick (30 mins)

9.143. Could Use Short Assignation

Use short assignment 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;

// PHP 7.4 and more recent
$l = $l ?? 'value';
$l ??= 'value';

?>

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

See also Assignation Operators.

9.143.1. Suggestions

  • Change the expression to use the short assignation
Short name Structures/CouldUseShortAssignation
Rulesets Analyze, Performances, CI-checks
Severity Minor
Time To Fix Instant (5 mins)
ClearPHP use-short-assignations
Examples ChurchCRM, Thelia

9.144. Could Use Try

Some commands may raise exceptions. It is recommended to use the try/catch block to intercept those exceptions, and process them.

  • / : DivisionByZeroError
  • % : DivisionByZeroError
  • intdiv() : DivisionByZeroError
  • << : ArithmeticError
  • >> : ArithmeticError
  • Phar\:\:mungserver : PharException
  • Phar\:\:webphar : PharException

See also Predefined Exceptions, PharException.

9.144.1. Suggestions

  • Add a try/catch clause around those commands
  • Add a check on the values used with those operator : for example, check a dividend is not 0, or a bitshift is not negative
Short name Exceptions/CouldUseTry
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Examples Mautic

9.145. Could Use __DIR__

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

Avoid using dirname() on __FILE__.

<?php

// Better way
$fp = fopen(__DIR__.'/myfile.txt', 'r');

// compatible, but slow way
$fp = fopen(dirname(__FILE__).'/myfile.txt', 'r');

// Since PHP 5.3
assert(dirname(__FILE__) == __DIR__);

?>

__DIR__ has been introduced in PHP 5.3.0.

See also Magic Constants.

9.145.1. Suggestions

  • Use __DIR__ instead of dirname(__FILE__);
Short name Structures/CouldUseDir
Rulesets Analyze, Suggestions, php-cs-fixable, CI-checks
Severity Major
Time To Fix Quick (30 mins)
Examples Woocommerce, Piwigo

9.146. Could Use array_fill_keys

array_fill_keys() is a native PHP function that creates an array from keys. It gets the list of keys, and a constant value to assign to each keys.

This is twice faster than doing the same with a loop.

Note that is possible to use an object as initializing value : every element of the final array will be pointing to the same value. And, also, using an object as initializing value means that the same object will be used for each key : the object will not be cloned for each value.

<?php

$array = range('a', 'z');

// Fast way to build the array
$b = array_fill_keys($a, 0);

// Fast way to build the array, but every element will be the same object
$b = array_fill_keys($a, new Stdclass());

// Slow way to build the array
foreach($array as $a) {
    $b[$a] = 0;
}

// Setting everything to null, slowly
$array = array_map(function() {}, $array);

?>

See also array_fill_keys.

9.146.1. Suggestions

  • Use array_fill_keys()
Short name Structures/CouldUseArrayFillKeys
Rulesets Suggestions
Severity Minor
Time To Fix Slow (1 hour)
Examples ChurchCRM, PhpIPAM

9.147. Could Use array_unique

Use array_unique() to collect unique elements from an array.

Always try to use native PHP functions, instead of rebuilding them with custom PHP code.

<?php

    $unique = array();
    foreach ($array as $b) {
        if (!in_array($b, $unique)) {
            /*  May be more code */
            $unique[] = $b;
        }
    }
?>

See also array_unique.

9.147.1. Suggestions

  • Turn the foreach() and its condition into a call to array_unique()
  • Extract the condition from the foreach() and add a separate call to array_unique()
Short name Structures/CouldUseArrayUnique
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)
Examples Dolibarr, OpenEMR

9.148. Could Use self

self keyword refers to the current class, or any of its parents. Using it is just as fast as the full class name, 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;
    }
}

?>

See also Scope Resolution Operator (::).

9.148.1. Suggestions

  • replace the explicit name with self
Short name Classes/ShouldUseSelf
Rulesets Analyze, Suggestions, ClassReview
Severity Minor
Time To Fix Instant (5 mins)
Examples WordPress, LiveZilla

9.149. 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 significant, 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';
}

?>

9.149.1. Suggestions

  • Use strrepeat() whenever possible
Short name Structures/CouldUseStrrepeat
Rulesets Analyze, Top10, CI-checks
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Examples Zencart

9.150. Crc32() Might Be Negative

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

According to the manual : Because PHP’s integer type is signed many CRC32 checksums will result in negative integers on 32 bits platforms. On 64 bits 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().

Short name Php/Crc32MightBeNegative
Rulesets Analyze
Severity Major
Time To Fix Slow (1 hour)

9.151. Cyclic References

Avoid cyclic references.

Cyclic references happen when an object points to another object, which reciprocate. This is particularly possible with classes, when the child class has to keep a reference to the parent class.

<?php

class a {
    private $p = null;

    function foo() {
        $this->p = new b();
        // the current class is stored in the child class
        $this->p->m($this);
    }
}

class b {
    private $pb = null;

    function n($a) {
        // the current class keeps a link to its parent
        $this->pb = $a;
    }
}
?>

Cyclic references, or circular references, are memory intensive : only the garbage collector can understand when they may be flushed from memory, which is a costly operation. On the other hand, in an acyclic reference code, the reference counter will know immediately know that an object is free or not.

See also About circular references in PHP and A Journey to find a memory leak.

9.151.1. Suggestions

  • Use a different object when calling the child objects.
  • Refactor your code to avoid the cyclic reference.
Short name Classes/CyclicReferences
Rulesets Analyze, ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.152. 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)
// $a is already a reference, even if it doesn't show here.
foreach($array as $a) {}
//$array === array(3,4,5,5);

?>

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, PHP foreach pass-by-reference: Do it right, or better not at all, How does PHP ‘foreach’ actually work?, References and foreach.

9.152.1. Suggestions

  • Avoid using the reference altogether : sometimes, the reference is not needed.
  • Add unset() right after the loop, to avoid reusing the reference.
Short name Structures/DanglingArrayReferences
Rulesets Analyze, Top10, CI-checks
Severity Major
Time To Fix Instant (5 mins)
ClearPHP no-dangling-reference
Examples Typo3, SugarCrm

9.153. 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, with spl_register_autoload(), there is no need for that kind of code. Structures should be defined, and accessible to the autoloading. Inclusions and deep definitions should be avoided, as they compel code to load some definitions, while autoloading will only load them if needed.

<?php

class X {
    function init() {
        // myFunction is defined when and only if X::init() is called.
        if (!function_exists('myFunction'){
            function myFunction($a) {
                return $a + 1;
            }
        })
    }
}

?>

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.

Definitions inside a if/then statement, that include PHP version check are accepted here.

See also Autoloading Classes.

9.153.1. Suggestions

  • Move function definitions to the global space : outside structures, and method.
Short name Functions/DeepDefinitions
Rulesets Analyze
Severity Major
Time To Fix Slow (1 hour)
Examples Dolphin

9.154. 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]);

?>
Short name Php/DefineWithArray
Rulesets CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.0 and more recent
Severity Critical
Time To Fix Slow (1 hour)

9.155. Dependant Abstract Classes

Abstract classes should be autonomous. It is recommended to avoid depending on methods, constant or properties that should be made available in inheriting classes, without explicitly abstracting them.

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

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

<?php

// autonomous abstract class : all it needs is within the class
abstract class c {
    private $p = 0;

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

// dependant abstract class : the inheriting classes needs to provide some properties or methods
abstract class c2 {
    function foo() {
        // $p must be provided by the extending class
        return ++$this->p;
    }
}

class c3 extends c2 {
    private $p = 0;
}
?>

See also Dependant Trait.

9.155.1. Suggestions

  • Make the class only use its own resources
  • Split the class in autonomous classes
  • Add local property definitions to make the class independent
Short name Classes/DependantAbstractClass
Rulesets Analyze, ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.156. 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;
}
?>

See also Dependant Abstract Classes.

9.156.1. Suggestions

  • Add local property definitions to make the trait independent
  • Make the trait only use its own resources
  • Split the trait in autonomous traits
Short name Traits/DependantTrait
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)
Examples Zencart

9.157. Deprecated Functions

The following functions are deprecated. It is recommended to stop using them now and replace them with a durable equivalent.

Note that these functions may be still usable : they generate warning that help tracking their usage in the log. To eradicate their usage, watch the logs, and update any deprecated warning. This way, the code won’t be stuck when the function is actually removed from PHP.

<?php

// This is the current function
list($day, $month, $year) = explode('/', '08/06/1995');

// This is deprecated
list($day, $month, $year) = split('/', '08/06/1995');

?>

9.157.1. Suggestions

  • Replace those deprecated with modern syntax
  • Stop using deprecated syntax
Short name Php/Deprecated
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Quick (30 mins)
ClearPHP no-deprecated
Examples Dolphin

9.158. 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];
?>
Short name Structures/DereferencingAS
Rulesets CompatibilityPHP53, CompatibilityPHP54
Php Version With PHP 5.3 and older
Severity Major
Time To Fix Quick (30 mins)

9.159. Detect Current Class

Detecting the current class should be done with self::class or static::class operator.

__CLASS__ may be replaced by self\:\:class. get_called_class() may be replaced by static\:\:class.

__CLASS__ and get_called_class() are set to be deprecated in PHP 7.4.

<?php

class X {
    function foo() {
        echo __CLASS__.\n;          // X
        echo self::class.\n;        // X

        echo get_called_class().\n;  // Y
        echo static::class.\n;       // Y
    }
}

class Y extends X {}

$y = new Y();
$y->foo();

?>

See also PHP RFC: Deprecations for PHP 7.4.

9.159.1. Suggestions

  • Use the self::class operator to detect the current class name, instead of __CLASS__ and get_class().
  • Use the static::class operator to detect the current called class name, instead of get_called_class().
Short name Php/DetectCurrentClass
Rulesets Suggestions, CompatibilityPHP74
Php Version With PHP 8.0 and older
Precision Very high

9.160. Different Argument Counts

Two methods with the same name shall have the same number of compulsory argument. PHP accepts different number of arguments between two methods, if the extra arguments have default values. Basically, they shall be called interchangeably with the same number of arguments.

The number of compulsory arguments is often mistaken for the same number of arguments. When this is the case, it leads to confusion between the two signatures. It will also create more difficulties when refactoring the signature.

While this code is legit, it is recommended to check if the two signatures could be synchronized, and reduce future surprises.

<?php

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

class y extends x {
    // This method is compatible with the above, its signature is different
    function foo($a, $b = 1) {}
}

?>

9.160.1. Suggestions

  • Extract the extra arguments into other methods
  • Remove the extra arguments
  • Add the extra arguments to all the signatures
Short name Classes/DifferentArgumentCounts
Rulesets Analyze, ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.161. Direct Call To __clone()

Direct call to magic method __clone() was forbidden. It is allowed since PHP 7.0.

From the RFC : Doing calls like $obj->`__clone( <https://www.php.net/manual/en/language.oop5.magic.php>`_ ) is now allowed. This was the only magic method that had a compile-time check preventing some calls to it, which doesn't make sense. If we allow all other magic methods to be called, there's no reason to forbid this one.

<?php

    class Foo {
        function __clone() {}
    }

    $a = new Foo;
    $a->__clone();
?>

See also Directly calling `__clone is allowed <https://wiki.php.net/rfc/abstract_syntax_tree#directly_calling_clone_is_allowed>`_.

9.161.1. Suggestions

  • Use the clone operator to call the __clone magic method
Short name Php/DirectCallToClone
Rulesets CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.0 and more recent
Severity Critical
Time To Fix Slow (1 hour)

9.162. Direct Injection

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

<?php

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

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

?>

See also Cross-Site Scripting (XSS)

9.162.1. Suggestions

  • Validate input : make sure the incoming data are what you expect from them.
  • Escape output : prepare outgoing data for the next system to use.
Short name Security/DirectInjection
Rulesets Security
Severity Major
Time To Fix Quick (30 mins)

9.163. Directly Use File

Some PHP functions have a close cousin that work directly on files : use them. This is faster and less code to write.

<?php

// Good way
$file_hash = hash_file('sha512', 'example.txt');

// Slow way
$file_hash = hash('sha512', file_get_contents('example.txt'));

?>

See also hash_file.

9.163.1. Suggestions

  • Use the _file() version of those functions
Short name Structures/DirectlyUseFile
Rulesets Suggestions
Severity Minor
Time To Fix Instant (5 mins)

9.164. Disconnected Classes

One class is extending the other, but they do not use any features from one another. Basically, those two classes are using extends, but they are completely independent and may be separated.

When using the ‘extends’ keyword, the newly created classes are now acting together and making one. This should be visible in calls from one class to the other, or simply by property usage : they can’t live without each other.

On the other hand, two completely independent classes that are merged, although they should be kept separated.

<?php

class A {
    private $pa = 1;

    function fooA() {
        $this->pa = 2;
    }
}

// class B and Class A are totally independent
class B extends A {
    private $pb = 1;

    function fooB() {
        $this->pb = 2;
    }
}


// class C makes use of class A : it is dependent on the parent class
class C extends A {
    private $pc = 1;

    function fooB() {
        $this->pc = 2 + $this->fooA();
    }
}
?>

9.164.1. Suggestions

  • Remove the extension
  • Make actual usage of the classes, at least from one of them
Short name Classes/DisconnectedClasses
Rulesets ClassReview
Severity Minor
Time To Fix Slow (1 hour)
Examples WordPress

9.165. Do In Base

Use SQL expression to compute aggregates.

<?php

// Efficient way
$res = $db->query('SELECT sum(e) AS sumE FROM table WHERE condition');

// The sum is already done
$row = $res->fetchArray();
$c += $row['sumE'];

// Slow way
$res = $db->query('SELECT e FROM table WHERE condition');

// This aggregates the column e in a slow way
while($row = $res->fetchArray()) {
    $c += $row['e'];
}

?>

9.165.1. Suggestions

  • Rework the query to move the calculations in the database
Short name Performances/DoInBase
Rulesets Performances
Severity Major
Time To Fix Quick (30 mins)

9.166. Don’t Be Too Manual

Adapt the examples from the PHP manual to your code. Don’t reuse directly the same names in your code : be more specific about what to expect in those variables.

<?php

// Search for phone numbers in a text
preg_match_all('/((\d{3})-(\d{3})-(\d{4}))/', $string, $phoneNumber);

// Search for phone numbers in a text
preg_match_all('/(\d{3})-(\d{3})-(\d{4})/', $string, $matches);

?>

9.166.1. Suggestions

  • Use precise name with your variables
Short name Structures/DontBeTooManual
Rulesets Coding Conventions
Severity Minor
Time To Fix Quick (30 mins)

9.167. 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 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.

Short name Structures/NoChangeIncomingVariables
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)

9.168. Don’t Echo Error

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

PHP’s uses the display_errors directive to control display of errors to the browser. This must be kept to off when in production.

<?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;
}

// Changing PHP configuration
ini_set('display_errors', 1);
// This is also a security error : 'false' means actually true.
ini_set('display_errors', 'false');

?>

Error messages should be logged, but not displayed.

See also Error reporting and List of php.ini directives.

9.168.1. Suggestions

  • Remove any echo, print, printf() call built with error messages from an exception, or external source.
Short name Security/DontEchoError
Rulesets Analyze, Security, CI-checks
Severity Critical
Time To Fix Instant (5 mins)
Examples ChurchCRM, Phpdocumentor

9.169. Don’t Loop On Yield

Use yield from, instead of looping on a generator with yield.

yield from delegate the yielding to another generator, and keep calling that generator until it is finished. It also works with implicit generator datastructure, like arrays.

<?php

function generator() {
    for($i = 0; $i < 10; ++$i) {
        yield $i;
    }
}

function delegatingGenerator() {
    yield from generator();
}

// Too much code here
function generator2() {
    foreach(generator() as $g) {
        yield $g;
    }
}

?>

There is a performance gain when delegating, over looping manually on the generator. You may even consider writing the loop to store all values in an array, then yield from the array.

See also Generator delegation via yield from.

9.169.1. Suggestions

  • Use yield from instead of the whole foreach() loop
Short name Structures/DontLoopOnYield
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)
Examples Dolibarr, Tikiwiki

9.170. Don’t Pollute Global Space

Avoid creating definitions in the global name space.

The global namespace is the default namespace, where all functions, classes, constants, traits and interfaces live. The global namespace is also known as the root namespace.

In particular, PHP native classes usually live in that namespace. By creating functions in that namespace, the code may encounter naming conflict, when the PHP group decides to use a name that the code also uses. This already happened in PHP version 5.1.1, where a Date native class was introduced, and had to be disabled in the following minor version.

Nowadays, conflicts appear between components, which claim the same name.

See also Using namespaces: fallback to global function/constant.

9.170.1. Suggestions

  • Create a namespace for your code, and store your definition there.
Short name Php/DontPolluteGlobalSpace
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.171. Don’t Read And Write In One Expression

Avoid giving value and using it at the same time, in one expression. This is an undefined behavior of PHP, and may change without warning.

One of those changes happens between PHP 7.2 and 7.3 :

<?php

$arr = [1];
$ref =& $arr[0];
var_dump($arr[0] + ($arr[0] = 2));
// PHP 7.2: int(4)
// PHP 7.3: int(3)

?>

See also UPGRADING 7.3.

9.171.1. Suggestions

  • Split the expression in two separate expressions
Short name Structures/DontReadAndWriteInOneExpression
Rulesets Analyze, CompatibilityPHP73, CompatibilityPHP74
Severity Critical
Time To Fix Quick (30 mins)

9.172. Don’t Send $this In Constructor

Don’t use $this as an argument while in the __construct(). Until the constructor is finished, the object is not finished, and may be in an unstable state. Providing it to another code may lead to error.

This is true when the receiving structure puts the incoming object immediately to work, and don’t store it for later use.

<?php

// $this is only provided when Foo is constructed
class Foo {
    private $bar = null;
    private $data = array();

    static public function build($data) {
        $foo = new Foo($data);
        // Can't build in one call. Must make it separate.
        $foo->finalize();
    }

    private function __construct($data) {
        // $this is provided too early
        $this->data = $data;
    }

    function finalize() {
        $this->bar = new Bar($this);
    }
}

// $this is provided too early, leading to error in Bar
class Foo2 extends Foo {
    private $bar = null;
    private $data = array();

    function __construct($data) {
        // $this is provided too early
        $this->bar = new Bar($this);
        $this->data = $data;
    }
}

class Bar {
    function __construct(Foo $foo) {
        // the cache is now initialized with a wrong
        $this->cache = $foo->getIt();
    }
}

?>

See also Don’t pass this out of a constructor.

9.172.1. Suggestions

  • Finish the constructor first, then call an external object.
  • Sending $this should be made accessible in a separate method, so external objects may call it.
  • Sending the current may be the responsibility of the method creating the object.
Short name Classes/DontSendThisInConstructor
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)
Examples Woocommerce, Contao

9.173. Don’t Unset Properties

Avoid unsetting properties. They would go undefined, and raise more warnings.

When getting rid of a property, assign it to null. This keeps the property in the object, yet allows existence check without errors.

<?php

class Foo {
    public $a = 1;
}

$a = new Foo();

var_dump((array) $a) ;
// la propriété est reportée, et null
// ['a' => null]

unset($a->a);

var_dump((array) $a) ;
//Empty []

// Check if a property exists
var_dump($a->b === null);

// Same result as above, but with a warning
var_dump($a->c === null);

?>

This analysis works on properties and static properties. It also reports magic properties being unset.

Thanks for Benoit Burnichon for the original idea.

9.173.1. Suggestions

  • Never unset properties : set it to null or its default value instead
  • Make the property an array, and set/unset its index
Short name Classes/DontUnsetProperties
Rulesets Analyze, Top10, php-cs-fixable, CI-checks
Severity Major
Time To Fix Slow (1 hour)
Examples Vanilla, Typo3

9.174. 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 obvious, 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);
}

?>
Short name Structures/DontChangeBlindKey
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.175. Dont Collect Void

When a method returns void, there is no need to collect the result. The collected value will actually be null.

<?php

function foo() : void {
    // doSomething()
}

// This is useless
$result = foo();

// This is useless. It looks like this is a left over from code refactoring
echo foo();

?>

9.175.1. Suggestions

  • Move the call to the function to its own expression with a semi-colon.
  • Remove assignation of the result of such calls.
Short name Functions/DontUseVoid
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high

9.176. Dont Compare Typed Boolean

There is no need to compare explicitly a function call to a boolean, when the definition has a boolean return typehint.

The analysis checks for equality and identity comparisons. It doesn’t check for the not operator usage.

<?php

// Sufficient check
if (foo()) {
    doSomething();
}

// Superfluous check
if (foo() === true) {
    doSomething();
}

function foo() : bool {}

?>

9.176.1. Suggestions

  • Simplify the code and make it short
Short name Structures/DontCompareTypedBoolean
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)

9.177. Dont Mix ++

++ operators, pre and post, have two distinct behaviors, and should be used separately.

When mixed in a larger expression, they are difficult to read, and may lead to unwanted behaviors.

<?php

    // Clear and defined behavior
    $i++;
    $a[$i] = $i;

    // The index is also incremented, as it is used AFTP the incrementation
    // With $i = 2; $a is array(3 => 3)
    $a[$i] = ++$i;

    // $i is actually modified twice
    $i = --$i + 1;
?>

See also EXP30-C. Do not depend on the order of evaluation for side effects.

9.177.1. Suggestions

  • Extract the increment from the expression, and put it on a separate line.
Short name Structures/DontMixPlusPlus
Rulesets Analyze
Severity Minor
Time To Fix Instant (5 mins)
Examples Contao, Typo3

9.178. 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;

?>
Short name Structures/DoubleAssignation
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)

9.179. Double Instructions

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

<?php

// repetition of the same command, with the same effect each time.
$a = array_merge($b, $c);
$a = array_merge($b, $c);

// false positive : commands are identical, but the effect is compounded
$a = array_merge($a, $c);
$a = array_merge($a, $c);

?>

9.179.1. Suggestions

  • Remove double work
  • Avoid repetition by using loops, variadic or quantifiers (dirname($path, 2))
Short name Structures/DoubleInstruction
Rulesets Analyze
Severity Minor
Time To Fix Instant (5 mins)

9.180. Double Object Assignation

Make sure that assigning the same object to two variables is the intended purpose.

<?php

// $x and $y are the same object, as they both hold a reference to the same object.
// This means that changing $x, will also change $y.
$x = $y = new Z();

// $a and $b are distinct values, by default
$a = $b = 1;

?>

9.180.1. Suggestions

Short name Structures/DoubleObjectAssignation
Rulesets Analyze, ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.181. Double array_flip()

Avoid double array_flip() to gain speed. While array_flip() alone is usually useful, a double call to array_flip() is made to make values and keys unique.

<?php

// without array_flip
function foo($array, $value) {
    $key = array_search($array, $value);

    if ($key !== false) {
        unset($array[$key]);
    }

    return $array;
}

// double array_flip
// array_flip() usage means that $array's values are all unique
function foo($array, $value) {
    $flipped = array_flip($value);
    unset($flipped[$value]);
    return array_flip($flipped);
}

?>

9.181.1. Suggestions

  • use array_unique() or array_count_values
  • use array_flip() once, and let PHP garbage collect it later
  • Keep the original values in a separate variable
Short name Performances/DoubleArrayFlip
Rulesets Performances
Severity Major
Time To Fix Quick (30 mins)
Examples NextCloud

9.182. Drop Else After Return

Avoid else clause when the then clause returns, but not the else. And vice-versa.

This way, the else block disappears, and is now the main sequence of the function.

This is also true if else has a return, and then not. When doing so, don’t forget to 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;
}

?>

9.182.1. Suggestions

  • Remove the else clause and move its code to the main part of the method
Short name Structures/DropElseAfterReturn
Rulesets Analyze, Suggestions, CI-checks
Severity Minor
Time To Fix Quick (30 mins)

9.183. Drop Substr Last Arg

Substr() works till the end of the string when the last argument is omitted. There is no need to calculate string size to make this work.

<?php

$string = 'abcdef';

// Extract the end of the string
$cde = substr($string, 2);

// Too much work
$cde = substr($string, 2, strlen($string));

?>

See also substr.

9.183.1. Suggestions

  • Use negative length
  • Omit the last argument to get the string till its end
Short name Structures/SubstrLastArg
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)
Examples SuiteCrm, Tine20

9.184. Duplicate Literal

Report literals that are repeated across the code. The minimum replication is 5, and is configurable with maxDuplicate.

Repeated literals should be considered a prime candidate for constants.

Integer, reals and strings are considered here. Boolean, Null and Arrays are omitted. 0, 1, 2, 10 and the empty string are all omitted, as too common.

<?php
    // array index are omitted
    $x[3] = 'b';

    // constanst are omitted
    const X = 11;
    define('Y', 'string')

    // 0, 1, 2, 10 are omitted
    $x = 0;

?>

9.184.1. Suggestions

  • Create a constant and use it in place of the literal
  • Create a class constant and use it in place of the literal
Name Default Type Description
minDuplicate 15 integer Minimal number of duplication before the literal is reported.
Short name Type/DuplicateLiteral
Rulesets Semantics
Severity Minor
Time To Fix Quick (30 mins)

9.185. Dynamic Library Loading

Loading a variable dynamically requires a lot of care in the preparation of the library name.

In case of injection in the variable, the dynamic loading of a library gives a lot of power to an intruder.

<?php

    // dynamically loading a library
     dl($library. PHP_SHLIB_SUFFIX);

    // dynamically loading ext/vips
     dl('vips.' . PHP_SHLIB_SUFFIX);

    // static loading ext/vips (unix only)
     dl('vips.so');

?>

See also dl.

9.185.1. Suggestions

  • Use a switch structure, to make the dl() calls static.
  • Avoid using dl() and make the needed extension always available in PHP binary.
Short name Security/DynamicDl
Rulesets Security
Severity Major
Time To Fix Slow (1 hour)

9.186. Echo Or Print

Echo and print have the same functional use. <?= and printf() are also considered in this analysis.

There seems to be a choice that is not enforced : one form is dominant, (> 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';

?>
Short name Structures/EchoPrintConsistance
Rulesets Coding Conventions
Severity Minor
Time To Fix Quick (30 mins)

9.187. 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 visible 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;
?>

9.187.1. Suggestions

  • Turn the concatenation into a list of argument, by replacing the dots by commas.
Short name Structures/EchoWithConcat
Rulesets Performances, Analyze, Suggestions
Severity Minor
Time To Fix Quick (30 mins)
ClearPHP no-unnecessary-string-concatenation
Examples Phpdocumentor, TeamPass

9.188. Ellipsis Usage

Usage of the ellipsis keyword. The keyword is three dots : . It is also named variadic or splat operator.

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();
}
?>

See also PHP RFC: Syntax for variadic functions, PHP 5.6 and the Splat Operator, and Variable-length argument lists.

Short name Php/EllipsisUsage
Rulesets CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55
Php Version With PHP 5.6 and more recent
Severity Major
Time To Fix Slow (1 hour)

9.189. 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();

?>

See also elseif/else if.

9.189.1. Suggestions

  • Merge else and if into elseif
  • Turn the else expression into a block, and have more than the second if in this block
  • Turn the if / else if / else into a switch structure
Short name Structures/ElseIfElseif
Rulesets Analyze, php-cs-fixable, Rector, CI-checks
Severity Minor
Time To Fix Quick (30 mins)
Examples TeamPass, Phpdocumentor

9.190. Empty Blocks

Full empty block, part of a control structures.

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;

?>

9.190.1. Suggestions

  • Fill the block with a command
  • Fill the block with a comment that explain the situation
  • Remove the block and its commanding operator
Short name Structures/EmptyBlocks
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Instant (5 mins)
Examples Cleverstyle, PhpIPAM

9.191. Empty Classes

Classes that do no define anything at all. This is probably dead code.

Classes that are directly derived from an exception are omitted.

<?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 {}

?>

9.191.1. Suggestions

  • Remove an empty class :it is probably dead code.
  • Add some code to the class to make it concrete.
Short name Classes/EmptyClass
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)
Examples WordPress

9.192. 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.

A method is considered empty when it contains nothing, or contains expressions without impact.

<?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() {}
}

?>

Methods which overwrite another methods are omitted. Methods which are the concrete version of an abstract method are considered.

9.192.1. Suggestions

  • Fill the function with actual code
  • Remove any usage of the function, then remove the function
Short name Functions/EmptyFunction
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Examples Contao

9.193. 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.

Comments that explains the reason of the situation are not taken into account.

<?php
    $condition = 3;;;;
    if ($condition) { }
?>

9.193.1. Suggestions

  • Remove the empty lines
  • Fill the empty lines
Short name Structures/EmptyLines
Rulesets Dead code, Analyze
Severity Minor
Time To Fix Instant (5 mins)
Examples Zurmo, ThinkPHP

9.194. 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?.

9.194.1. Suggestions

  • Remove the interface
  • Add some methods or constants to the interface
Short name Interfaces/EmptyInterface
Rulesets Analyze
Severity Minor
Time To Fix Instant (5 mins)

9.195. 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);

?>

9.195.1. Suggestions

  • Remove empty list() calls
Short name Php/EmptyList
Rulesets Analyze, CompatibilityPHP70
Php Version With PHP 7.0 and older
Severity Major
Time To Fix Quick (30 mins)

9.196. Empty Namespace

Declaring a namespace in the code and not using it for structure declarations or global instructions is useless.

Using simple style :

<?php

namespace Y;

class foo {}


namespace X;
// This is useless

?>

Using bracket-style syntax :

<?php

namespace X {
    // This is useless
}

namespace Y {

    class foo {}

}

?>

9.196.1. Suggestions

  • Remove the namespace
Short name Namespaces/EmptyNamespace
Rulesets Analyze, Dead code, CI-checks
Severity Minor
Time To Fix Instant (5 mins)
ClearPHP no-empty-namespace

9.197. Empty Slots In Arrays

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

<?php
    $a = array( 1, 2, 3, );
    $b =      [ 4, 5, ];
?>
Short name Arrays/EmptySlots
Rulesets Coding Conventions
Severity Minor
Time To Fix Instant (5 mins)

9.198. 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.

9.198.1. Suggestions

  • Add some code to the trait
  • Remove the trait
Short name Traits/EmptyTrait
Rulesets Analyze
Severity Minor
Time To Fix Instant (5 mins)

9.199. Empty Try Catch

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

<?php

try {
    doSomething();
} catch (Throwable $e) {
    // 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 <https://www.php.net/manual/en/class.throwable.php>`_ $e) with empty catch block should be banned. They ignore any error and proceed as if nothing happened. At worst, the event should be logged for future analysis.

See also Empty Catch Clause.

9.199.1. Suggestions

  • Add some logging in the catch
  • Add a comment to mention why the catch is empty
  • Change the exception, chain it and throw again
Short name Structures/EmptyTryCatch
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)
Examples LiveZilla, Mautic

9.200. 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();
}

?>

See also empty.

9.200.1. Suggestions

  • Use the compatible syntax, and store the result in a local variable before testing it with empty
Short name Structures/EmptyWithExpression
Rulesets Suggestions
Php Version With PHP 5.5 and more recent
Severity Major
Time To Fix Quick (30 mins)
Examples HuMo-Gen

9.201. 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.

This analysis also detects Unicode codepoint with superfluous leading zeros.

<?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{000041}l;
$a('php_info();');

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

?>

9.201.1. Suggestions

  • Make all simple letter appear clearly
  • Add comments about why this code is encoded
Short name Security/EncodedLetters
Rulesets Security
Severity Minor
Time To Fix Quick (30 mins)
Examples Zurmo

9.202. Eval() Usage

Using eval() is evil.

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 the code code given to eval() 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 pregenerated file is faster and cacheable.

There are several situations where eval() is actually the only solution :

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

See also eval and The Land Where PHP Uses `eval() <https://www.exakat.io/land-where-php-uses-eval/>`_.

9.202.1. Suggestions

  • Use a dynamic feature of PHP to replace the dynamic code
  • Store the code on the disk, and use include
  • Replace create_function() with a closure!
Short name Structures/EvalUsage
Rulesets Analyze, Performances, Security
Severity Major
Time To Fix Quick (30 mins)
ClearPHP no-eval
Examples XOOPS, Mautic

9.203. Exceeding Typehint

The typehint is not fully used in the method. Some of the defined methods in the typehint are unused. A tighter typehint could be used, to avoid method pollution.

<?php

interface i {
    function i1();
    function i2();
}

interface j {
    function j1();
    function j2();
}

function foo(i $a, j $b) {
    // the i typehint is totally used
    $a->i1();
    $a->i2();

    // the i typehint is not totally used : j2() is not used.
    $b->j1();
}

?>

Tight typehint prevents the argument from doing too much. They also require more maintenance : creation of dedicated interfaces, method management to keep all typehint tight.

See also Insufficient Typehint.

9.203.1. Suggestions

  • Keep the typehint tight, do not inject more than needed.
Short name Functions/ExceedingTypehint
Rulesets ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.204. 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
}

?>
Short name Exceptions/AlreadyCaught
Rulesets Dead code
Examples Woocommerce

9.205. Exit() Usage

Using exit or die() <https://www.php.net/`die>`_ in the code makes the code untestable (it will break unit tests). Moreover, 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.

9.205.1. Suggestions

  • Avoid exit and die. Let the script finish.
  • Throw an exception and let it be handled before finishing
Short name Structures/ExitUsage
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Quick (30 mins)
ClearPHP no-exit
Examples Traq, ThinkPHP

9.206. Exponent Usage

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

<?php

$eight = 2 ** 3;

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

?>

See also Arithmetic Operators.

Short name Php/ExponentUsage
Rulesets CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55
Php Version With PHP 5.6 and more recent
Severity Major
Time To Fix Instant (5 mins)

9.207. 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') { }

?>

9.207.1. Suggestions

  • Fix the string
  • Fix the length of the string
  • Put the string in a constant, and use strlen() or mb_strlen()
Short name Structures/FailingSubstrComparison
Rulesets Analyze, Top10, CI-checks
Severity Major
Time To Fix Instant (5 mins)
Examples Zurmo, MediaWiki

9.208. Fetch One Row Format

When reading results with ext/Sqlite3, it is recommended to explicitly 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.

9.208.1. Suggestions

  • Specify the result format when reading rows from a Sqlite3 database
Short name Performances/FetchOneRowFormat
Rulesets Performances
Severity Minor
Time To Fix Instant (5 mins)

9.209. Filter To add_slashes()

FILTER_SANITIZE_MAGIC_QUOTES is deprecated. In PHP 7.4, it should be replaced with addslashes()

According to the migration RDFC : ‘Magic quotes were deprecated all the way back in PHP 5.3 and later removed in PHP 5.4. The filter extension implements a sanitization filter that mimics this behavior of magic_quotes by calling addslashes() on the input in question.’

<?php

// Deprecated way to filter input
$var = filter_input($input, FILTER_SANITIZE_MAGIC_QUOTES);

// Alternative way to filter input
$var = addslashes($input);

?>

addslashes() used to filter data while building SQL queries, to prevent injections. Nowadays, prepared queries are a better option.

See also Deprecations for PHP 7.4.

9.209.1. Suggestions

  • Replace FILTER_SANITIZE_MAGIC_QUOTES with addslashes()
  • Replace FILTER_SANITIZE_MAGIC_QUOTES with an adapted escaping system
Short name Php/FilterToAddSlashes
Rulesets CompatibilityPHP74
Severity Minor
Time To Fix Quick (30 mins)

9.210. Final Class Usage

List of all final classes being used.

final may be applied to classes and methods.

<?php
class BaseClass {
   public function test() {
       echo 'BaseClass::test() called'.PHP_EOL;
   }

   final public function moreTesting() {
       echo 'BaseClass::moreTesting() called'.PHP_EOL;
   }
}

class ChildClass extends BaseClass {
   public function moreTesting() {
       echo 'ChildClass::moreTesting() called'.PHP_EOL;
   }
}
// Results in Fatal error: Cannot override final method BaseClass::moreTesting()
?>

See also Final Keyword.

Short name Classes/Finalclass
Rulesets ClassReview, LintButWontExec

9.211. Final Methods Usage

List of all final methods being used.

final may be applied to classes and methods.

<?php
class BaseClass {
   public function test() {
       echo 'BaseClass::test() called'.PHP_EOL;
   }

   final public function moreTesting() {
       echo 'BaseClass::moreTesting() called'.PHP_EOL;
   }
}

class ChildClass extends BaseClass {
   public function moreTesting() {
       echo 'ChildClass::moreTesting() called'.PHP_EOL;
   }
}
// Results in Fatal error: Cannot override final method BaseClass::moreTesting()
?>

See also Final Keyword.

Short name Classes/Finalmethod
Rulesets LintButWontExec, ClassReview

9.212. Flexible Heredoc

Flexible syntax for Heredoc.

The new flexible syntax for heredoc and nowdoc enable the closing marker to be indented, and remove the new line requirement after the closing marker.

It was introduced in PHP 7.3.

<?php

// PHP 7.3 and newer
foo($a = <<<END

    flexible syntax
    with extra indentation

    END);

// All PHP versions
$a = <<<END

    Normal syntax

END;


?>

This syntax is backward incompatible : once adopted in the code, previous versions won’t compile it.

See also Heredoc and Flexible Heredoc and Nowdoc Syntaxes.

Short name Php/FlexibleHeredoc
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.3 and more recent
Severity Critical
Time To Fix Instant (5 mins)

9.213. Fn Argument Variable Confusion

Avoid using local variables as arrow function arguments.

When a local variable name is used as an argument’s name in an arrow function, the local variable from the original scope is not imported. They are now two distinct variables.

When the local variable is not listed as argument, it is then imported in the arrow function.

<?php

function foo() {
    $locale = 1;

    // Actually ignores the argument, and returns the local variable ``$locale``.
    $fn2 = fn ($argument) => $locale;

    // Seems similar to above, but returns the incoming argument
    $fn2 = fn ($locale) => $locale;
}

?>

See also Arrow functions.

9.213.1. Suggestions

  • Change the name of the local variable
  • Change the name of the argument
Short name Functions/FnArgumentVariableConfusion
Rulesets Analyze, Semantics
Php Version 7.4+
Severity Minor
Time To Fix Quick (30 mins)

9.214. 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.

Short name Structures/ForWithFunctioncall
Rulesets Performances, Top10
Severity Minor
Time To Fix Slow (1 hour)
ClearPHP no-functioncall-in-loop

9.215. Foreach Don’t Change Pointer

foreach loops use their own internal cursor.

A foreach loop won’t change the internal pointer of the array, as it works on a copy of the source. Hence, applying array pointer’s functions such as current() or next() to the source array won’t have the same behavior in PHP 5 than PHP 7.

This only 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
}

?>

See also foreach no longer changes the internal array pointer and foreach.

Short name Php/ForeachDontChangePointer
Rulesets CompatibilityPHP70
Php Version With PHP 7.0 and older
Severity Major
Time To Fix Slow (1 hour)

9.216. Foreach On Object

Foreach on object looks like a typo. This is particularly true when both object and member are variables.

Foreach on an object member is a legit PHP syntax, though it is very rare : blind variables rarely have to be securing in an object to be processed.

<?php

// Looks suspicious
foreach($array as $o -> $b) {
    doSomething();
}

// This is the real thing
foreach($array as $o => $b) {
    doSomething();
}

?>
Short name Php/ForeachObject
Rulesets Analyze
Severity Major
Time To Fix Instant (5 mins)

9.217. 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.

<?php

$letters = range('a', 'z');

// $letter is not used here
foreach($letters as &$letter) {
    $alphabet .= $letter;
}

// $letter is actually used here
foreach($letters as &$letter) {
    $letter = strtoupper($letter);
}

?>

9.217.1. Suggestions

  • Remove the reference from the foreach
  • Actually modify the content of the reference
Short name Structures/ForeachReferenceIsNotModified
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Quick (30 mins)
Examples Dolibarr, Vanilla

9.218. Foreach With list()

Foreach loops have the ability to use list as blind variables. This syntax assign directly array elements to various variables.

PHP 5.5 introduced the usage of list in foreach() loops. Until PHP 7.1, it was not possible to use non-numerical arrays as list() wouldn’t support string-indexed arrays.

<?php
    // PHP 5.5 and later, with numerically-indexed arrays
    foreach($array as list($a, $b)) {
        // do something
    }


    // PHP 7.1 and later, with arrays
    foreach($array as list('col1' => $a, 'col3' => $b)) { // 'col2 is ignored'
        // 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
    }
?>

See also The list function & practical uses of array destructuring in PHP.

Short name Structures/ForeachWithList
Rulesets CompatibilityPHP53, CompatibilityPHP54
Php Version With PHP 5.5 and more recent
Severity Minor
Time To Fix Slow (1 hour)

9.219. Forgotten Interface

The following classes have been found implementing an interface’s methods, though it doesn’t explicitly 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() {}
}

?>

See also could-use-trait.

9.219.1. Suggestions

  • Mention interfaces explicitly whenever possible
Short name Interfaces/CouldUseInterface
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.220. 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();
}

?>

9.220.1. Suggestions

  • Remove the throw expression
  • Add the new to the throw expression
Short name Exceptions/ForgottenThrown
Rulesets Analyze
Severity Major
Time To Fix Instant (5 mins)

9.221. Forgotten Visibility

Some classes elements (property, method, constant) 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.

Class constants support also visibility since PHP 7.1.

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.

9.221.1. Suggestions

  • Always add explicit visibility to methods and constants in a class
  • Always add explicit visibility to properties in a class, after PHP 7.4
Short name Classes/NonPpp
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Instant (5 mins)
ClearPHP always-have-visibility
Examples FuelCMS, LiveZilla

9.222. Forgotten Whitespace

Forgotten whitespaces only bring misery to the code.

White spaces have been left at either end of a file : before the PHP opening tag, or after the closing tag.

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

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

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

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

9.222.1. Suggestions

  • Remove all whitespaces before and after a script. This doesn’t apply to template, which may need to use those spaces.
  • Remove the final tag, to prevent any whitespace to be forgotten at the end of the file. This doesn’t apply to the opening PHP tag, which is always necessary.
Short name Structures/ForgottenWhiteSpace
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Instant (5 mins)

9.223. Fossilized Method

A method is fossilized when it is overwritten so often that changing a default value, a return type or an argument type is getting difficult.

This happens when a class is extended. When a method is overwritten once, it may be easy to update the signature in two places. The more methods are overwriting a parent method, the more difficult it is to update it.

This analysis counts the number of times a method is overwritten, and report any method that is ovrewritten more than 6 times. This threshold may be configured.

<?php

class x1 {
    // foo1() is never overwritten. It is easy to update.
    function foo1() {}

    // foo7() is overwritten seven times. It is hard to update.
    function foo7() {}
}

// classes x2 to x7, all overwrite foo7();
// Only x2 is presente here.
class x2 extends x1 {
    function foo7() {}
}

?>
Name Default Type Description
fossilizationThreshold 6 integer Minimal number of overwriting methods to consider a method difficult to update.
Short name Classes/FossilizedMethod
Rulesets ClassReview, Typechecks
Severity Minor
Time To Fix Quick (30 mins)

9.224. 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.

9.224.1. Suggestions

  • Drop the initial when creating constants with define() : for example, use trim($x, ‘’), which removes anti-slashes before and after.
Short name Namespaces/ConstantFullyQualified
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)

9.225. 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.

Short name Structures/FunctionSubscripting
Rulesets CompatibilityPHP53
Php Version With PHP 5.4 and more recent
Severity Minor
Time To Fix Instant (5 mins)

9.226. 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';

?>

9.226.1. Suggestions

  • Skip the local variable and directly use the return value from the function
Short name Structures/FunctionPreSubscripting
Rulesets Suggestions
Php Version With PHP 5.4 and more recent
Severity Minor
Time To Fix Instant (5 mins)
Examples OpenConf

9.227. 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.

Short name Php/Php54RemovedFunctions
Rulesets CompatibilityPHP54
Php Version With PHP 5.4 and older
Severity Major
Time To Fix Quick (30 mins)

9.228. Functions Removed In PHP 5.5

Those functions were removed in PHP 5.5.

<?php

echo '<img src="' . $_SERVER['PHP_SELF'] .
     '?=' . php_logo_guid() . '" alt="PHP Logo !" />';

?>

See also Deprecated features in PHP 5.5.x.

9.228.1. Suggestions

  • Stop using those functions
Short name Php/Php55RemovedFunctions
Rulesets CompatibilityPHP55
Php Version With PHP 5.5 and older
Severity Major
Time To Fix Quick (30 mins)

9.229. Generator Cannot Return

Generators could not use return and yield at the same time. In PHP 7.0, generator can now use both of them.

<?php

// This is not allowed until PHP 7.0
function foo() {
    yield 1;
    return 'b';
}

?>

9.229.1. Suggestions

  • Remove the return
Short name Functions/GeneratorCannotReturn
Rulesets CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version 7.0+
Severity Major
Time To Fix Quick (30 mins)

9.230. Getting Last Element

Getting the last element of an array relies on array_key_last().

array_key_last() was added in PHP 7.3. Before that,

<?php

$array = ['a' => 1, 'b' => 2, 'c' => 3];

// Best solutions, by far
$last = $array[array_key_last($array)];

// Best solutions, just as fast 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));
);

?>

9.230.1. Suggestions

  • Use PHP native function : array_key_last(), when using PHP 7.4 and later
  • Use PHP native function : array_pop()
  • Organise the code to put the last element in the first position (array_unshift() instead of append operator [])
Short name Arrays/GettingLastElement
Rulesets Performances
Severity Minor
Time To Fix Instant (5 mins)
Examples Thelia

9.231. Global Inside Loop

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

<?php

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

// Global is called each time : this is slow.
foreach($a as $b) {
    global $total;
    $total += $b;
}
?>

9.231.1. Suggestions

  • Move the global keyword outside the loop
Short name Structures/GlobalOutsideLoop
Rulesets Performances

9.232. Global Usage

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

<?php
$a = 1; /* global scope */

function test()
{
    echo $a; /* reference to local scope variable */
}

test();

?>

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

See also Variable scope.

Short name Structures/GlobalUsage
Rulesets Analyze
Severity Minor
Time To Fix Slow (1 hour)
ClearPHP no-global

9.233. 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.

Short name Php/GroupUseDeclaration
Rulesets CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Severity Minor
Time To Fix Instant (5 mins)

9.234. 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 Trailing Commas In List Syntax and Revisit trailing commas in function arguments.

Short name Php/GroupUseTrailingComma
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.2 and more recent
Severity Major
Time To Fix Instant (5 mins)

9.235. 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';   // yes, this doesn't exists, it's an example
$conn_id = ftp_connect($ftp_server);

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

?>

See also 10 GitHub Security Best Practices and Git How-To: Remove Your Password from a Repository.

9.235.1. Suggestions

  • Remove all passwords from the code. Also, check for history if you are using a VCS.
Name Default Type Description
passwordsKeys password_keys.json data List of array index and property names that shall be checked for potential secret key storages.
Short name Functions/HardcodedPasswords
Rulesets Analyze, Security
Severity Major
Time To Fix Slow (1 hour)
ClearPHP no-hardcoded-credential

9.236. Hash Algorithms

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

<?php

// This hash has existed in PHP. Check with hash_algos() if it is available on your system.
echo hash('ripmed160', 'The quick brown fox jumped over the lazy dog.');

// This hash doesn't exist
echo hash('ripemd160', 'The quick brown fox jumped over the lazy dog.');

?>

See also hash_algos.

9.236.1. Suggestions

  • Use a hash algorithm that is available on several PHP versions
  • Fix the name of the hash algorithm
Short name Php/HashAlgos
Rulesets Analyze
Severity Major
Time To Fix Slow (1 hour)

9.237. Hash Algorithms Incompatible With PHP 5.3

List of hash algorithms incompatible with PHP 5.3.

<?php

// Compatible only with 5.3 and more recent
echo hash('md2', 'The quick brown fox jumped over the lazy dog.');

// Always compatible
echo hash('ripemd320', 'The quick brown fox jumped over the lazy dog.');

?>

See also hash_algos.

Short name Php/HashAlgos53
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP72
Severity Major
Time To Fix Slow (1 hour)

9.238. Hash Algorithms Incompatible With PHP 5.4/5.5

List of hash algorithms incompatible with PHP 5.4 and 5.5.

<?php

// Compatible only with 5.4 and more recent
echo hash('fnv132', 'The quick brown fox jumped over the lazy dog.');

// Always compatible
echo hash('ripemd320', 'The quick brown fox jumped over the lazy dog.');

?>

See also hash_algos.

Short name Php/HashAlgos54
Rulesets CompatibilityPHP54, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP72
Php Version With PHP 5.4 and older
Severity Major
Time To Fix Slow (1 hour)

9.239. Hash Algorithms Incompatible With PHP 7.1-

List of hash algorithms incompatible with PHP 7.1 and more recent. At the moment of writing, this is compatible up to 7.3.

The hash algorithms were introduced in PHP 7.1.

<?php

// Compatible only with 7.1 and more recent
echo hash('sha512/224', 'The quick brown fox jumped over the lazy dog.');

// Always compatible
echo hash('ripemd320', 'The quick brown fox jumped over the lazy dog.');

?>

See also hash_algos.

Short name Php/HashAlgos71
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.1 and older
Severity Major
Time To Fix Slow (1 hour)

9.240. Hash Algorithms Incompatible With PHP 7.4-

List of hash algorithms incompatible with PHP 7.3 and older recent. At the moment of writing, this is compatible up to 7.4s.

The hash algorithms were introduced in PHP 7.4s.

<?php

// Compatible only with 7.1 and more recent
echo hash('crc32cs', 'The quick brown fox jumped over the lazy dog.');

// Always compatible
echo hash('ripemd320', 'The quick brown fox jumped over the lazy dog.');

?>

See also hash_algos.

Short name Php/HashAlgos74
Rulesets CompatibilityPHP74
Php Version With PHP 7.4 and older
Severity Major
Time To Fix Slow (1 hour)

9.241. Hash Will Use Objects

The ext/hash extension used resources, and is being upgraded to use resources.

<?php

// Post 7.2 code
    $hash = hash_init('sha256');
    if (!is_object($hash)) {
        trigger_error('error');
    }
    hash_update($hash, $message);

// Pre-7.2 code
    $hash = hash_init('md5');
    if (!is_resource($hash)) {
        trigger_error('error');
    }
    hash_update($hash, $message);

?>

See also Move ext/hash from resources to objects.

Short name Php/HashUsesObjects
Rulesets CompatibilityPHP72
Severity Major
Time To Fix Quick (30 mins)

9.242. 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 several forms are dominant (> 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;

?>
Short name Structures/HeredocDelimiterFavorite
Rulesets Coding Conventions

9.243. 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
?>
Short name Type/HexadecimalString
Rulesets CompatibilityPHP70, CompatibilityPHP71
Severity Major
Time To Fix Slow (1 hour)

9.244. Hidden Nullable

Argument with default value of null are nullable. Even when the null typehint (PHP 8.0), or the ? operator are not used, setting the default value to null is allowed, and makes the argument nullable.

This doesn’t happen with properties : they must be defined with the nullable type to accept a ``null``value as default value.

This doesn’t happen with constant, which can’t be typehinted.

<?php

// explicit nullable parameter $s
function bar(?string $s = null) {

// implicit nullable parameter $s
function foo(string $s = null) {
    echo $s ?? 'NULL-value';
}

// both display NULL-value
foo();
foo(null);

?>

See also Nullable types and Type declaration.

9.244.1. Suggestions

  • Change the default value to a compatible literal : for example, string $s = ''
  • Add the explicit ? nullable operator, or ``null``with PHP 8.0
  • Remove the default value
Short name Classes/HiddenNullable
Rulesets Analyze, ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.245. Hidden Use Expression

The use expression for namespaces should always be at the 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) {}
    }
}

?>

9.245.1. Suggestions

  • Group all uses together, at the beginning of the namespace or class
Short name Namespaces/HiddenUse
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Instant (5 mins)
Examples Tikiwiki, OpenEMR

9.246. 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 HTML 4 or 5, etc. It is highly recommended to set it explicitly.

The third argument of the functions is the encoding of the string. In PHP 5.3, it is 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.

<?php
$str = 'A quote is <b>bold</b>';

// Outputs, without depending on the php.ini: A &#039;quote&#039; is &lt;b&gt;bold&lt;/b&gt;
echo htmlentities($str, ENT_QUOTES, 'UTF-8');

// Outputs, while depending on the php.ini: A quote is &lt;b&gt;bold&lt;/b&gt;
echo htmlentities($str);

?>

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.

See also htmlentities and htmlspecialchars.

9.246.1. Suggestions

  • Always use the third argument with htmlentities()
Short name Structures/Htmlentitiescall
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Instant (5 mins)

9.247. 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 in parenthesis is bad
if (($a) ^ ($a)) {}

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

?>

9.247.1. Suggestions

  • Merge the two structures into one unique test
  • Add extra expressions between the two structures
  • Nest the structures, to show that different attempts are made
Short name Structures/IdenticalConditions
Rulesets Analyze, CI-checks
Severity Critical
Time To Fix Quick (30 mins)
Examples WordPress, Dolibarr, Mautic

9.248. Identical Consecutive Expression

Identical consecutive expressions are worth being checked.

They may be a copy/paste with unmodified content. When the content has to be duplicated, it is recommended to avoid executing the expression again, and just access the cached result.

<?php

$current  = $array[$i];
$next     = $array[$i + 1];
$nextnext = $array[$i + 1]; // OOps, nextnext is wrong.

// Initialization
$previous = foo($array[1]); // previous is initialized with the first value on purpose
$next     = foo($array[1]); // the second call to foo() with the same arguments should be avoided
// the above can be rewritten as :
$next     = $previous; // save the processing.

for($i = 1; $i < 200; ++$i) {
    $next = doSomething();
}
?>
Short name Structures/IdenticalConsecutive
Rulesets Analyze
Severity Minor
Time To Fix Instant (5 mins)

9.249. Identical On Both Sides

Operands should be different when comparing or making a logical combination. Of course, the value each operand holds may be identical. When the same operand appears on both sides of the expression, the result is know before execution.

<?php

// Trying to confirm consistency
if ($login == $login) {
    doSomething();
}

// Works with every operators
if ($object->login( ) !== $object->login()) {
    doSomething();
}

if ($sum >= $sum) {
    doSomething();
}

//
if ($mask && $mask) {
    doSomething();
}

if ($mask || $mask) {
    doSomething();
}

?>

9.249.1. Suggestions

  • Remove one of the alternative, and remove the logical link
  • Modify one of the alternative, and make it different from the other
Short name Structures/IdenticalOnBothSides
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Quick (30 mins)
Examples phpMyAdmin, HuMo-Gen

9.250. 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();
}

?>

9.250.1. Suggestions

  • Merge the two conditions so the condition is used once.
  • Change one of the condition, so they are different
  • Make it obvious that the first condition is a try, preparing the normal conditions.
Short name Structures/IfWithSameConditions
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Quick (30 mins)
Examples phpMyAdmin, Phpdocumentor

9.251. 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;
}

?>

9.251.1. Suggestions

  • Move the assignation inside the loop, and make an existence test in the condition.
  • Move the assignation before the if/then, make an existence test in the condition.
Short name Structures/Iffectation
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high

9.252. 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() {}
}

?>

See also Magic Methods.

9.252.1. Suggestions

  • Avoid method names starting with a double underscore : __
  • Use method visibilities to ensure that methods are only available to the current class or its children
Short name Classes/WrongName
Rulesets Analyze
Severity Major
Time To Fix Slow (1 hour)
Examples PrestaShop, Magento

9.253. Implement Is For Interface

With class heritage, implements should be used for interfaces, and extends with classes.

PHP defers the implements check until execution : the code in example does lint, but won,t run.

<?php

class x {
    function foo() {}
}

interface y {
    function foo();
}

// Use implements with an interface
class z implements y {}

// Implements is for an interface, not a class
class z implements x {}

?>

9.253.1. Suggestions

  • Create an interface from the class, and use it with the implements keyword
Short name Classes/ImplementIsForInterface
Rulesets Analyze
Severity Minor
Time To Fix Quick (30 mins)

9.254. 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() {}
}

?>

See also Interfaces and Interfaces - the next level of abstraction.

9.254.1. Suggestions

  • Make the implemented method public
Short name Classes/ImplementedMethodsArePublic
Rulesets Analyze
Severity Major
Time To Fix Instant (5 mins)

9.255. Implied If

It is confusing to emulate if/then with boolean operators.

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);

// Defines a default value if provided is empty-ish
// Warning : this is
$user = $_GET['user'] || 'anonymous';

?>

It is recommended to use a real ‘if then’ structures, to make the condition readable.

Short name Structures/ImpliedIf
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Instant (5 mins)
ClearPHP no-implied-if

9.256. Implode One Arg

implode() may be called with one arg. It is recommended to avoid it.

Using two arguments makes it less surprising to new comers, and consistent with explode() syntax.

<?php

$array = range('a', 'c');

// empty string is the glue
print implode('', $array);

// only the array : PHP uses the empty string as glue.
// Avoid this
print implode($array);

?>

See also implode.

9.256.1. Suggestions

  • Add an empty string as first argument
Short name Php/ImplodeOneArg
Rulesets Suggestions, php-cs-fixable
Severity Minor
Time To Fix Quick (30 mins)

9.257. Implode() Arguments Order

implode() accepted two signatures, but is only recommending one. Both types orders of string then array, and array then string have been possible until PHP 7.4.

In PHP 7.4, the order array then string is deprecated, and emits a warning. It will be removed in PHP 8.0.

<?php

$glue = ',';
$pieces = range(0, 4);

// documented argument order
$s = implode($glue, $pieces);

// Pre 7.4 argument order
$s = implode($pieces, $glue);

// both produces 0,1,2,3,4

?>

See also implode().

9.257.1. Suggestions

  • Always use the array as the second argument
Short name Structures/ImplodeArgsOrder
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Quick (30 mins)

9.258. Inclusion Wrong Case

Inclusion should follow exactly the case of included files and path. This prevents the infamous case-sensitive filesystem bug, where files are correctly included in a case-insensitive system, and failed to be when moved to production.

<?php

// There must exist a path called path/to and a file library.php with this case
include path/to/library.php;

// Error on the case, while the file does exist
include path/to/LIBRARY.php;

// Error on the case, on the PATH
include path/TO/library.php;

?>

See also include_once, about case sensitivity and inclusions.

9.258.1. Suggestions

  • Make the inclusion string identical to the file name.
  • Change the name of the file to reflect the actual inclusion. This is the best way when a naming convention has been set up for the project, and the file doesn’t adhere to it. Remember to change all other inclusion.
Short name Files/InclusionWrongCase
Rulesets Analyze
Severity Major
Time To Fix Slow (1 hour)

9.259. Incompatible Signature Methods

Methods should have the same signature when being overwritten.

The same signatures means the children class must have : + the same name + the same visibility or less restrictive + the same typehint or removed + the same default value or removed + a reference like its parent

This problem emits a fatal error, for abstract methods, or a warning error, for normal methods. Yet, it is difficult to lint, because classes are often stored in different files. As such, PHP do lint each file independently, as unknown parent classes are not checked if not present. Yet, when executing the code, PHP lint the actual code and may encounter a fatal error.

<?php

class a {
    public function foo($a = 1) {}
}

class ab extends a {
    // foo is overloaded and now includes a default value for $a
    public function foo($a) {}
}

?>

See also Object Inheritance.

9.259.1. Suggestions

  • Make signatures compatible again
Short name Classes/IncompatibleSignature
Rulesets Analyze, LintButWontExec
Php Version 7.4-
Severity Critical
Time To Fix Quick (30 mins)
Examples SuiteCrm

9.260. Incompatible Signature Methods With Covariance

Methods should have the compatible signature when being overwritten.

The same signatures means the children class must have : + the same name + the same visibility or less restrictive + the same contravariant typehint or removed + the same covariant return typehint or removed + the same default value or removed + a reference like its parent

This problem emits a fatal error, for abstract methods, or a warning error, for normal methods. Yet, it is difficult to lint, because classes are often stored in different files. As such, PHP do lint each file independently, as unknown parent classes are not checked if not present. Yet, when executing the code, PHP lint the actual code and may encounter a fatal error.

<?php

class a {
    public function foo($a = 1) {}
}

class ab extends a {
    // foo is overloaded and now includes a default value for $a
    public function foo($a) {}
}

?>
See also Object Inheritance,
PHP RFC: Covariant Returns and Contravariant Parameters and Incompatible Signature Methods.

9.260.1. Suggestions

  • Make signatures compatible again
Short name Classes/IncompatibleSignature74
Rulesets Analyze
Php Version 7.4+
Severity Critical
Time To Fix Quick (30 mins)
Examples SuiteCrm

9.261. 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 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 not compilable 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.

9.261.1. Suggestions

  • If this file is a template for PHP code, change the extension to something else than .php
  • Fix the syntax so it works with various versions of PHP
Short name Php/Incompilable
Rulesets Analyze
Severity Critical
Time To Fix Slow (1 hour)
ClearPHP no-incompilable
Examples xataface

9.262. Inconsistent Elseif

Chaining if/elseif requires a consistent string of conditions. The conditions are executed one after the other, and the conditions shouldn’t overlap.

This analysis reports chains of elseif that don’t share a common variable (or array, or property, etc.. ). As such, testing different conditions are consistent.

<?php

// $a is always common, so situations are mutually exclusive
if ($a === 1) {
    doSomething();
} else if ($a > 1) {
    doSomethingElse();
} else {
    doSomethingDefault();
}

// $a is always common, so situations are mutually exclusive
// although, it may be worth checking the consistency here
if ($a->b === 1) {
    doSomething();
} else if ($a->c > 1) {
    doSomethingElse();
} else {
    doSomethingDefault();
}

// if $a === 1, then $c doesn't matter?
// This happens, but then logic doesn't appear in the code.
if ($a === 1) {
    doSomething();
} else if ($c > 1) {
    doSomethingElse();
} else {
    doSomethingDefault();
}

?>
Short name Structures/InconsistentElseif
Rulesets Analyze
Severity Major
Time To Fix Slow (1 hour)

9.263. 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);

/*
The above displays
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.

Note also that PHP detects integer inside strings, and silently turn them into integers. Partial and octal numbers are not transformed.

<?php
    $a = [1      => 1,
          '2'    => 2,
          '011'  => 9, // octal number
          '11d'  => 11, // partial number
          ];

    var_dump($a);

/*
The above displays
array(4) {
  [1]=>
  int(1)
  [2]=>
  int(2)
  [011]=>
  int(9)
  [11d]=>
  int(11)
}*/
?>

See also Arrays syntax.

9.263.1. Suggestions

  • Do not use any type but string or integer
  • Force typecast the keys when building an array
Short name Structures/IndicesAreIntOrString
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Quick (30 mins)
Examples Zencart, Mautic

9.264. Indirect Injection

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

<?php

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

function foo($b) {
    echo $b;
}
foo($_POST['c']);

?>

9.264.1. Suggestions

  • Always validate incoming values before using them.
Short name Security/IndirectInjection
Rulesets Security
Severity Critical
Time To Fix Slow (1 hour)

9.265. Infinite Recursion

A method is calling itself, with unchanged arguments. This will probably repeat indefinitely.

This applies to recursive functions without any condition. This also applies to function which inject the incoming arguments, without modifications.

<?php

function foo($a, $b) {
    if ($a > 10) {
        return;
    }
    foo($a, $b);
}

function foo2($a, $b) {
    ++$a;   // $a is modified
    if ($a > 10) {
        return;
    }
    foo2($a, $b);
}

?>

9.265.1. Suggestions

  • Modify arguments before injecting them again in the same method
  • Use different values when calling the same method
Short name Structures/InfiniteRecursion
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)

9.266. Instantiating Abstract Class

PHP cannot instantiate an abstract class.

The classes are actually abstract classes, and should be derived into a concrete class to be instantiated.

<?php

abstract class Foo {
    protected $a;
}

class Bar extends Foo {
    protected $b;
}

// instantiating a concrete class.
new Bar();

// instantiating an abstract class.
// In real life, this is not possible also because the definition and the instantiation are in the same file
new Foo();

?>

See also Class Abstraction.

Short name Classes/InstantiatingAbstractClass
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)

9.267. Insufficient Property Typehint

The typehint used for a class property doesn’t cover all it usage.

The typehint is insufficient when a undefined method is called, or if members are access while the typehint is an interface.

<?php

class A {
    function a1() {}
}

// PHP 7.4 and more recent
class B {
    private A $a = null;

    function b2() {
        // this method is available in A
        $this->a->a1();
        // this method is NOT available in A
        $this->a->a2();
    }
}

// Supported by all PHP versions
class C {
    private $a = null;

    function __construct(A $a) {
        $this->a = $a;
    }

    function b2() {
        // this method is available in A
        $this->a->a1();
        // this method is NOT available in A
        $this->a->a2();
    }
}

?>

This analysis relies on typehinted properties, as introduced in PHP 7.4. It also relies on typehinted assignations at construct time : the typehint of the assigned argument will be used as the property typehint. Getters and setters are not considered here.

9.267.1. Suggestions

  • Change the typehint to match the actual usage of the object in the class.
Short name Classes/InsufficientPropertyTypehint
Rulesets ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.268. Insufficient Typehint

An argument is typehinted, but it actually calls methods that are not listed in the interface.

Classes may be implementing more methods than the one that are listed in the interface they also implements. This means that filtering objects with a typehint, but calling other methods will be solved at execution time : if the method is available, it will be used; if it is not, a fatal error is reported.

<?php

class x implements i {
    function methodI() {}
    function notInI() {}
}

interface i {
    function methodI();
}

function foo(i $x) {
    $x->methodI(); // this call is valid
    $x->notInI();  // this call is not garanteed
}
?>

Inspired by discussion with Brandon Savage.

9.268.1. Suggestions

  • Extend the interface with the missing called methods
  • Change the body of the function to use only the methods that are available in the interface
  • Change the used objects so they don’t depend on extra methods
Short name Functions/InsufficientTypehint
Rulesets Analyze, Typechecks
Severity Major
Time To Fix Quick (30 mins)

9.269. Integer As Property

It is backward incompatible to use integers are property names. This feature was introduced in PHP 7.2.

If the code must be compatible with previous versions, avoid casting arrays to object.

<?php

// array to object
$arr = [0 => 1];
$obj = (object) $arr;
var_dump(
    $obj,
    $obj->{'0'}, // PHP 7.2+ accessible
    $obj->{0} // PHP 7.2+ accessible

    $obj->{'b'}, // always been accessible
);
?>

See also PHP RFC: Convert numeric keys in object/array casts.

Short name Classes/IntegerAsProperty
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.2 and more recent
Severity Major
Time To Fix Slow (1 hour)

9.270. Integer Conversion

Comparing incoming variables to integer may lead to injection.

When comparing a variable to an integer, PHP applies type juggling, and transform the variable in an integer too. When the value converts smoothly to an integer, this means the validation may pass and yet, the value may carry an injection.

<?php

// This is safer
if ($_GET['x'] === 2) {
    echo $_GET['x'];
}

// Using (int) for validation and display
if ((int) $_GET['x'] === 2) {
    echo (int) $_GET['x'];
}

// This is an injection
if ($_GET['x'] == 2) {
    echo $_GET['x'];
}

// This is unsafe, as $_GET['x']  is tester as an integer, but echo'ed raw
if ((int) $_GET['x'] === 2) {
    echo $_GET['x'];
}

?>

This analysis spots situations where an incoming value is compared to an integer. The usage of the validated value is not considered.

See also Type Juggling Authentication Bypass Vulnerability in CMS Made Simple, PHP STRING COMPARISON VULNERABILITIES and PHP Magic Tricks: Type Juggling.

9.270.1. Suggestions

Short name Security/IntegerConversion
Rulesets Security
Severity Major
Time To Fix Quick (30 mins)

9.271. Interfaces Don’t Ensure Properties

When using an interface as a typehint, properties are not enforced, nor available.

An interface is a template for a class, which specify the minimum amount of methods and constants. Properties are never defined in an interface, and should not be relied upon.

<?php

interface i {
    function m () ;
}

class x implements i {
    public $p = 1;

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

function foo(i $i, x $x) {
    // this is invalid, as $p is not defined in i, so it may be not available
    echo $i->p;

    // this is valid, as $p is defined in $x
    echo $x->p;
}

?>

9.271.1. Suggestions

  • Use classes for typehint when properties are accessed
  • Only use methods and constants which are available in the interface
Short name Interfaces/NoGaranteeForPropertyConstant
Rulesets Analyze, ClassReview
Severity Minor
Time To Fix Quick (30 mins)

9.272. Interfaces Is Not Implemented

Classes that implements interfaces, must implements each of the interface’s methods.

<?php

class x implements i {
    // This method implements the foo method from the i interface
    function foo() {}

    // The method bar is missing, yet is requested by interface i
    function foo() {}
}

interface i {
    function foo();
    function bar();
}

?>

This problem tends to occur in code that splits interfaces and classes by file. This means that PHP’s linting will skip the definitions and not find the problem. At execution time, the definitions will be checked, and a Fatal error will occur.

This situation usually detects code that was forgotten during a refactorisation of the interface or the class and its sibblings.

See also Interfaces.

9.272.1. Suggestions

  • Implements all the methods from the interfaces
  • Remove the class
  • Make the class abstract
  • Make the missing methods abstract
Short name Interfaces/IsNotImplemented
Rulesets Analyze, ClassReview, LintButWontExec, CI-checks
Severity Minor
Time To Fix Quick (30 mins)

9.273. Interpolation

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

<?php

class b {
    public $b = 'c';
    function __toString() { return __CLASS__; }
}
$x = array(1 => new B());

// -> after the $x[1] looks like a 2nd dereferencing, but it is not.
print $x[1]->b;
// displays : b->b

print {$x[1]->b};
// displays : c

?>

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

See also Double quoted.

Short name Type/StringInterpolation
Rulesets Coding Conventions
Severity Minor
Time To Fix Quick (30 mins)

9.274. Invalid Constant Name

There is a naming convention for PHP constants names.

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, must follow this regex : /[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/.

In particular when defined using define() function, no error is produced. When using const, on the other hand, the

<?php

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

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

?>

See also Constants.

9.274.1. Suggestions

  • Change constant name
Short name Constants/InvalidName
Rulesets Analyze
Severity Major
Time To Fix Quick (30 mins)
Examples OpenEMR

9.275. Invalid Octal In String

Any octal sequence inside a string can’t be go 7. Those will be a fatal error at parsing time.

The check is applied to the string, starting with PHP 7.1. In PHP 7.0 and older, those sequences were silently adapted (modulo/% 0).

<?php

// A valid octal in a PHP string
echo 0; // @

// Emit a warning in PHP 7.1
//Octal escape sequence overflow 0 is greater than 7
echo 0; // @

// Silent conversion
echo 8; // 8

?>

See also Integers.

9.275.1. Suggestions

  • Use a double slash to avoid the sequence to be an octal sequence
  • Use a function call, such as decoct() to convert larger number to octal notation
Short name Type/OctalInString
Rulesets CompatibilityPHP71
Php Version With PHP 7.1 and older
Severity Major
Time To Fix Quick (30 mins)

9.276. Invalid Pack Format

Some characters are invalid in a pack() format string.

pack() and unpack() accept the following format specifiers : aAhHcCsSnviIlLNVqQJPfgGdeExXZ.

unpack() also accepts a name after the format specifier and an optional quantifier.

All other situations is not a valid, and produces a warning : pack(): Type t: unknown format code

<?php
    $binarydata = pack(nvc*, 0x1234, 0x5678, 65, 66);

    // the first unsigned short is stored as 'first'. The next matches are names with numbers.
    $res = unpack('nfirst/vc*', $binarydata);
?>

Check pack() documentation for format specifiers that were introduced in various PHP version, namely 7.0, 7.1 and 7.2.

See also pack and unpack.

9.276.1. Suggestions

  • Fix the packing format with correct values
Short name Structures/InvalidPackFormat
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Quick (30 mins)

9.277. Invalid Regex

The PCRE regex doesn’t compile. It isn’t a valid regex.

Several reasons may lead to this situation : syntax error, Unknown modifier, missing parenthesis or reference.

<?php

// valid regex
preg_match('/[abc]/', $string);

// invalid regex (missing terminating ] for character class
preg_match('/[abc/', $string);

?>

Regex are check with the Exakat version of PHP.

Dynamic regex are only checked for simple values. Dynamic values may eventually generate a compilation error.

9.277.1. Suggestions

  • Fix the regex before running it
Short name Structures/InvalidRegex
Rulesets Analyze, CI-checks
Severity Major
Time To Fix Quick (30 mins)
Examples SugarCrm

9.278. Is Actually Zero

This addition actually may be simplified 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 obvious
$a = $b[3] - $c + $d->foo(1,2,3) + $c + $b[3];

?>

9.278.1. Suggestions

  • Clean the code and remove the null sum
  • Fix one of the variable : this expression needs another variable here
  • When adding differences, calculate the difference in a temporary variable first.
Short name Structures/IsZero
Rulesets Analyze, CI-checks
Severity Minor
Time To Fix Instant (5 mins)
Examples Dolibarr, SuiteCrm

9.279. Is_A() With String

When using is_a() with a string as first argument, the third argument is compulsory.

<?php

// is_a() works with string as first argument, when the third argument is 'true'
if (is_s('A', 'B', true)) {}

// is_a() works with object as first argument
if (is_s(new A, 'A')) {}
?>

See also is_a().

9.279.1. Suggestions

  • Add the third argument, and set it to true
  • Use an object as a first argument
Short name Php/IsAWithString
Rulesets Analyze, Rector, CI-checks
Severity Minor
Time To Fix Quick (30 mins)

9.280. Isset Multiple Arguments

isset() may be used with multiple arguments and acts as a AND.

<?php

// isset without and
if (isset($a, $b, $c)) {
    // doSomething()
}

// isset with and
if (isset($a) && isset($b) && isset($c)) {
    // doSomething()
}

?>

See also Isset <http://www.php.net/`isset>`_.

9.280.1. Suggestions

  • Merge all isset() calls into one
Short name Php/IssetMultipleArgs
Rulesets Suggestions, php-cs-fixable
Severity Minor
Time To Fix Instant (5 mins)
Examples ThinkPHP, LiveZilla

9.281. Isset() On The Whole Array

Isset() works quietly on a whole array. There is no need to test all previous index before testing for the target index.

<?php

// Straight to the point
if (isset($a[1]['source'])) {
    // Do something with $a[1]['source']
}

// Doing too much work
if (isset($a) && isset($a[1]) && isset($a[1]['source'])) {
    // Do something with $a[1]['source']
}

?>

There is a gain in readability, by avoiding long and hard to read logical expression, and reducing them in one simple isset call.

There is a gain in performances by using one call to isset, instead of several, but it is a micro-optimization.

See also Isset <http://www.php.net/`isset>`_.

9.281.1. Suggestions

  • Remove all unnecessary calls to isset()
Short name Performances/IssetWholeArray
Rulesets Suggestions, Performances
Severity Minor
Time To Fix Instant (5 mins)
Examples Tine20, ExpressionEngine

9.282. Joining file()

Use file() to read lines separately.

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.

If the delimiter is not ‘’, then implode() and file() are a better solution than file_get_contents() and str_replace() or nl2br().

<?php

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

// memory and CPU intensive
$content = join('', file('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. Consider using readfile() to echo the content directly to the output.

See also file_get_contents and file.

9.282.1. Suggestions

  • Use file_get_contents() instead of implode(file()) to read the whole file at once.
  • Use readfile() to echo the content to stdout at once.
  • Use fopen() to read the lines one by one, generator style.
Short name Performances/JoinFile
Rulesets Performances
Severity Minor
Time To Fix Quick (30 mins)
Examples WordPress, SPIP, ExpressionEngine, PrestaShop

9.283. Keep Files Access Restricted

Avoid using 0777 as file or directory mode. In particular, setting a file or a directory to 0777 (or universal read-write-execute) may lead to security vulnerabilities, as anything on the server may read, write and even execute

File mode may be changed using the chmod() function, or at directory creation, with mkdir().

<?php

file_put_contents($file, $content);

// this file is accessible to the current user, and to his group, for reading and writing.
chmod($file, 0550);

// this file is accessible to everyone
chmod($file, 0777);

?>

By default, this analysis report universal access (0777). It is possible to make this analysis more restrictive, by providing more forbidden modes in the filePrivileges parameter. For example : 511,510,489. Only use a decimal representation.

See also Mkdir Default and Least Privilege Violation.

9.283.1. Suggestions

  • Set the file mode to a level of restriction as low as possible.
Name Default Type Description
filePrivileges 0777 string List of forbidden file modes (comma separated).
Short name Security/KeepFilesRestricted
Rulesets Security
Severity Minor
Time To Fix Quick (30 mins)

9.284. Large Try Block

Try block should enclosing only the expression that may emit an exception.

When writing large blocks of code in a try, it becomes difficult to understand where the expression is coming from. Large blocks may also lead to catch multiples exceptions, with a long list of catch clause.

In particular, the catch clause will resume the execution without knowing where the try was interrupted : there are no indication of achievement, even partial. In fact, catching an exception signals a very dirty situation.

<?php

// try is one expression only
try {
    $database->query($query);
} catch (DatabaseException $e) {
    // process exception
}

// Too many expressions around the one that may actually emit the exception
try {
    $SQL = build_query($arguments);
    $database = new Database($dsn);
    $database->setOption($options);
    $statement = $database->prepareQuery($SQL);
    $result = $statement->query($query);
} catch (DatabaseException $e) {
    // process exception
}

?>

This analysis reports try blocks that are 5 lines or more. This threshold may be configured with the directive tryBlockMaxSize. Catch clause, and finally are not considered here.

9.284.1. Suggestions

  • Reduce the amount of code in the block, by moving it before and after
Name Default Type Description
tryBlockMaxSize 5 integer Maximal number of expressions in the try block.
Short name Exceptions/LargeTryBlock
Rulesets Suggestions
Severity Minor
Time To Fix Quick (30 mins)

9.285. 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'];

?>
Short name Php/ListShortSyntax
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.1 and more recent
Severity Major
Time To Fix Quick (30 mins)

9.286. 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
)

9.286.1. Suggestions

  • Refactor code to avoid using append in a list() call
Short name Php/ListWithAppends
Rulesets CompatibilityPHP70
Severity Minor
Time To Fix Slow (1 hour)

9.287. 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];

?>
Short name Php/ListWithKeys
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.1 and more recent
Severity Major
Time To Fix Quick (30 mins)

9.288. List With Reference

Support for references in list calls is not backward compatible with older versions of PHP. The support was introduced in PHP 7.3.

<?php

$array = [1,2,3];

[$c, &$d, $e] = $a;

$d++;
$c++;
print_r($array);
/*
displays
Array
(
    [0] => 1  // Not a reference to $c, unchanged
    [1] => 3  // Reference from $d
    [2] => 3
)
*/
?>

See also list() Reference Assignment.

9.288.1. Suggestions

  • Avoid using references in list for backward compatibility
Short name Php/ListWithReference
Rulesets CompatibilityPHP53, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Php Version With PHP 7.3 and more recent
Severity Major
Time To Fix Slow (1 hour)

9.289. 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 syntactically correct, it is unusual that defined resources 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<