1. Rules

1.1. Introduction

Exakat provides unique 1561 rules to detect BUGS, CODE SMELLS, SECURITY OR QUALITY ISSUES in your PHP code.

Each rule is documented with code example to allow you to remediate your code. If you want to automate remediation, ours cobblers can are there to fix the issues in your code for your.

1.2. List of Rules

1.2.1. $FILES full_path

A new index ‘full_path’ was added to the $_FILES to handle directory <https://www.php.net/`directory>`_ uploads. This was added in PHP 8.1, and is not available before.
<?php

// list uploaded files in a directory
print_r($_FILES['full_path']);

?>

See also PHP 8.1: $_FILES: New full_path value for directory-uploads.

1.2.1.1. Specs

Short name Php/FilesFullPath
Rulesets All, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73, CompatibilityPHP74
Exakat since 2.2.4
PHP Version With PHP 8.1 and older
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features $_FILES
Available in Entreprise Edition, Exakat Cloud

1.2.2. $GLOBALS Or global

Usually, PHP projects make a choice between the global keyword, and the $GLOBALS variable. Sometimes, the project has no recommendations.

When your project use a vast majority of one of the convention, then the analyzer will report all remaining inconsistently cased constant.

<?php

global $a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m;

// This access is inconsistent with the previous usage
$GLOBALS['a'] = 2;

?>

1.2.2.1. Specs

Short name Php/GlobalsVsGlobal
Rulesets All, Preferences
Exakat since 0.9.2
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features global
Available in Entreprise Edition, Exakat Cloud

1.2.3. $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.

1.2.3.1. Suggestions

  • Use php://input with fopen() instead.

1.2.3.2. Specs

Short name Php/RawPostDataUsage
Rulesets All, Appinfo, CE, Changed Behavior, CompatibilityPHP56
Exakat since 0.8.4
PHP Version All
Severity Major
Time To Fix Slow (1 hour)
Changed Behavior PHP 5.6
Precision Very high
Features $HTTP_RAW_POST_DATA
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.4. $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();
}

?>

1.2.4.1. Suggestions

  • Use error_get_last() instead.

1.2.4.2. Specs

Short name Php/PhpErrorMsgUsage
Rulesets All, CE, Changed Behavior, CompatibilityPHP80
Exakat since 2.1.8
PHP Version With PHP 8.0 and older
Severity Minor
Time To Fix Quick (30 mins)
Changed Behavior PHP 8.0
Precision High
Features $php_errormsg
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.5. $this Belongs To Classes Or Traits

The pseudo-variable $this must be used inside a class or trait, or bound closures.

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

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

}
?>

See also class.

1.2.5.1. Suggestions

  • Do not use $this as a variable name, except for the current object, in a class, trait or closure.

1.2.5.2. Specs

Short name Classes/ThisIsForClasses
Rulesets All, Analyze, LintButWontExec
Exakat since 0.8.4
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features $this, self, parent, static
Examples OpenEMR
Note This issue may lint but will not run
Available in Entreprise Edition, Exakat Cloud

1.2.6. $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.

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

1.2.6.2. Specs

Short name Classes/ThisIsNotAnArray
Rulesets All, Analyze
Exakat since 0.8.4
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision High
Features $this
Available in Entreprise Edition, Exakat Cloud

