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 byphp://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¶
$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¶
<?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¶
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¶
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.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.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.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 anassert
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
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 theinstanceof
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¶
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
orDirectoryIterator
classes. - Use
RegexIterator
to filter any unwanted results fromFilesystemIterator
. - 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()¶
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¶
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¶
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¶
<?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 areturn
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 :
- forward_static_call_array()
- forward_static_call()
- register_shutdown_function()
- register_tick_function()
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¶
<?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 theTraversable
interface. The alternative is to implementIterator
orIteratorAggregate
, which also implementsTraversable
.
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 extendingThrowable
can’t be thrown, unless they also extendException
. The same applies to interfaces that extendsThrowable
.
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¶
<?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¶
<?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¶
<?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¶
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 befinal
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 toparent
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¶
Theclone
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.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.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 |