1.2.7. $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>`_.

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

1.2.7.2. Specs

Short name Classes/ThisIsNotForStatic
Rulesets All, Analyze
Exakat since 0.8.4
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features static, method
ClearPHP no-static-this
Available in Entreprise Edition, Exakat Cloud

1.2.8. ** 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.

1.2.8.1. Suggestions

  • Use the ** operator
  • For powers of 2, use the bitshift operators
  • For literal powers of 2, consider using the 0xFFFFFFFFF syntax.

1.2.8.2. Specs

Short name Php/NewExponent
Rulesets All, Suggestions
Exakat since 0.8.4
PHP Version With PHP 5.6 and more recent
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features exponential
Examples Traq, TeamPass
Available in Entreprise Edition, Exakat Cloud

1.2.9. ::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.

1.2.9.1. Suggestions

  • Use ::class whenever possible. That exclude any dynamic call.

1.2.9.2. Specs

Short name Php/StaticclassUsage
Rulesets All, CompatibilityPHP53, CompatibilityPHP54
Exakat since 0.8.4
PHP Version With PHP 5.5 and more recent
Severity Major
Time To Fix Slow (1 hour)
Precision Very high
Features coalesce
Available in Entreprise Edition, Exakat Cloud

1.2.10. <?= Usage

Usage of the short echo tab, <?=, that echo’s directly the following content.

<?= $variable; ?>

1.2.10.1. Specs

Short name Php/EchoTagUsage
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Instant (5 mins)
Precision Very high
Features short-echo-tag
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.11. @ 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 very slow : it processes the error, and finally decides 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 avoids user’s error display, and keeps the error in the PHP logs, for later processing.

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

This was the case with fopen(), stream_socket_server(), token_get_all(). As of PHP 7.0, they are all hiding errors when @ is active.

Name Default Type Description
authorizedFunctions noscream_functions.json data Functions that are authorized to sports a @.

See also I scream, you scream, we all scream for @, Error Control Operators and Five reasons why the shut-op operator should be avoided.

1.2.11.1. Suggestions

  • Remove the @ operator by default

1.2.11.2. Specs

Short name Structures/Noscream
Rulesets All, Analyze, Appinfo, CE, CI-checks, Performances
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features class
ClearPHP no-noscream
Examples Phinx, PhpIPAM
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.12. Abstract Away

Avoid using PHP native functions that produce data directly 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 custom interface.

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.

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

1.2.12.1. Suggestions

  • Abstract away the calls to native PHP functions, and upgrade the unit tests

1.2.12.2. Specs

Short name Patterns/AbstractAway
Rulesets All, Suggestions
Exakat since 2.1.5
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Medium
Available in Entreprise Edition, Exakat Cloud

1.2.13. Abstract Class Constants

Those are class constants which are defined in multiple children, but not in the parent class.

If this class is a feature of the parent class, or shall and must be defined in the children classes, it is recommended to add them in the parent class, and let them overloaded in the children class.

In the illustration below, CONSTA is defined in all two children, but not in the parent class. A third children would miss the constants definitions, until an error has been reported.

<?php

class A {
    // no constant
}

class A1 extends A {
    public const CONSTA = 1;
}

class A2 extends A {
    public const CONSTA = 2;
}

?>
Name Default Type Description
minimum 2 integer Minimal number of constant found in children to report this as a potential abstract class.

See also I often find myself wishing for abstract constants in PHP.

1.2.13.1. Suggestions

  • Define the constants in the parent class, with some neutral value

1.2.13.2. Specs

Short name Classes/AbstractConstants
Rulesets All, Class Review
Exakat since 2.3.3
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Medium
Features class-constant, abstract
Available in Entreprise Edition, Exakat Cloud

1.2.14. Abstract Class Usage

List of all abstract classes being used.
<?php

abstract class foo {
    function foobar();
}

class bar extends foo {
    // extended method
    function foobar() {
        // doSomething()
    }

    // extra method
    function barbar() {
        // doSomething()
    }
}
?>

See also Classes abstraction.

1.2.14.1. Specs

Short name Classes/Abstractclass
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features class, abstract
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.15. Abstract Methods Usage

List of all abstract methods being used.
<?php

// abstract class
abstract class foo {
    // abstract method
    function foobar();
}

class bar extends foo {
    // extended abstract method
    function foobar() {
        // doSomething()
    }

    // extra method
    function barbar() {
        // doSomething()
    }
}
?>

See also Classes abstraction.

1.2.15.1. Specs

Short name Classes/Abstractmethods
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features class, abstract
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

1.2.16.1. Suggestions

  • Implements all the abstract methods of the class
  • Make the class abstract

1.2.16.2. Specs

Short name Classes/AbstractOrImplements
Rulesets All, Analyze, LintButWontExec
Exakat since 1.3.3
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features abstract, implements
Examples Zurmo
Note This issue may lint but will not run
Available in Entreprise Edition, Exakat Cloud

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

1.2.17.1. Suggestions

  • Remove abstract keyword from the method
  • Remove static keyword from the method
  • Remove the method

1.2.17.2. Specs

Short name Classes/AbstractStatic
Rulesets All, Analyze
Exakat since 0.8.4
PHP Version With PHP 7.0 and older
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features method, abstract, constant
Available in Entreprise Edition, Exakat Cloud

1.2.18. Access Protected Structures

It is not allowed to access protected properties, methods or constants 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.

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

1.2.18.2. Specs

Short name Classes/AccessProtected
Rulesets All, Analyze, IsExt, IsPHP, IsStub
Exakat since 0.8.4
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision High
Features visibility
Configurable by php_core, php_extensions, stubs
Available in Entreprise Edition, Exakat Cloud

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

?>

1.2.19.1. Specs

Short name Classes/AccessPrivate
Rulesets All, Analyze
Exakat since 0.8.4
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features class, private
Available in Entreprise Edition, Exakat Cloud

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

1.2.20.1. Suggestions

  • Add a default value for parameters

1.2.20.2. Specs

Short name Functions/AddDefaultValue
Rulesets All, Suggestions
Exakat since 1.4.5
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features default-value
Examples Zurmo, Typo3
Available in Entreprise Edition, Exakat Cloud

1.2.21. Add Return Typehint

Add obvious returntypes to methods, functions, closures and arrow functions.
<?php

// This has no type, but could use int
function foo() {
    return 1;
}

?>

1.2.21.1. Specs

Short name Complete/ReturnTypehint
Rulesets All, First, NoDoc
Exakat since 2.3.9
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Available in Entreprise Edition, Exakat Cloud

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

This analysis also report using + with data containers, which triggers an automated conversion 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;

$z = '12';
print +$z + 1;

?>

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.

1.2.22.1. Suggestions

  • Remove the +/- 0, may be the whole assignation
  • Use an explicit type casting operator (int)

1.2.22.2. Specs

Short name Structures/AddZero
Rulesets All, Analyze, CE, CI-checks, Rector
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Instant (5 mins)
Precision High
Features addition
ClearPHP no-useless-math
Examples Thelia, OpenEMR
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.23. Aliases

List of all aliases used, to alias namespaces.
<?php

// This is an alias
use stdClass as aClass;

// This is not an alias : it is not explicit
use stdClass;

trait t {
    // This is not an alias, it's a trait usage
    use otherTrait;
}

?>

See also Using namespaces: Aliasing/Importing and A Complete Guide to PHP Namespaces.

1.2.23.1. Specs

Short name Namespaces/Alias
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features namespace
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

1.2.24.1. Specs

Short name Variables/VariableUppercase
Rulesets All, Coding conventions
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Features variable
Available in Entreprise Edition, Exakat Cloud

1.2.25. All strings

Strings, heredocs and nowdocs in one place.
<?php

$string = 'string';

$query = <<<SQL
Heredoc
SQL;

?>

1.2.25.1. Specs

Short name Type/CharString
Rulesets All, Inventory
Exakat since 0.10.1
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features string, heredoc, nowdoc
Available in Entreprise Edition, Exakat Cloud

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

?>

This analysis may report classes which do not explicitly implements any interfaces : the issue is then coming from the parents.

1.2.26.1. Suggestions

  • Keep the implements call in the class that do implements the methods. Remove it from the children classes.

1.2.26.2. Specs

Short name Interfaces/AlreadyParentsInterface
Rulesets All, Analyze, IsExt, IsPHP, IsStub, Suggestions
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Instant (5 mins)
Precision Very high
Features implements, inheritance
Configurable by php_core, php_extensions, stubs
Examples WordPress, Thelia
Available in Entreprise Edition, Exakat Cloud

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

1.2.27.1. Suggestions

  • Eliminate the trait in the parent class
  • Eliminate the trait in the child class

1.2.27.2. Specs

Short name Traits/AlreadyParentsTrait
Rulesets All, Analyze
Exakat since 1.8.0
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features trait
Available in Entreprise Edition, Exakat Cloud

1.2.28. Altering Foreach Without Reference

Foreach() loop that could use a reference as value.

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

array_walk() and array_map() are also alternative to prevent the use of foreach(), when $key is not used.

See also foreach.

1.2.28.1. Suggestions

  • Add the reference on the modified blind variable, and avoid accessing the source array

1.2.28.2. Specs

Short name Structures/AlteringForeachWithoutReference
Rulesets All, Analyze, CE, CI-checks
Exakat since 0.8.4
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision High
Features foreach, loop
ClearPHP use-reference-to-alter-in-foreach
Examples Contao, WordPress
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

?>

1.2.29.1. Specs

Short name Structures/AlternativeConsistenceByFile
Rulesets All, Analyze
Exakat since 0.11.2
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision High
Features alternative-syntax
Available in Entreprise Edition, Exakat Cloud

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

1.2.30.1. Suggestions

  • Add an anchor to the beginning and ending of the string

1.2.30.2. Specs

Short name Security/AnchorRegex
Rulesets All, Security
Exakat since 0.12.15
PHP Version All
Severity Major
Time To Fix Instant (5 mins)
Precision High
Features regex
Available in Entreprise Edition, Exakat Cloud

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

?>

1.2.31.1. Suggestions

  • Compare count() to non-zero values
  • Use empty()

1.2.31.2. Specs

Short name Structures/NeverNegative
Rulesets All, Analyze, CE, CI-checks
Exakat since 0.8.4
PHP Version All
Severity Major
Time To Fix Instant (5 mins)
Precision Very high
Examples Magento
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.32. Always Use Function With array_key_exists()

array_key_exists() has been granted a special virtual machine 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 specially compiled functions.

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

1.2.32.2. Specs

Short name Performances/Php74ArrayKeyExists
Rulesets All, Performances
Exakat since 1.8.4
PHP Version With PHP 7.4 and more recent
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features vm, opcode
Available in Entreprise Edition, Exakat Cloud

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

1.2.33.1. Suggestions

  • Only use string or integer as key for an array.
  • Use transtyping operator (string) and (int) to make sure of the type

1.2.33.2. Specs

Short name Arrays/AmbiguousKeys
Rulesets All, Analyze, Semantics
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features array
Examples PrestaShop, Mautic
Available in Entreprise Edition, Exakat Cloud

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

?>

1.2.34.1. Specs

Short name Classes/AmbiguousStatic
Rulesets All, Analyze, Semantics
Exakat since 1.0.3
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Features static
Available in Entreprise Edition, Exakat Cloud

1.2.35. Ambiguous Types With Variables

The same variable is assigned various types, in different methods. This means that one may expect the same named variable to behave differently in different context.
<?php

function foo() {
     $i = 1;
     $user = new User();
}

function goo() {
     $i = 2; // $i is always an integer
     $user = new Propect();  // Sometimes $user is a User, and sometimes it is a Propect
}

?>

1.2.35.1. Specs

Short name Variables/AmbiguousTypes
Rulesets All, Semantics
Exakat since 2.5.0
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Available in  

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

?>

1.2.36.1. Suggestions

  • Sync visibilities for both properties, in the different classes
  • Use different names for properties with different usages

1.2.36.2. Specs

Short name Classes/AmbiguousVisibilities
Rulesets All, Analyze, Semantics
Exakat since 1.3.4
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Features class, visibility
Examples Typo3
Available in Entreprise Edition, Exakat Cloud

1.2.37. An OOP Factory

A method or function that implements a factory. A factory is a class that handles the creation of an object, based on parameters. The factory hides the logic that leads to the creation of the object.
<?php
    class AutomobileFactory {
        public static function create($make, $model) {
            $className = \\Automaker\\Brand\\$make;
            return new $className($model);
        }
    }

    // The factory is able to build any car, based on their
    $fuego = AutomobileFactory::create('Renault', 'Fuego');

    print_r($fuego->getMakeAndModel()); // outputs Renault Fuego
?>

See also Factory (object-oriented programming) and Factory.

1.2.37.1. Specs

Short name Patterns/Factory
Rulesets All, Appinfo, CE
Exakat since 1.6.7
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features pattern
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.38. Anonymous Classes

Anonymous classes.
<?php

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

?>

See also Anonymous classes.

1.2.38.1. Specs

Short name Classes/Anonymous
Rulesets All, Appinfo, CE, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56
Exakat since 0.8.4
PHP Version With PHP 7.0 and more recent
Severity Major
Time To Fix Slow (1 hour)
Precision Very high
Features class, anonymous-class, abstract
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.39. Argon2 Usage

Argon2 is an optionally compiled password hashing API.

Argon2 has been added to the password hashing API in PHP 7.2.

It is not available in older version. It also requires PHP to be compiled with the –with-password-argon2 option.

<?php

// Hashing a password with argon2
$hash = password_hash('password', PASSWORD_ARGON2I, ['memory_cost' => 1<<17,
                                                     'time_cost'   => PASSWORD_ARGON2_DEFAULT_TIME_COST,
                                                     'threads'     => PASSWORD_ARGON2_DEFAULT_THREADS]);

?>

See also Argon2 Password Hash.

1.2.39.1. Specs

Short name Php/Argon2Usage
Rulesets All, Appinfo, CE
Exakat since 1.0.4
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Features argon2
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.40. Argument Could Be Iterable

This argument could use the iterable typehint.
<?php

function foo($a) {
    foreach($a as $b) {

    }
}

?>

See also iterable.

1.2.40.1. Suggestions

  • Add the iterable typehint

1.2.40.2. Specs

Short name Classes/CouldBeIterable
Rulesets All, Suggestions, Typechecks
Exakat since 2.3.3
PHP Version With PHP 7.1 and more recent
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features iterable
Available in Entreprise Edition, Exakat Cloud

1.2.41. Argument Counts Per Calls

Collects the number of arguments passed to PHP functions.

This is focused on PHP native functions, with optional characters.

<?php

// One entry, in_array 2 arguments
$c = in_array($array, $needle);

// One entry, in_array 3 arguments
$c = in_array($array, $needle, true);

?>

This helps detect unused or lesser know arguments.

1.2.41.1. Specs

Short name Dump/ArgumentCountsPerCalls
Rulesets All, Dump
Exakat since 2.5.2
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Available in  

1.2.42. 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 <https://www.php.net/manual/en/class.`closure.php>`_ arguments are omitted.

See also Type declarations.

1.2.42.1. Suggestions

  • Add the typehint to the function arguments

1.2.42.2. Specs

Short name Functions/ShouldBeTypehinted
Rulesets All, Typechecks
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Features typehint
ClearPHP always-typehint
Examples Dolphin, Mautic
Available in Entreprise Edition, Exakat Cloud

1.2.43. Array Access On Literal Array

Accessing an element on a literal array makes that array non-reusable.

It is recommended to make this array a constant or a property, for easier reusage. It also make that content more visiblem in the class definitions.

<?php

class Suit {
     const NAMES = ['Club' => 1, 'Spade' => 2, 'Heart' => 3, 'Diamond' => 4];

     function __construct($name) {
             if (!isset(self::NAMES[$name]) {
                     throw new \Exception('Not a suit color');
             }
     }
}

class HiddenSuitList {
     function __construct($name) {
             if (!isset(['Club' => 1, 'Spade' => 2, 'Heart' => 3, 'Diamond' => 4][$name]) {
                     throw new \Exception('Not a suit color');
             }
     }
}

?>

1.2.43.1. Suggestions

  • Make the literal array a constant or a property

1.2.43.2. Specs

Short name Structures/ArrayAccessOnLiteralArray
Rulesets All, Semantics
Exakat since 2.5.2
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Available in  

1.2.44. Array Addition

Addition where one of the operands are arrays.
<?php
    $a = [1] + [2 ,3];
?>

See also Combining arrays using + versus array_merge in PHP and Array operators.

1.2.44.1. Specs

Short name Structures/ArrayAddition
Rulesets All, Appinfo
Exakat since 2.4.2
Severity  
Time To Fix  
Precision High
Available in Entreprise Edition, Exakat Cloud

1.2.45. Array Index

List of all indexes used in arrays.
<?php

// Index
$x['index'] = 1;

// in array creation
$a = array('index2' => 1);
$a2 = ['index3' => 2];

?>

1.2.45.1. Specs

Short name Arrays/Arrayindex
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision High
Features array
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.46. Array() / [ ] Consistence

array() or [ ] is the favorite.

array() and [ ] have the same functional use.

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

It happens that array() or [] are used depending on coding style and files. One file may be consistently using array(), while the others are all using [].

<?php

$a = array(1, 2);
$b = array(array(3, 4), array(5, 6));
$c = array(array(array(7, 8), array(9, 10)), array(11, 12), array(13, 14)));

// be consistent
$d = [1, 3];
?>

The only drawback to use [] over array() is backward incompatibility.

Name Default Type Description
array_ratio 10 integer Percentage of arrays in one of the syntaxes, to trigger the other syntax as a violation.

1.2.46.1. Suggestions

  • Use one syntax consistently.

1.2.46.2. Specs

Short name Arrays/ArrayBracketConsistence
Rulesets All, Preferences
Exakat since 0.8.9
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision High
Available in Entreprise Edition, Exakat Cloud

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

1.2.47.1. Suggestions

  • Use a loop to fill in the array with cloned() objects.

1.2.47.2. Specs

Short name Structures/ArrayFillWithObjects
Rulesets All, Analyze
Exakat since 2.1.9
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features array, object
Available in Entreprise Edition, Exakat Cloud

1.2.48. Array_Map() Passes By Value

array_map() requires the callback to receive elements by value. Unlike array_walk(), which accepts by value or by reference, depending on the action taken.

PHP 8.0 and more recent emits a Warning

<?php
// Example, courtery of Juliette Reinders Folmer
function trimNewlines(&$line, $key) {
    $line = str_replace(array(\n, \r), '', $line);
}

$original = [
    text\n\n,
    text\n\r
];

$array = $original;
array_walk($array, 'trimNewlines');

var_dump($array);

array_map('trimNewlines', $original, [0, 1]);

?>

See also array_map.

1.2.48.1. Suggestions

  • Make the callback first argument a reference

1.2.48.2. Specs

Short name Structures/ArrayMapPassesByValue
Rulesets All, Analyze, CE, CompatibilityPHP80, IsExt, IsPHP, IsStub
Exakat since 2.2.0
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Medium
Features array, map, by-value, by-reference
Configurable by php_core, php_extensions, stubs
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

1.2.49.1. Suggestions

  • Use `array(array())` or `[[]]` as default value for array_merge()
  • Remove any non-array value from the values in the default array

1.2.49.2. Specs

Short name Structures/ArrayMergeArrayArray
Rulesets All, Analyze
Exakat since 2.1.4
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features array
Available in Entreprise Edition, Exakat Cloud

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

1.2.50.1. Suggestions

  • Rename the custom function with another name

1.2.50.2. Specs

Short name Php/AssertFunctionIsReserved
Rulesets All, Analyze, Changed Behavior, CompatibilityPHP73, Deprecated
Exakat since 1.3.9
PHP Version All
Severity Critical
Time To Fix Slow (1 hour)
Changed Behavior PHP 7.2
Precision Very high
Features assertion
Available in Entreprise Edition, Exakat Cloud

1.2.51. Assertions

Usage of assertions, to add checks within PHP code.

Assertions should be used as a debugging feature only. You may use them for sanity-checks that test for conditions that should always be TRUE and that indicate some programming errors if not or to check for the presence of certain features like extension functions or certain system limits and features.

<?php

function foo($string) {
    assert(!empty($string), 'An empty string was provided!');

    echo '['.$string.']';
}

?>

See also assert.

1.2.51.1. Specs

Short name Php/AssertionUsage
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features assertion
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

1.2.52.1. Suggestions

  • Use parenthesis
  • Separate assignation and comparison
  • Drop assignation or comparison

1.2.52.2. Specs

Short name Structures/AssigneAndCompare
Rulesets All, Analyze, CE, CI-checks
Exakat since 1.6.3
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features assignation, comparison
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

See also PHP Default parameters.

1.2.53.1. Suggestions

  • Add a default value whenever possible. This is easy for scalars, and array()

1.2.53.2. Specs

Short name Classes/MakeDefault
Rulesets All, Analyze
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Instant (5 mins)
Precision Very high
Features default-value
ClearPHP use-properties-default-values
Examples LiveZilla, phpMyAdmin
Available in Entreprise Edition, Exakat Cloud

1.2.54. Assign With And Precedence

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.

1.2.54.1. Suggestions

  • Use symbolic operators rather than letter ones
  • To be safe, add parenthesis to enforce priorities

1.2.54.2. Specs

Short name Php/AssignAnd
Rulesets All, Analyze, CE, CI-checks
Exakat since 0.12.4
PHP Version All
Severity Critical
Time To Fix Quick (30 mins)
Precision Very high
Features precedence, operator, logical-operator
Examples xataface
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.55. Assigned In One Branch

Report variables that are assigned in one branch, and not in the other.
<?php

if ($condition) {
    // $assigned_in_this_branch is assigned in only one of the branches
    $assigned_in_this_branch = 1;
    $also_assigned = 1;
} else {
    // $also_assigned is assigned in the two branches
    $also_assigned = 1;
}

?>

1.2.55.1. Suggestions

  • Assign in the second branch
  • Assign outside the condition

1.2.55.2. Specs

Short name Structures/AssignedInOneBranch
Rulesets All
Exakat since 1.0.5
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features assignation
Available in Entreprise Edition, Exakat Cloud

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

?>

1.2.56.1. Suggestions

  • Remove the first assignation
  • Remove the second assignation
  • Change the name of the variable in one or both cases

1.2.56.2. Specs

Short name Variables/AssignedTwiceOrMore
Rulesets All, Analyze
Exakat since 0.9.8
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features variable
Available in Entreprise Edition, Exakat Cloud

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

1.2.57.1. Suggestions

  • Make the context of the code more explicit
  • Use a class to handle specific array index
  • Avoid using named index by using foreach()

1.2.57.2. Specs

Short name Php/Assumptions
Rulesets All, Analyze
Exakat since 2.1.9
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features assumption
Available in Entreprise Edition, Exakat Cloud

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

1.2.58.1. Suggestions

  • Change the variable on the left of the append
  • Change the variable on the right of the append

1.2.58.2. Specs

Short name Performances/Autoappend
Rulesets All, Performances
Exakat since 1.8.3
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Available in Entreprise Edition, Exakat Cloud

1.2.59. Autoloading

Usage of the autoloading feature of PHP.
<?php

spl_autoload_register('my_autoloader');

// Old way to autoload. Deprecated in PHP 7.2
function __autoload($class ) {}

?>

Defining the __autoload() function is obsolete since PHP 7.2.

See also __autoload.

1.2.59.1. Specs

Short name Php/AutoloadUsage
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features autoload
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

1.2.60.1. Suggestions

  • Collect all pieces in an array, then implode() the array in one call.

1.2.60.2. Specs

Short name Performances/NoConcatInLoop
Rulesets All, Performances, Top10
Exakat since 0.12.4
PHP Version All
Severity Major
Time To Fix Slow (1 hour)
Precision Very high
Features loop
Examples SuiteCrm, ThinkPHP
Available in Entreprise Edition, Exakat Cloud

1.2.61. 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, use a constant or even a global variable.

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

?>

1.2.61.1. Suggestions

  • Make the literal a global constant or a class constant
  • Make the literal an argument, so it can be injected
  • Make the literal an property, with the array as default value
  • Make the literal an static variable, with the array as default value

1.2.61.2. Specs

Short name Structures/NoAssignationInFunction
Rulesets All, Performances
Exakat since 0.9.7
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision High
Features array
Available in Entreprise Edition, Exakat Cloud

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

1.2.62.1. Suggestions

  • Use a null object to fill any missing value
  • Make sure the property is set at constructor time

1.2.62.2. Specs

Short name Classes/AvoidOptionalProperties
Rulesets All, Analyze
Exakat since 0.12.0
PHP Version All
Severity Major
Time To Fix Slow (1 hour)
Precision Very high
Features property, null
Examples ChurchCRM, Dolibarr
Available in Entreprise Edition, Exakat Cloud

1.2.63. Avoid Parenthesis With Language Construct

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.

1.2.63.1. Suggestions

  • Remove the parenthesis

1.2.63.2. Specs

Short name Structures/PrintWithoutParenthesis
Rulesets All, Analyze, CE, CI-checks
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features language-construct
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

1.2.64.1. Suggestions

  • Replace is_real() by is_float()
  • Replace (real) by (float)

1.2.64.2. Specs

Short name Php/AvoidReal
Rulesets All, Suggestions, Top10
Exakat since 1.3.9
PHP Version With PHP 8.0 and older
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features real
Available in Entreprise Edition, Exakat Cloud

1.2.65. 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 would happen at execution time, leading to undefined errors.

self may be used for typing : then, argument types in the host class must use the interface name, and can’t use self nor the class name, for compatibility reason. self can be used for returntype, as expected.

parent has the same behavior than self, except that it cannot be used inside an interface. This is one of those error that lint but won’t execute in certain conditions : namely, when a class implements the interface with parent, but has no parent by itself. This is now a dependency to the host class.

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

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

    // This makes interface i dependant on the host class, where X must be defined.
    // It actually yields an error :  Undefined class constant 'self::I'
    public const I4 = self::X;
}

class x implements k {
    const X = 1;
}
?>

See also Scope Resolution Operator (::).

1.2.65.1. Suggestions

  • Use a fully qualified namespace instead of self
  • Use a locally defined constant, so self is a valid reference

1.2.65.2. Specs

Short name Interfaces/AvoidSelfInInterface
Rulesets All, Class Review, LintButWontExec
Exakat since 1.5.4
PHP Version All
Severity Critical
Time To Fix Slow (1 hour)
Precision Very high
Features self, interface
Note This issue may lint but will not run
Available in Entreprise Edition, Exakat Cloud

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

1.2.66.1. Suggestions

  • Replace substr() with the array notations for strings.
  • Replace substr() with a call to mb_substr().

1.2.66.2. Specs

Short name Structures/NoSubstrOne
Rulesets All, Analyze, CE, CI-checks, CompatibilityPHP71, Performances, Suggestions, Top10
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Instant (5 mins)
Precision Very high
Examples ChurchCRM, LiveZilla
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.67. Avoid Those Hash Functions

The following cryptography algorithms are considered insecure, and should be replaced with new and more modern 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 cryptographic algorthim, used with a modern PHP extension (easier to update)
echo hash('md5', 'The quick brown fox jumped over the lazy dog.');

// Strong cryptographic 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.

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

1.2.67.2. Specs

Short name Security/AvoidThoseCrypto
Rulesets All, Security
Exakat since 0.8.4
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features hash
Available in Entreprise Edition, Exakat Cloud

1.2.68. 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. Some PHP or frameworks functions, such as json_encode(), do return them : this is fine, although it is reported here.

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

1.2.68.1. Suggestions

  • Create a custom class to handle the properties

1.2.68.2. Specs

Short name Php/UseStdclass
Rulesets All, Analyze
Exakat since 0.9.1
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Features stdclass
Available in Entreprise Edition, Exakat Cloud

1.2.69. Avoid array_push()

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

This is also true when the append operator is called several times, while array_push() is be called only once, with an arbitrary number of argument.

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

?>

It is a micro-optimisation.

1.2.69.1. Suggestions

  • Use the [] operator

1.2.69.2. Specs

Short name Performances/AvoidArrayPush
Rulesets All, Performances
Exakat since 0.9.1
PHP Version All
Severity Minor
Time To Fix Instant (5 mins)
Precision Very high
Available in Entreprise Edition, Exakat Cloud

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

1.2.70.1. Suggestions

  • Upgrade to PHP 7.2
  • Use an alternative way to make values unique in an array, using array_count_values(), for example.

1.2.70.2. Specs

Short name Structures/NoArrayUnique
Rulesets All, Performances
Exakat since 0.8.4
PHP Version With PHP 7.2 and older
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features array
Available in Entreprise Edition, Exakat Cloud

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

1.2.71.1. Specs

Short name Structures/UseInstanceof
Rulesets All, Analyze, CE, CI-checks
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features class
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.72. Avoid get_object_vars()

get_object_vars() changes behavior between PHP 7.3 and 7.4. It also behaves different within and outside a class.
<?php

// Illustration courtesy of Doug Bierer
$obj = new ArrayObject(['A' => 1,'B' => 2,'C' => 3]);
var_dump($obj->getArrayCopy());
var_dump(get_object_vars($obj));

?>

See also get_object_vars script on 3V4L and The Strange Case of ArrayObject.

1.2.72.1. Suggestions

  • Use ArrayObject and getArrayCopy() method

1.2.72.2. Specs

Short name Php/AvoidGetobjectVars
Rulesets All, CompatibilityPHP74, CompatibilityPHP80
Exakat since 2.2.1
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features class, arrayobject
Available in Entreprise Edition, Exakat Cloud

1.2.73. 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 explicitly 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://.

1.2.73.1. Suggestions

  • Use FilesystemIterator or DirectoryIterator classes.
  • Use RegexIterator to filter any unwanted results from FilesystemIterator.
  • Use glob protocol for files : $it = new DirectoryIterator(‘glob://path/to/examples/*.php’);

1.2.73.2. Specs

Short name Performances/NoGlob
Rulesets All, Performances
Exakat since 0.9.6
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features glob, directoryiterator, filesystemiterator
Examples Phinx, NextCloud
Available in Entreprise Edition, Exakat Cloud

1.2.74. 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 and DPC2019: Of representation and interpretation: A unified theory - Arnout Boks.

1.2.74.1. Suggestions

  • Store and transmit the data format

1.2.74.2. Specs

Short name Php/AvoidMbDectectEncoding
Rulesets All, Analyze
Exakat since 1.8.9
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features mbstring
Available in Entreprise Edition, Exakat Cloud

1.2.75. 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 __construct(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 __construct(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 and PHP RFC: Named Arguments (Type-safe and documented options).

1.2.75.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
  • Use named parameters to pass and document the arguments

1.2.75.2. Specs

Short name Classes/AvoidOptionArrays
Rulesets All, Analyze, Class Review
Exakat since 1.7.9
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Medium
Features constructor
Available in Entreprise Edition, Exakat Cloud

1.2.76. 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().

1.2.76.1. Suggestions

  • Remove the 6th argument of registered handlers.

1.2.76.2. Specs

Short name Php/AvoidSetErrorHandlerContextArg
Rulesets All, CompatibilityPHP72
Exakat since 1.0.4
PHP Version All
Severity Major
Time To Fix Slow (1 hour)
Precision High
Features error-handler
Examples shopware, Vanilla
Available in Entreprise Edition, Exakat Cloud

1.2.77. 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).

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

1.2.77.2. Specs

Short name Security/NoSleep
Rulesets All, Security
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features sleep, cli
Available in Entreprise Edition, Exakat Cloud

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

1.2.78.1. Suggestions

  • Avoid using names that doesn’t comply with PHP’s convention

1.2.78.2. Specs

Short name Constants/BadConstantnames
Rulesets All, Analyze
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Features constant
Examples PrestaShop, Zencart
Available in Entreprise Edition, Exakat Cloud

1.2.79. 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 typehints, so the two methods are compatible.

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

1.2.79.2. Specs

Short name Functions/BadTypehintRelay
Rulesets All, Typechecks
Exakat since 1.6.6
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features typehint
Available in Entreprise Edition, Exakat Cloud

1.2.80. Bail Out Early

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

The main benefit is to make clear the method applies a condition, and stop immediately when this condition is not satisfied. The main sequence is then focused on the important code.

This analysis 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).

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

1.2.80.2. Specs

Short name Structures/BailOutEarly
Rulesets All, Analyze
Exakat since 0.8.9
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features return
Examples OpenEMR, opencfp
Available in Entreprise Edition, Exakat Cloud

1.2.81. Binary Glossary

List of all the integer values using the binary format.
<?php

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

?>

See also Integer syntax.

1.2.81.1. Specs

Short name Type/Binary
Rulesets All, Appinfo, CE, CompatibilityPHP53, Inventory
Exakat since 0.8.4
PHP Version With PHP 5.4 and more recent
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features integer, binary-integer
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.82. Blind Variables

Variables that are used in foreach or for structure, for their managing the loop itself.
<?php
    foreach($array as $key => $value) {
        // $key and $value are blind values
    }

?>

1.2.82.1. Specs

Short name Variables/Blind
Rulesets All
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features variable-blind
Available in Entreprise Edition, Exakat Cloud

1.2.83. 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() and match() cannot be without bracket.

1.2.83.1. Suggestions

  • Assign in the second branch
  • Assign outside the condition

1.2.83.2. Specs

Short name Structures/Bracketless
Rulesets All, Coding conventions
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Instant (5 mins)
Precision Very high
Available in Entreprise Edition, Exakat Cloud

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

1.2.84.1. Specs

Short name Structures/BreakOutsideLoop
Rulesets All, Analyze, CompatibilityPHP70
Exakat since 0.8.4
PHP Version With PHP 7.0 and older
Severity Major
Time To Fix Slow (1 hour)
Precision Very high
Features break, loop
Available in Entreprise Edition, Exakat Cloud

1.2.85. Break With 0

It is not possible to break 0 : it 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;
        }
    }

?>

1.2.85.1. Suggestions

  • Remove 0, or the break

1.2.85.2. Specs

Short name Structures/Break0
Rulesets All, CompatibilityPHP53
Exakat since 0.8.4
PHP Version With PHP 5.4 and older
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Available in Entreprise Edition, Exakat Cloud

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

?>

1.2.86.1. Suggestions

  • Only use integer with break

1.2.86.2. Specs

Short name Structures/BreakNonInteger
Rulesets All, CompatibilityPHP54
Exakat since 0.8.4
PHP Version With PHP 5.4 and older
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Available in Entreprise Edition, Exakat Cloud

1.2.87. 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++) {

}
?>

1.2.87.1. Suggestions

  • Extract the assignation and set it on its own line, prior to the current expression.
  • Check if the local variable is necessary

1.2.87.2. Specs

Short name Structures/BuriedAssignation
Rulesets All, Analyze
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Examples XOOPS, Mautic
Available in Entreprise Edition, Exakat Cloud

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

1.2.88.1. Suggestions

  • Avoid using blind variables outside loops.
  • Store blind variables in local variables or properties for later reuse.

1.2.88.2. Specs

Short name Performances/CacheVariableOutsideLoop
Rulesets All, Performances
Exakat since 1.2.8
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Medium
Available in Entreprise Edition, Exakat Cloud

1.2.89. Call Order

This is a representation of the code. Each node is a function or method, and each link a is call from a method to another.

The only link is the possible call from a method to the other. All control flow is omitted, including conditional calls and loops.

<?php

    function foo() {
        bar();
        foobar();
    }

    function bar() {
        foobar();
    }

    function foobar() {

    }
?>

From the above script, the resulting network will display ‘foo() -> bar(), foo() -> foobar(), bar() -> foobar()’ calls.

1.2.89.1. Specs

Short name Dump/CallOrder
Rulesets All, CE, Dump
Exakat since 2.1.4
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.90. Callback Function 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.

1.2.90.1. Suggestions

  • Add an explicit return to the callback
  • Use null to unset elements in an array without destroying the index

1.2.90.2. Specs

Short name Functions/CallbackNeedsReturn
Rulesets All, Analyze, CE, CI-checks
Exakat since 1.2.6
PHP Version All
Severity Major
Time To Fix Instant (5 mins)
Precision High
Features callback
Examples Contao, Phpdocumentor
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.91. Calling Static Trait Method

Calling directly a static method, defined in a trait is deprecated. It emits a deprecation notice in PHP 8.1.
<?php

trait T {
    public static function t() {
        //
    }
}

T::t();

?>

Calling the same method, from the class point of view is valid.

See also PHP RFC: Deprecations for PHP 8.1.

1.2.91.1. Suggestions

  • Call the method from one of the class using the trait
  • Move the method to a class

1.2.91.2. Specs

Short name Php/CallingStaticTraitMethod
Rulesets All, CompatibilityPHP81, Deprecated
Exakat since 2.2.5
PHP Version With PHP 8.1 and older
Severity Minor
Time To Fix Quick (30 mins)
Precision Medium
Features trait, static-method
Available in Entreprise Edition, Exakat Cloud

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

?>

See also Passing by Reference.

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

1.2.92.2. Specs

Short name Structures/CalltimePassByReference
Rulesets All, CompatibilityPHP54
Exakat since 0.8.4
PHP Version With PHP 5.4 and older
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features by-value, by-reference
Available in Entreprise Edition, Exakat Cloud

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

1.2.93.1. Suggestions

  • Add a check before using count such as a type check

1.2.93.2. Specs

Short name Structures/CanCountNonCountable
Rulesets All, CompatibilityPHP72
Exakat since 1.0.4
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features countable
Available in Entreprise Edition, Exakat Cloud

1.2.94. Can’t Disable Class

This is the list of potentially dangerous PHP class being used in the code, such as `Phar <https://www.php.net/phar>`_.
<?php

// This script uses ftp_connect(), therefore, this function shouldn't be disabled.
$phar = new Phar();

?>

This analysis is the base for suggesting values for the disable_classes directive.

1.2.94.1. Specs

Short name Security/CantDisableClass
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features disable-classes
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.95. Can’t Disable Function

This is the list of potentially dangerous PHP functions being used in the code, such as exec() or fsockopen().

eval() is not reported here, as it is not a PHP function, but a language construct : it can’t be disabled.

<?php

// This script uses ftp_connect(), therefore, this function shouldn't be disabled.
$ftp = ftp_connect($host, 21);

// This script doesn't use imap_open(), therefore, this function may be disabled.

?>

This analysis is the base for suggesting values for the disable_functions directive.

1.2.95.1. Specs

Short name Security/CantDisableFunction
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity Major
Time To Fix Slow (1 hour)
Precision High
Features disable-functions
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

1.2.96.1. Suggestions

  • Remove the final keyword
  • Remove the extending class

1.2.96.2. Specs

Short name Classes/CantExtendFinal
Rulesets All, Analyze, Dead code, IsExt, IsPHP, IsStub
Exakat since 0.8.4
PHP Version All
Severity Critical
Time To Fix Instant (5 mins)
Precision Medium
Features final
Configurable by php_core, php_extensions, stubs
Available in Entreprise Edition, Exakat Cloud

1.2.97. Can’t Implement Traversable

It is not possible to implement the Traversable interface. The alternative is to implement Iterator or IteratorAggregate, which also implements Traversable.

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.

1.2.97.1. Suggestions

  • Implement Iterator or IteratorAggregate

1.2.97.2. Specs

Short name Interfaces/CantImplementTraversable
Rulesets All, Analyze, CE, CI-checks, LintButWontExec
Exakat since 1.9.8
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features interface
Note This issue may lint but will not run
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.98. Can’t 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

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

1.2.98.1. Suggestions

  • Make the constructor public
  • Create a factory, as a static method, in that class, to create objects
  • Remove the new call

1.2.98.2. Specs

Short name Classes/CantInstantiateClass
Rulesets All, Analyze
Exakat since 1.2.8
PHP Version All
Severity Critical
Time To Fix Quick (30 mins)
Precision High
Features constructor, visibility
Examples WordPress
Available in Entreprise Edition, Exakat Cloud

1.2.99. Can’t Throw Throwable

Classes extending Throwable can’t be thrown, unless they also extend Exception. The same applies to interfaces that extends Throwable.

Although such 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 <https://www.php.net/throwable>`_.php>`_, extend `Exception <https://www.php.net/exception>`_ or `Error <https://www.php.net/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.

1.2.99.1. Suggestions

  • Extends the Exception class
  • Extends the Error class

1.2.99.2. Specs

Short name Exceptions/CantThrow
Rulesets All, Analyze, LintButWontExec
Exakat since 1.3.3
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Features throwable
Note This issue may lint but will not run
Available in Entreprise Edition, Exakat Cloud

1.2.100. 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).

Name Default Type Description
cancelThreshold 75 integer Minimal number of cancelled methods to suggest the cancellation of the parent.

1.2.100.1. Suggestions

  • Drop the common method, and the cancelled methods in the children
  • Fill the children’s methods with actual code

1.2.100.2. Specs

Short name Classes/CancelCommonMethod
Rulesets All, Class Review, Suggestions
Exakat since 2.1.8
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features class
Available in Entreprise Edition, Exakat Cloud

1.2.101. Cancelled Parameter

A parameter is cancelled, when its value is hardcoded, and cannot be changed by the calling expression. The argument is in the signature, but it is later hardcoded to a literal value : thus, it is not usable, from the caller point of view.

Reference argument are omitted in this rule, as their value changes, however hardcoded, may have an impact on the calling code.

<?php

function foo($a, $b) {
    // $b is cancelled, and cannot be changed.
    $b = 3;

    // $a is the only parameter here
    return $a + $b;
}

function bar($a, $b) {
    // $b is actually processed
    $c = $b;
    $c = process($c);

    $b = $c;

    // $a is the only parameter here
    return $a + $b;
}

?>

1.2.101.1. Suggestions

  • Remove the parameter in the method signature

1.2.101.2. Specs

Short name Functions/CancelledParameter
Rulesets All, Analyze
Exakat since 2.2.0
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Available in Entreprise Edition, Exakat Cloud

1.2.102. Cannot Call Static Trait Method Directly

From the migration docs : Calling a static method, or accessing a static property directly on a trait is deprecated. Static methods and properties should only be accessed on a class using the trait.
<?php
 trait t { static public function t() {}}
 a::t();
// OK
 t::t();
 //Calling static trait method t::t is deprecated, it should only be called on a class using the trait

 class a {
    use t;
 }

?>

See also Calling a static element on a trait.

1.2.102.1. Suggestions

  • Use the trait in a class, and call the method from the class.

1.2.102.2. Specs

Short name Traits/CannotCallTraitMethod
Rulesets All, Analyze, CompatibilityPHP81, CompatibilityPHP82
Exakat since 2.3.1
PHP Version With PHP 8.1 and older
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features trait, static-method
Available in Entreprise Edition, Exakat Cloud

1.2.103. Cannot Use Static For Closure

The reported closures and arrow functions cannot use the static keyword.

Closures that makes use of the $this pseudo-variable cannot use the static keyword, at it prevents the import of the $this context in the closure <https://www.php.net/`closure>`_. It will fail at execution.

Closures that makes use of the bindTo() method, to change the context of execution, also cannot use the static keyword. Even if $this is not used in the closure <https://www.php.net/`closure>`_, the static keyword prevents the call to bindTo().

<?php

class x {
    function foo() {
        // Not possible, $this is now undefined in the body of the closure
        static function () { return $this->a;};
    }

    function foo2() {
        // Not possible, $this is now undefined in the body of the arrow function
        static fn () => $this->a;
    }

    function foo3() {
        // Not possible, the closure gets a new context before being called.
        $a = static fn () => $ba;
        $this->foo4($a);
    }

    function foo4($c) {
        $c->bindTo($this);
        $c();
    }

}
?>

See also Static anonymous functions.

1.2.103.1. Suggestions

  • Remove the static keyword
  • Remove the call to bindTo() method
  • Remove the usage of the $this variable

1.2.103.2. Specs

Short name Functions/CannotUseStaticForClosure
Rulesets All, Analyze
Exakat since 2.2.2
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Medium
Features closure, static
Available in Entreprise Edition, Exakat Cloud

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

1.2.104.1. Suggestions

  • Avoid inheriting abstract methods for compatibility beyond 7.2 (and older)

1.2.104.2. Specs

Short name Classes/CantInheritAbstractMethod
Rulesets All, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71
Exakat since 0.11.8
PHP Version With PHP 7.2 and more recent
Severity Critical
Time To Fix Quick (30 mins)
Precision Very high
Features abstract
Available in Entreprise Edition, Exakat Cloud

1.2.105. Cant Overload Constants

It was not possible to overload class constants within a class, when the constant was defined in an interface.
<?php

interface i {
    const A = 1;
}

//This lints, but doesn't executin in PHP 8.0 and older.
class x implements i {
    const A = 1;
}

?>

See also interface constants <https://www.php.net/manual/en/language.oop5.interfaces.php#language.oop5.interfaces.constants>.

1.2.105.1. Suggestions

  • Avoid overloading constants
  • Define the constants only in the classes

1.2.105.2. Specs

Short name Interfaces/CantOverloadConstants
Rulesets All, Changed Behavior, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73, CompatibilityPHP74, CompatibilityPHP80, LintButWontExec
Exakat since 2.3.2
Severity Minor
Time To Fix Quick (30 mins)
Changed Behavior PHP 8.1
Precision High
Features interface, class
Note This issue may lint but will not run
Available in Entreprise Edition, Exakat Cloud

1.2.106. Cant Overwrite Final Constant

A constant may be final, and can’t be overwritten in a child class.
<?php

class y extends x {
    const F = 1;
    const P = 2;
}

class x {
    final const F = 3;
    private const PRI = 5; // Private can't be final
    const P = 4;
}

?>

1.2.106.1. Suggestions

  • Remove the final keyword in the parent class
  • Remove the class constant in the child class
  • Rename the class constant in the child class

1.2.106.2. Specs

Short name Classes/CantOverwriteFinalConstant
Rulesets All, Analyze, Class Review, IsExt, IsPHP, IsStub, LintButWontExec
Exakat since 2.3.9
PHP Version With PHP 8.1 and more recent
Severity Major
Time To Fix Slow (1 hour)
Precision High
Features final, overwrite
Configurable by php_core, php_extensions, stubs
Note This issue may lint but will not run
Available in Entreprise Edition, Exakat Cloud

1.2.107. Cant Overwrite Final Method

A method may be final, and can’t be overwritten in a child class.
<?php

class y extends x {
    function method() {}
}

class x {
    final function method() {}
}

?>

1.2.107.1. Suggestions

  • Remove the final keyword in the parent class
  • Remove the method in the child class
  • Rename the method in the child class

1.2.107.2. Specs

Short name Classes/CantOverwriteFinalMethod
Rulesets All, IsExt, IsPHP, IsStub
Exakat since 2.4.2
PHP Version With PHP 5.0 and more recent
Severity Major
Time To Fix Slow (1 hour)
Precision High
Features final, overwrite
Configurable by php_core, php_extensions, stubs
Available in Entreprise Edition, Exakat Cloud

1.2.108. Cant Use Function

Those functions only contains an error or an exception. As such, they are a warning that such function or method shouldn’t be used.
<?php

function obsoleteFoo() {
    throw new exception('Don\'t use obsoleteFoo() but rather the new version of foo().');
}
?>

1.2.108.1. Specs

Short name Functions/CantUse
Rulesets All, CE
Exakat since 1.8.7
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features function
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

1.2.109.1. Specs

Short name Php/CantUseReturnValueInWriteContext
Rulesets All, CompatibilityPHP53, CompatibilityPHP54
Exakat since 0.8.4
PHP Version With PHP 5.5 and more recent
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features return
Available in Entreprise Edition, Exakat Cloud

1.2.110. Case Insensitive Constants

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

This feature is deprecated since PHP 7.3 and is removed since 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.

1.2.110.1. Specs

Short name Constants/CaseInsensitiveConstants
Rulesets All, Appinfo, CE, CompatibilityPHP73, Deprecated
Exakat since 1.3.9
PHP Version With PHP 8.0 and older
Severity Critical
Time To Fix Slow (1 hour)
Precision Very high
Features dynamic-constant, constant
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

?>

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

1.2.111.2. Specs

Short name Structures/CastToBoolean
Rulesets All, Analyze
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Instant (5 mins)
Precision Very high
Features cast
Examples MediaWiki, Dolibarr
Available in Entreprise Edition, Exakat Cloud

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

1.2.112.1. Suggestions

  • Replace (unset) with a call to unset().
  • Remove the unset call altogether.

1.2.112.2. Specs

Short name Php/CastUnsetUsage
Rulesets All, CE, CompatibilityPHP80
Exakat since 2.1.8
PHP Version With PHP 8.0 and older
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features cast, unset
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.113. Cast Usage

List of all cast usage.

PHP does not require (or support) explicit type definition in variable declaration; a variable’s type is determined by the context in which the variable is used.

<?php

if (is_int($_GET['x'])) {
    $number = (int) $_GET['x'];
} else {
    error_display('a wrong value was provided for "x"');
}

?>

Until PHP 7.2, a (unset) operator was available. It had the same role as unset() as a function.

See also Type Juggling and unset.

1.2.113.1. Specs

Short name Php/CastingUsage
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features cast
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

1.2.114.1. Suggestions

  • Add parenthesis around the ternary operator
  • Skip the casting
  • Cast in another expression

1.2.114.2. Specs

Short name Structures/CastingTernary
Rulesets All, Analyze, CE, CI-checks
Exakat since 1.8.0
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features ternary, casting
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

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

1.2.115.2. Specs

Short name Structures/CatchShadowsVariable
Rulesets All, Analyze
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Instant (5 mins)
Precision Very high
Features try-catch
ClearPHP no-catch-overwrite
Examples PhpIPAM, SuiteCrm
Available in Entreprise Edition, Exakat Cloud

1.2.116. Catch With Undefined Variable

Always initialize every 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;
     }
?>

See also catch and Non-capturing exception catches in PHP 8.

1.2.116.1. Suggestions

  • Always define the variable used in the catch clause, before the try block.

1.2.116.2. Specs

Short name Exceptions/CatchUndefinedVariable
Rulesets All, Analyze
Exakat since 2.1.5
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Available in Entreprise Edition, Exakat Cloud

1.2.117. Caught Exceptions

Exceptions used in catch clause.
<?php

try {
    foo();
} catch (MyException $e) {
    fixException();
} finally {
    clean();
}

?>

1.2.117.1. Specs

Short name Exceptions/CaughtExceptions
Rulesets All
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features catch
Available in Entreprise Edition, Exakat Cloud

1.2.118. Caught Expressions

List of caught exceptions.
<?php

// This analyzer reports MyException and Exception
try {
    doSomething();
} catch (MyException $e) {
    fixIt();
} catch (\Exception $e) {
    fixIt();
}

?>

See also Exceptions.

1.2.118.1. Specs

Short name Php/TryCatchUsage
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features catch
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.119. Caught Variable

Catch clauses require an exception and a variable name. Often, the variable name is the same, $e, as learnt from the manual.

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.

<?php

try {
    // do Something()
}
catch (MyException1 $e)       { $log->log($e->getMessage();}
catch (MyException2 $e)       { $log->log($e->getMessage();}
catch (MyException3 $e)       { $log->log($e->getMessage();}
catch (MyException4 $e)       { $log->log($e->getMessage();}
catch (MyException5 $e)       { $log->log($e->getMessage();}
catch (MyException6 $e)       { $log->log($e->getMessage();}
catch (MyException7 $e)       { $log->log($e->getMessage();}
catch (MyException8 $e)       { $log->log($e->getMessage();}
catch (MyException9 $e)       { $log->log($e->getMessage();}
catch (MyException10 $e)      { $log->log($e->getMessage();}
catch (\RuntimeException $e)  { $log->log($e->getMessage();}
catch (\Error $error)         { $log->log($error->getMessage();}
catch (\Exception $exception) { $log->log($exception->getMessage();}
?>

1.2.119.1. Suggestions

  • Make all caught constant consistent, and avoid using them for something else

1.2.119.2. Specs

Short name Exceptions/CatchE
Rulesets All, Preferences
Exakat since 1.7.6
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Features exception
Available in Entreprise Edition, Exakat Cloud

1.2.120. Check All Types

When checking for type, avoid using else. Mention explicitly all tested types, and raise an exception when all available options have been exhausted : after all, this is when the code doesn’t know how to handle the datatype.

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.

1.2.120.1. Suggestions

  • Include a default case to handle all unknown situations
  • Include and process explicit types as much as possible

1.2.120.2. Specs

Short name Structures/CheckAllTypes
Rulesets All, Analyze
Exakat since 0.10.6
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Medium
Examples Zend-Config, Vanilla
Available in Entreprise Edition, Exakat Cloud

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

1.2.121.1. Suggestions

  • Lengthen the cryptographic key

1.2.121.2. Specs

Short name Security/CryptoKeyLength
Rulesets All, Security
Exakat since 2.1.1
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features cryptography, openssl
Available in Entreprise Edition, Exakat Cloud

1.2.122. Check Division By Zero

Always check before dividing by a value. If that value is cast to 0, PHP might stop the processing with an exception, or keep processing it with 0 as a result. Both will raise problems.

The best practise is to check the incoming value before attempting the division. On possible alternative is to catch the DivisionByZeroError <https://www.php.net/manual/en/class.`divisionbyzeroerror.php>`_ exception, that PHP 8.0 and more recent will raise.

<?php

// Check the value before usage
function foo($a = 1) {
    if ($a !== 0) {
        return 42 / $a;
    } else {
        // process an error
    }
}

// Check the value after usage (worse than the above)
function foo($a = 1) {
    try {
        return 42 / $a;
    } catch (DivisionByZero) {
        // fix the situation now
    }
}

// This might fails with a division by 0
function foo($a = 1) {
    return 42 / $a;
}

?>

See also DivisionByZeroError.

1.2.122.1. Specs

Short name Structures/CheckDivision
Rulesets All, Analyze
Exakat since 2.3.3
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features exception, arithmeticerror
Available in Entreprise Edition, Exakat Cloud

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

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

1.2.123.2. Specs

Short name Structures/CheckJson
Rulesets All, Analyze, CE, CI-checks
Exakat since 1.3.0
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features json
Examples Woocommerce
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

1.2.124.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()

1.2.124.2. Specs

Short name Classes/CheckOnCallUsage
Rulesets All, Analyze, CE, CI-checks
Exakat since 1.7.2
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features magic-method
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.125. Checks Property Existence

This analysis reports checks property existence.

In PHP 8.2, non-specified properties are discouraged : they should always be defined in the class code. When this guideline is applied, properties always exists, and a call to property_exists() is now useless.

Some situations are still legit : + When the class is stdClass, where no property is initially defined. This may be the case of JSON data, or arrays cast to objects. + When the class uses magic methods, in particular __get(), __set() and __isset().

<?php

class x {
    private $a = 1;

    function foo() {
        $this->cache = $this->a + 1;
    }

}

?>

In this analysis, isset() and property_exists() are both used to detect this checking behavior. property_exists() is actually the only method to actually check the existence, since isset() will confuse non-existing properties and null.

While the behavior is deprecated in PHP 8.2, it is recommended to review older code and remove it. It will both ensure forward compatibility and cleaner, faster local code.

1.2.125.1. Specs

Short name Classes/ChecksPropertyExistence
Rulesets All, CompatibilityPHP82
Exakat since 2.3.3
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features stdclass, property
Available in Entreprise Edition, Exakat Cloud

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

?>

1.2.126.1. Specs

Short name Classes/ChildRemoveTypehint
Rulesets All, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71, Typechecks
Exakat since 0.12.4
PHP Version With PHP 7.2 and more recent
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features typehint
Available in Entreprise Edition, Exakat Cloud

1.2.127. Class Const With Array

Constant defined with const keyword may be arrays, starting with PHP 5.6.
<?php

const MY_ARRAY = array();

class x {
     const MY_OTHER_ARRAY = [1, 2];
}
?>

1.2.127.1. Suggestions

  • Store the array in a variable
  • Upgrade to PHP 7.0 or more recent

1.2.127.2. Specs

Short name Php/ClassConstWithArray
Rulesets All, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55
Exakat since 0.8.4
PHP Version With PHP 5.5 and more recent
Severity Critical
Time To Fix Slow (1 hour)
Precision Very high
Features constant
Available in Entreprise Edition, Exakat Cloud

1.2.128. 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 and When to declare methods final.

1.2.128.1. Suggestions

  • Make the class final
  • Extends the class

1.2.128.2. Specs

Short name Classes/CouldBeFinal
Rulesets All, Analyze, Class Review
Exakat since 1.4.3
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features final
Available in Entreprise Edition, Exakat Cloud

1.2.129. Class Could Be Readonly

When all properties are readonly, it is possible to set the option at the class. This feature was introduced in PHP 8.2.
<?php

// This class could be readonly
class x {
     private readonly A $p;
     private readonly A $p2;
}

?>

See also PHP 8.2: readonly Classes.

1.2.129.1. Suggestions

  • Remove readonly from the properties, and add it to the classes.

1.2.129.2. Specs

Short name Classes/CouldBeReadonly
Rulesets All, Class Review, Suggestions
Exakat since 2.5.1
PHP Version With PHP 8.2 and more recent
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features readonly
Available in  

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

?>

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

1.2.130.2. Specs

Short name Php/ClassFunctionConfusion
Rulesets All, Semantics
Exakat since 0.10.2
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Features function, class
Available in Entreprise Edition, Exakat Cloud

1.2.131. Class Has Fluent Interface

Mark a class as such when it contains at least one fluent method. A fluent method is a method that returns $this, for chaining.
<?php

class foo {
    private $count = 0;

    function a() {
        ++$this->count;
        return $this;
    }

    function b() {
        $this->count += 2;
        return $this;
    }

    function c() {
        return $this->count;
    }
}

$bar = new foo();
print $bar->a()
          ->b()
          ->c();

// display 3 (1 + 2).

?>

See also Fluent interface are evil, The basics of Fluent interfaces in PHP and FluentInterface.

1.2.131.1. Specs

Short name Classes/HasFluentInterface
Rulesets All
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision High
Features fluent-interface
Available in Entreprise Edition, Exakat Cloud

1.2.132. Class Invasion

Class invasion happens when an object access another object’s private methods or property. This is possible from the scope of the class itself.

Class invasion is a PHP feature.

<?php

class x {
     private $p = 1;

     function foo(X $x) {
             // This is the normal access to private properties.
             $this->p = 3;
             // This is class invasion, as $x is a distinct object.
             $x->p = 2;
     }
}

?>

This applies to properties, constants and methods, static or not.

1.2.132.1. Specs

Short name Classes/ClassInvasion
Rulesets All, Class Review
Exakat since 2.5.1
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Medium
Features class-invasion
Available in  

1.2.133. Class Overreach

An object of class A may reach any private or protected properties, constants or methods in another object of the same class. This is a PHP feature, though seldom known.

This feature is also called class invasion.

<?php

class A {
    private $p = 1;

    public function foo(A $a) {
        return $a->p + 1;
    }
}

echo (new A)->foo(new A);

?>

See also Visibility from other objects and spatie/invade.

1.2.133.1. Suggestions

  • Use a getter to reach inside the other object private properties

1.2.133.2. Specs

Short name Classes/ClassOverreach
Rulesets All, Appinfo
Exakat since 2.2.2
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Medium
Features visibility, class-invasion
Available in Entreprise Edition, Exakat Cloud

1.2.134. 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 Final classes by default, why? and When to declare classes final.

1.2.134.1. Specs

Short name Classes/FinalByOcramius
Rulesets All, Class Review
Exakat since 0.9.4
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision High
Features final
Available in Entreprise Edition, Exakat Cloud

1.2.135. Class Usage

List of classes being used.
<?php

// Class may be used in a use expression
use MyClass as MyAliasedClass;

// class may be aliased with class_alias
class_alias('MyOtherAliasedClass', 'MyClass');

// Class may be instanciated
$o = new MyClass();

// Class may be used with instanceof
var_dump($o instanceof \MyClass);

// Class may be used in static calls
MyClass::aConstant;
echo MyClass::$aProperty;
echo MyClass::aMethod( $o );

// Class may be extended
class MyOtherClass {

}

class MyClass extends MyOtherClass {
    const aConstant = 1;

    public static $aProperty = 2;

    // also used as a typehint
    public static function aMethod(MyClass $object) {
        return __METHOD__;
    }
}

?>

1.2.135.1. Specs

Short name Classes/ClassUsage
Rulesets All
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features class
Available in Entreprise Edition, Exakat Cloud

1.2.136. 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 eventually executed. In PHP 8.0, PHP detects this error at compile time, except for parent keyword in a closure <https://www.php.net/`closure>`_.

parent usage in trait is detected. It is only reported when the trait is used inside a class without parent, as the trait may also be used in another class, which has a parent.

<?php

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

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

1.2.136.2. Specs

Short name Classes/NoParent
Rulesets All, Analyze, CE, CI-checks, Changed Behavior, Class Review
Exakat since 1.9.0
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Changed Behavior PHP 8.0
Precision Very high
Features parent
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.137. Class, Interface, Enum 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 */ }
    enum a      { /* some definitions */ } // PHP 8.1
?>

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.

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

1.2.137.2. Specs

Short name Classes/CitSameName
Rulesets All, Analyze
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features class
Examples shopware, NextCloud
Available in Entreprise Edition, Exakat Cloud

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

?>

1.2.138.1. Specs

Short name Classes/MutualExtension
Rulesets All, Class Review, LintButWontExec
Exakat since 0.8.4
PHP Version All
Severity Major
Time To Fix Quick (30 mins)
Precision Very high
Features extends
Note This issue may lint but will not run
Available in Entreprise Edition, Exakat Cloud

1.2.139. Classes Names

List of all classes, as defined in the application.
<?php

// foo is in the list
class foo {}

// Anonymous classes are not in the list
$o = class { function foo(){} }

?>

See also class.

1.2.139.1. Specs

Short name Classes/Classnames
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features class
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.140. Clone Constant

Cloning constant is only possible since PHP 8.1. Until that version, constants could not be an object, and as such, could not be cloned.

This is also valid with default values, however they are assigned to a variable, which falls back to the classic clone usage.

Backward compatibility is OK, since PHP compile such code, and only checks at execution time that the constant is an object.

<?php

// new is available in constant definition, since PHP 8.2
const A = new B();
$c = clone A;

?>

See also New in initializers.

1.2.140.1. Specs

Short name Php/CloneConstant
Rulesets All, Analyze, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73, CompatibilityPHP74, CompatibilityPHP80, LintButWontExec
Exakat since 2.4.7
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features new, constant
Note This issue may lint but will not run
Available in Entreprise Edition, Exakat Cloud

1.2.141. Clone Usage

List of all clone situations.
<?php
    $dateTime = new DateTime();
    echo (clone $dateTime)->format('Y');
?>

See also Object cloning.

1.2.141.1. Specs

Short name Classes/CloningUsage
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features clone
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

1.2.142.1. Suggestions

  • Only clone containers (like variables, properties…)
  • Add typehint to injected properties, so they are checked as objects.

1.2.142.2. Specs

Short name Classes/CloneWithNonObject
Rulesets All, Analyze, LintButWontExec
Exakat since 1.7.0
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features clone
Note This issue may lint but will not run
Available in Entreprise Edition, Exakat Cloud

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

1.2.143.1. Suggestions

  • Always leave the last closing tag out

1.2.143.2. Specs

Short name Php/CloseTags
Rulesets All, Coding conventions
Exakat since 0.8.4
PHP Version All
Severity Minor
Time To Fix Instant (5 mins)
Precision Very high
Features close-tag
ClearPHP leave-last-closing-out
Available in Entreprise Edition, Exakat Cloud

1.2.144. Close Tags Consistency

PHP scripts may omit the final closing tag.

This is a convention, used to avoid the infamous ‘headers already sent’ error message, that appears when a script with extra invisible spaces is included before actually emitting the headers.

The PHP manual recommends : If a file is pure PHP code, it is preferable to omit the PHP closing tag at the end of the file.. (See PHP Tags):

.. code-block:: php

   <?php

   class foo {

   }

1.2.144.1. Specs

Short name Php/CloseTagsConsistency
Rulesets All, Preferences
Exakat since 0.9.3
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features close-tag
Available in Entreprise Edition, Exakat Cloud

1.2.145. Closure Could Be A Callback

Closure <https://www.php.net/manual/en/class.`closure.php>`_ and arrow function may be simplified to a callback. Callbacks are strings or arrays.

A simple closure <https://www.php.net/`closure>`_ that only returns arguments relayed to another function or method, could be reduced to a simpler expression.

Closure <https://www.php.net/manual/en/class.`closure.php>`_ may be simplified with a string, for functioncall, with an array for methodcalls and static methodcalls.

Performances : simplifying a closure <https://www.php.net/`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.

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

1.2.145.2. Specs

Short name Functions/Closure2String
Rulesets All, Performances, Rector, Suggestions
Exakat since 1.4.3
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features closure, callback
Examples Tine20, NextCloud
Available in Entreprise Edition, Exakat Cloud

1.2.146. 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 <https://www.php.net/`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.

1.2.146.1. Specs

Short name Php/ClosureThisSupport
Rulesets All, Changed Behavior, CompatibilityPHP53
Exakat since 0.8.4
PHP Version With PHP 5.4 and older
Severity Minor
Time To Fix Quick (30 mins)
Changed Behavior PHP 5.4
Precision Very high
Features closure, $this
Available in Entreprise Edition, Exakat Cloud

1.2.147. Closures Glossary

List of all the closures in the code.
<?php

// A closure is also a unnamed function
$closure = function ($arg) { return 'A'.strtolower($arg); }

?>

See also The Closure Class.

1.2.147.1. Specs

Short name Functions/Closures
Rulesets All, Appinfo, CE
Exakat since 0.8.4
PHP Version All
Severity  
Time To Fix  
Precision Very high
Features closure
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.148. Coalesce

Usage of coalesce operator.

Note that the coalesce operator is a special case of the ternary operator.

<?php

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

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

?>

See also Ternary Operator.

1.2.148.1. Specs

Short name Php/Coalesce
Rulesets All, Appinfo, CE, One Liners
Exakat since 0.8.4
PHP Version With PHP 5.3 and more recent
Severity  
Time To Fix  
Precision Very high
Features closure, $this
Available in Entreprise Edition, Community Edition, Exakat Cloud

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

?>

1.2.149.1. Suggestions

  • Add parenthesis around ?? operator to avoid misbehavior
  • Add parenthesis around the else condition to avoid misbehavior ( ?? ($a . $b))
  • Do not use dot and ?? together in the same expression

1.2.149.2. Specs

Short name Structures/CoalesceAndConcat
Rulesets All, Analyze, CE, CI-checks
Exakat since 1.9.4
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features coalesce, concat, precedence
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.150. Coalesce And Ternary Operators Order

The ternary operator and the null-coalesce operator cannot be used in any order. The ternary operator is wider, so ot should be used last.

In particular, the ternary operator works on truthy values, and NULL is a falsy one. So, NULL might be captured by the ternary operator, and the following coalesce operator has no chance to process it.

On the other hand, the coalesce operator only process NULL, and will leave the false (or any other falsy value) to process to the ternary operator.

<?php

// Good order : NULL is processed first, otherwise, false will be processed.
$b = $a ?? 'B' ?: 'C';

// Wrong order : this will never use the ??
$b = $a ?: 'C' ?? 'B';

?>

1.2.150.1. Suggestions

  • Use the good order of operator : most specific first, then less specific.

1.2.150.2. Specs

Short name Structures/CoalesceNullCoalesce
Rulesets All, Analyze
Exakat since 2.5.0
PHP Version With PHP 7.0 and more recent
Severity Minor
Time To Fix Quick (30 mins)
Precision High
Features ternary, null, coalesce
Available in  

1.2.151. Coalesce Equal

Usage of coalesce assignment operator. The operator is available in PHP since PHP 7.4.
<?php

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

// Equivalent to the line above
$a = $a ?? 'default value';

?>

See also Ternary Operator.

1.2.151.1. Suggestions

  • Use the short assignment syntax

1.2.151.2. Specs

Short name Php/CoalesceEqual
Rulesets All, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71, CompatibilityPHP72, CompatibilityPHP73
Exakat since 2.0.4
PHP Version With PHP 7.4 and more recent
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features coalesce, short-syntax
Available in Entreprise Edition, Exakat Cloud

1.2.152. Codeigniter usage

This analysis reports usage of the Codeigniter 4 framework.

Note : Code igniter 3 and older are not reported.

<?php

// A code igniter controller
class Blog extends \App\Controllers\Home {

        public function index()
        {
                echo 'Hello World!';
        }
}

?>

See also Codeigniter.

1.2.152.1. Specs

Short name Vendors/Codeigniter
Rulesets All, Appinfo, CE
Exakat since 0.11.8
PHP Version All
Severity Minor
Time To Fix Slow (1 hour)
Precision Very high
Features framework
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.153. Collect Atom Counts

Collects the list of each atom detected by exakat, and the number of occurrences.

1.2.153.1. Specs

Short name Dump/CollectAtomCounts
Rulesets All, CE, Dump
Exakat since 2.1.8
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.154. Collect Block Size

Collect block size for for, foreach, while, do…while, ifthen.

1.2.154.1. Specs

Short name Dump/CollectBlockSize
Rulesets All, Dump
Exakat since 2.2.0
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features inclusion
Available in Entreprise Edition, Exakat Cloud

1.2.155. Collect Calls

Collects calls to methods, and functions, and mentions the calling method or function.
<?php

function foo() {
     goo();
}
?>

The code above produces a link : foo => goo.

For methods, the results depends on type detection. Interface types might also limit this analysis.

Closures and arrow functions are omitted, so far.

1.2.155.1. Specs

Short name Dump/CollectCalls
Rulesets All, Dump
Exakat since 2.5.0
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Medium
Available in  

1.2.156. Collect Class Children Count

Count the number of class children for each class.
<?php

// 2 children
class a {}

// 1 children
class b extends a {}

// no children
class c extends b {}

// no children
class d extends a {}
?>

1.2.156.1. Specs

Short name Dump/CollectClassChildren
Rulesets All, CE, Dump
Exakat since 2.0.3
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Features inclusion
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.157. Collect Class Constant Counts

This analysis collects the number of class constants per class or interface.

The count applies to classes, anonymous classes and interfaces. They are considered distinct one from another.

<?php

class foo {
    // 3 constant
    const A =1, B =2;
}

interface bar {
    // 3 properties
    const A=1, B=2, C=3;
}

?>

1.2.157.1. Specs

Short name Dump/CollectClassConstantCounts
Rulesets All, CE, Dump
Exakat since 2.1.2
PHP Version All
Severity Minor
Time To Fix Quick (30 mins)
Precision Very high
Available in Entreprise Edition, Community Edition, Exakat Cloud

1.2.158. Collect Class Depth

Count the number of level of extends for classes.
<?php

class a {}

class b extends a {}

class c extends b {}

class d extends a {}
?>

1.2.158.1. Specs

Short name Dump/CollectClassDepth
Rulesets