1. Rules¶
1.1. Introduction¶
Exakat provides unique 1465 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. 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.1.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.1.2. Specs¶
Short name | Arrays/AmbiguousKeys |
Rulesets | Analyze, All |
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.2. 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.2.1. Suggestions¶
- Use one syntax consistently.
1.2.2.2. Specs¶
Short name | Arrays/ArrayBracketConsistence |
Rulesets | Preferences, All |
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.3. Short Syntax For Arrays¶
Arrays written with the new short syntax.
PHP 5.4 introduced the new short syntax, with square brackets. The previous syntax, based on the array() keyword is still available.
<?php
// All PHP versions array
$a = array(1, 2, 3);
// PHP 5.4+ arrays
$a = [1, 2, 3];
?>
See also Array.
1.2.3.1. Specs¶
Short name | Arrays/ArrayNSUsage |
Rulesets | Appinfo, CE, CompatibilityPHP53, All |
Exakat since | 0.8.4 |
PHP Version | With PHP 5.3 and more recent |
Severity | Critical |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Features | array |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.4. 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.4.1. Specs¶
Short name | Arrays/Arrayindex |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Instant (5 mins) |
Precision | Very high |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.5. Empty Final Element¶
The array() construct allows for the empty last element.
By putting an element on each line, and adding the final comma, it is possible to reduce the size of the diff when comparing code with the previous version.
<?php
// Array definition with final empty element
$array = [1,
2,
3,
];
// This array definition has only one line of diff with the previous array : the line with '4,'
$array = [1,
2,
3,
4,
];
// This array definition is totally different from the first array :
$array = [1, 2, 3, 4];
?>
See also Array, Zend Framework Coding Standard and How clean is your code? How clean are your diffs?.
1.2.5.1. Specs¶
Short name | Arrays/EmptyFinal |
Rulesets | Preferences, All |
Exakat since | 0.11.0 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.6. Empty Slots In Arrays¶
PHP tolerates the last element of an array to be empty.
<?php
$a = array( 1, 2, 3, );
$b = [ 4, 5, ];
?>
1.2.6.1. Specs¶
Short name | Arrays/EmptySlots |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Instant (5 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.7. Getting Last Element¶
Getting the last element of an array relies on array_key_last().
array_key_last() was added in PHP 7.3. Before that,
<?php
$array = ['a' => 1, 'b' => 2, 'c' => 3];
// Best solutions, by far
$last = $array[array_key_last($array)];
// Best solutions, just as fast as each other
$last = $array[count($array) - 1];
$last = end($array);
// Bad solutions
// popping, but restoring the value.
$last = array_pop($array);
$array[] = $last;
// array_unshift would be even worse
// reversing array
$last = array_reverse($array)[0];
// slicing the array
$last = array_slice($array, -1)[0]',
$last = current(array_slice($array, -1));
);
?>
1.2.7.1. Suggestions¶
- Use PHP native function : array_key_last(), when using PHP 7.4 and later
- Use PHP native function : array_pop()
- Organise the code to put the last element in the first position (array_unshift() instead of append operator [])
1.2.7.2. Specs¶
Short name | Arrays/GettingLastElement |
Rulesets | Performances, All |
Exakat since | 0.9.0 |
PHP Version | All |
Severity | Minor |
Time To Fix | Instant (5 mins) |
Precision | High |
Examples | Thelia |
Available in | Entreprise Edition, Exakat Cloud |
1.2.8. Mass Creation Of Arrays¶
Literal creation of an array, by assigning a lot of index.
<?php
$row['name'] = $name;
$row['last'] = $last;
$row['address'] = $address;
?>
1.2.8.1. Specs¶
Short name | Arrays/MassCreation |
Rulesets | All |
Exakat since | 1.1.8 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.9. Mistaken Concatenation¶
A unexpected structure is built for initialization. It may be a typo that creates an unwanted expression.
<?php
// This 'cd' is unexpected. Isn't it 'c', 'd' ?
$array = array('a', 'b', 'c'. 'd');
$array = array('a', 'b', 'c', 'd');
// This 4.5 is unexpected. Isn't it 4, 5 ?
$array = array(1, 2, 3, 4.5);
$array = array(1, 2, 3, 4, 5);
?>
1.2.9.1. Specs¶
Short name | Arrays/MistakenConcatenation |
Rulesets | All |
Exakat since | 1.0.8 |
PHP Version | All |
Severity | Major |
Time To Fix | Instant (5 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.10. Mixed Keys Arrays¶
Avoid mixing constants and literals in array keys.
When defining default values in arrays, it is recommended to avoid mixing constants and literals, as PHP may mistake them and overwrite the previous with the latter.
Either switch to a newer version of PHP (5.5 or newer), or make sure the resulting array hold the expected data. If not, reorder the definitions.
<?php
const ONE = 1;
$a = [ 1 => 2,
ONE => 3];
?>
1.2.10.1. Suggestions¶
- Use only literals or constants when building the array
1.2.10.2. Specs¶
Short name | Arrays/MixedKeys |
Rulesets | CompatibilityPHP53, CompatibilityPHP54, All |
Exakat since | 0.8.4 |
PHP Version | With PHP 5.6 and more recent |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.11. Multidimensional Arrays¶
Simply, arrays of arrays.
<?php
$x[1][2] = $x[2][3][4];
?>
See also Type array and Using Multidimensional Arrays in PHP.
1.2.11.1. Specs¶
Short name | Arrays/Multidimensional |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.12. Multiple Index Definition¶
Indexes that are defined multiple times in the same array.
<?php
// Multiple identical keys
$x = array(1 => 2,
2 => 3,
1 => 3);
// Multiple identical keys (sneaky version)
$x = array(1 => 2,
1.1 => 3,
true => 4);
// Multiple identical keys (automated version)
$x = array(1 => 2,
3, // This will be index 2
2 => 4); // this index is overwritten
?>
They are indeed overwriting each other. This is most probably a typo.
Name | Default | Type | Description |
arrayMaxSize | 15000 | integer | Maximal size of arrays to be analyzed. This will speed up analysis, and leave the largest arrays untouched. |
1.2.12.1. Suggestions¶
- Review your code and check that arrays only have keys defined once.
- Review carefully your code and check indirect values, like constants, static constants.
1.2.12.2. Specs¶
Short name | Arrays/MultipleIdenticalKeys |
Rulesets | Analyze, CE, CI-checks, Rector, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Instant (5 mins) |
Precision | Very high |
Features | arrays |
Examples | Magento, MediaWiki |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.13. Negative Start Index In Array¶
Negative starting index in arrays changed in PHP 8.0. Until then, they were ignored, and automatic index started always at 0. Since PHP 8.0, the next index is calculated.
The behavior will break code that relies on automatic index in arrays, when a negative index is used for a starter.
<?php
$x = [-5 => 2];
$x[] = 3;
print_r($x);
/*
PHP 7.4 and older
Array
(
[-5] => 2
[0] => 3
)
*/
/*
PHP 8.0 and more recent
Array
(
[-5] => 2
[-4] => 3
)
*/
?>
See also PHP RFC: Arrays starting with a negative index.
1.2.13.1. Suggestions¶
- Explicitly create the index, instead of using the automatic indexing
- Add an explicit index of 0 in the initial array, to set the automatic process in the right track
- Avoid using specified index in array, conjointly with automatic indexing.
1.2.13.2. Specs¶
Short name | Arrays/NegativeStart |
Rulesets | CE, CompatibilityPHP80, All |
Exakat since | 2.1.9 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.14. No Spread For Hash¶
The spread operator...
only works on integer-indexed arrays.
<?php
// This is valid, as ``-33`` is cast to integer by PHP automagically
var_dump(...[1,-33 => 2, 3]);
// This is not valid
var_dump(...[1,C => 2, 3]);
?>
See also Variable-length argument lists.
1.2.14.1. Suggestions¶
- Add a call to array_values() instead of the hash
- Upgrade to PHP 8.1
1.2.14.2. Specs¶
Short name | Arrays/NoSpreadForHash |
Rulesets | Analyze, All |
Exakat since | 1.9.3 |
PHP Version | With PHP 8.1 and older |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.15. Non-constant Index In Array¶
Undefined constants revert as strings in Arrays. They are also calledbarewords
.
In $array[index]
, PHP cannot find index as a constant, but, as a default behavior, turns it into the string index
.
This default behavior raise concerns when a corresponding constant is defined, either using define() or the const keyword (outside a class). The definition of the index constant will modify the behavior of the index, as it will now use the constant definition, and not the ‘index’ string.
<?php
// assign 1 to the element index in $array
// index will fallback to string
$array[index] = 1;
//PHP Notice: Use of undefined constant index - assumed 'index'
echo $array[index]; // display 1 and the above error
echo "$array[index]"; // display 1
echo "$array['index']"; // Syntax error
define('index', 2);
// now 1 to the element 2 in $array
$array[index] = 1;
?>
It is recommended to make index a real string (with ‘ or “), or to define the corresponding constant to avoid any future surprise.
Note that PHP 7.2 removes the support for this feature. See also PHP RFC: Deprecate and Remove Bareword (Unquoted) Strings and Syntax.
1.2.15.1. Suggestions¶
- Declare the constant to give it an actual value
- Turn the constant name into a string
1.2.15.2. Specs¶
Short name | Arrays/NonConstantArray |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Instant (5 mins) |
Precision | High |
Features | array |
Examples | Dolibarr, Zencart |
Available in | Entreprise Edition, Exakat Cloud |
1.2.16. Null Or Boolean Arrays¶
Null and booleans are valid PHP array base. Yet, they only producesnull
values. They also did not emits any warning until PHP 7.4.
This analysis has been upgraded to cover int and float types too.
<?php
// outputs NULL
var_dump(null[0]);
const MY_CONSTANT = true;
// outputs NULL
var_dump(MY_CONSTANT[10]);
?>
See also Null and True.
1.2.16.1. Suggestions¶
- Avoid using the array syntax on null and boolean
- Avoid using null and boolean on constant that are expecting arrays
1.2.16.2. Specs¶
Short name | Arrays/NullBoolean |
Rulesets | Analyze, All |
Exakat since | 1.8.6 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.17. PHP Arrays Index¶
List of indexes used when manipulating PHP arrays in the code.
<?php
// HTTP_HOST is a PHP array index.
$ip = 'http'.$_SERVER['HTTP_HOST'].'/'.$row['path'];
//'path' is not a PHP index
?>
1.2.17.1. Specs¶
Short name | Arrays/Phparrayindex |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.18. Randomly Sorted Arrays¶
Those literal arrays are written in several places, but their items are in various orders.
This may reduce the reading and proofing of the arrays, and induce confusion. The random order may also be a residue of development : both arrays started with different values, but they grew overtime to handle the same items. The way they were written lead to the current order.
Unless order is important, it is recommended to always use the same order when defining literal arrays. This makes it easier to match different part of the code by recognizing one of its literal.
<?php
// an array
$set = [1,3,5,9,10];
function foo() {
// an array, with the same values but different order, in a different context
$list = [1,3,5,10,9,];
}
// an array, with the same order than the initial one
$inits = [1,3,5,9,10];
?>
1.2.18.1. Suggestions¶
- Match the sorting order of the arrays. Choose any of them.
- Configure a constant and use it as a replacement for those arrays.
- Leave the arrays intact : the order may be important.
- For hash arrays, consider turning the array in a class.
1.2.18.2. Specs¶
Short name | Arrays/RandomlySortedLiterals |
Rulesets | Analyze, Suggestions, All |
Exakat since | 0.11.2 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Examples | Contao, Vanilla |
Available in | Entreprise Edition, Exakat Cloud |
1.2.19. Preprocess Arrays¶
Using long list of assignations for initializing arrays is significantly slower than the declaring them as an array.
<?php
// Slow way
$a = []; // also with $a = array();
$a[1] = 2;
$a[2] = 3;
$a[3] = 5;
$a[4] = 7;
$a[5] = 11;
// Faster way
$a = [1 => 2,
2 => 3,
3 => 5,
4 => 7,
5 => 11];
// Even faster way if indexing is implicit
$a = [2, 3, 5, 7, 11];
?>
If the array has to be completed rather than created, it is also faster to use += when there are more than ten elements to add.
<?php
// Slow way
$a = []; // also with $a = array();
$a[1] = 2;
$a[2] = 3;
$a[3] = 5;
// some expressions to get $seven and $eleven
$a[4] = $seven;
$a[5] = $eleven;
// Faster way
$a = [1 => 2,
2 => 3,
3 => 5];
// some expressions to get $seven and $eleven
$a += [4 => $seven,
5 => $eleven];
// Even faster way if indexing is implicit
$a = [2, 3, 5];
// some expressions to get $seven and $eleven
$a += [$seven, $eleven];
?>
1.2.19.1. Suggestions¶
- Preprocess the code so PHP doesn’t do it. Keep the detailed version into comments.
1.2.19.2. Specs¶
Short name | Arrays/ShouldPreprocess |
Rulesets | Suggestions, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.20. Slice Arrays First¶
Always start by reducing an array before applying some transformation on it. The shorter array will be processed faster.
<?php
// fast version
$a = array_map('foo', array_slice($array, 2, 5));
// slower version
$a = array_slice(array_map('foo', $array), 2, 5);
?>
The gain produced here is greater with longer arrays, or greater reductions. They may also be used in loops. This is a micro-optimisation when used on short arrays.
1.2.20.1. Suggestions¶
- Use the array transforming function on the result of the array shortening function.
1.2.20.2. Specs¶
Short name | Arrays/SliceFirst |
Rulesets | Performances, Suggestions, All |
Exakat since | 1.0.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Examples | WordPress |
Available in | Entreprise Edition, Exakat Cloud |
1.2.21. String Initialization¶
It used to be possible to initialize a variable with an string, and use it as an array. It is not the case anymore in PHP 7.1.
<?php
// Initialize arrays with array()
$a = array();
$a[3] = 4;
// Don't start with a string
$a = '';
$a[3] = 4;
print $a;
// Don't start with a string
if (is_numeric($a)) {
$a[] = $a;
}
?>
1.2.21.1. Suggestions¶
- Always initialize arrays with an empty array(), not a string.
1.2.21.2. Specs¶
Short name | Arrays/StringInitialization |
Rulesets | CompatibilityPHP71, All |
Exakat since | 1.6.5 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.22. Too Many Array Dimensions¶
When arrays a getting to many nesting.
<?php
$a = array(); // level 1;
$a[1] = array(); // level 2
$a[1][2] = array(); // level 3 : still valid by default
$a[1][2][3] = array(); // level 4
?>
PHP has no limit, and accepts any number of nesting levels. Yet, this is usually very memory hungry.
Name | Default | Type | Description |
maxDimensions | 3 | integer | Number of valid dimensions in an array. |
1.2.22.1. Specs¶
Short name | Arrays/TooManyDimensions |
Rulesets | Analyze, All |
Exakat since | 1.9.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.23. Weird Array Index¶
Array index that looks weird. Arrays index may be string or integer, but some strings looks weird.
In particular, strings that include terminal white spaces, often leads to missed values.
<?php
$array = ['a ' => 1, 'b' => 2, 'c' => 3];
// Later in the code
//Notice: Undefined index: a in /Users/famille/Desktop/analyzeG3/test.php on line 8
echo $array['a'];
//Notice: Undefined index: b in /Users/famille/Desktop/analyzeG3/test.php on line 10
// Note that the space is visible, but easy to miss
echo $array['b '];
// all fine here
echo $array['c'];
?>
Although this is rare error, and often easy to spot, it is also very hard to find when it strikes.
1.2.23.1. Suggestions¶
- Remove white spaces when using strings as array index.
1.2.23.2. Specs¶
Short name | Arrays/WeirdIndex |
Rulesets | Semantics, All |
Exakat since | 1.9.9 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Features | index |
Available in | Entreprise Edition, Exakat Cloud |
1.2.24. Handle Arrays With Callback¶
Use functions like array_map().
<?php
// Handles arrays with callback
$uppercase = array_map('strtoupper', $source);
// Handles arrays with foreach
foreach($source as &$s) {
$s = uppercase($s);
}
?>
See also array_map.
1.2.24.1. Specs¶
Short name | Arrays/WithCallback |
Rulesets | Appinfo, CE, All |
Exakat since | 1.3.7 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.25. Missing Attribute Attribute¶
While not strictly required it is recommended to create an actual class for every attribute.
<?php
namespace Example;
use Attribute;
#[Attribute]
class MyAttribute
{
}
#Missing the above attribute
class MyOtherAttribute
{
}
?>
See also Declaring Attribute Classes.
1.2.25.1. Suggestions¶
- Add the Attribute attribute to those classes
1.2.25.2. Specs¶
Short name | Attributes/MissingAttributeAttribute |
Rulesets | Analyze, Attributes, All, PHP recommendations |
Exakat since | 2.2.4 |
PHP Version | With PHP 8.0 and more recent |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Available in | Entreprise Edition, Exakat Cloud |
1.2.26. Modify Immutable¶
A class, marked as immutable, is being modified.
This attribute is supported as a PHPdoc comment, `@immutable, and as a PHP 8.0 attribute.
<?php
/** @Immutable */
#[Immutable]
class x {
public $x = 1, $y, $z;
}
$x = new X;
// $x->x is modified, while it should not
$x->x = 2 + $x->z;
// $x->z is read only, as expected
?>
See also phpstorm-stubs/meta/attributes/Immutable.php and PhpStorm 2020.3 EAP #4: Custom PHP 8 Attributes.
1.2.26.1. Suggestions¶
- Removed the modification
- Clone the immutable object
1.2.26.2. Specs¶
Short name | Attributes/ModifyImmutable |
Rulesets | Analyze, Attributes, All |
Exakat since | 2.2.0 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Medium |
Available in | Entreprise Edition, Exakat Cloud |
1.2.27. 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.27.1. Suggestions¶
- Implements all the abstract methods of the class
- Make the class abstract
1.2.27.2. Specs¶
Short name | Classes/AbstractOrImplements |
Rulesets | Analyze, LintButWontExec, All |
Exakat since | 1.3.3 |
PHP Version | All |
Severity | Major |
Time To Fix | Quick (30 mins) |
Precision | High |
Examples | Zurmo |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.28. 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.28.1. Suggestions¶
- Remove abstract keyword from the method
- Remove static keyword from the method
- Remove the method
1.2.28.2. Specs¶
Short name | Classes/AbstractStatic |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | With PHP 7.0 and older |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Available in | Entreprise Edition, Exakat Cloud |
1.2.29. 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.29.1. Specs¶
Short name | Classes/Abstractclass |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | Very high |
Features | class |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.30. 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.30.1. Specs¶
Short name | Classes/Abstractmethods |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | Very high |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.31. 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.31.1. Specs¶
Short name | Classes/AccessPrivate |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Major |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.32. 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.32.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.32.2. Specs¶
Short name | Classes/AccessProtected |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Major |
Time To Fix | Quick (30 mins) |
Precision | High |
Features | visibility |
Available in | Entreprise Edition, Exakat Cloud |
1.2.33. 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.33.1. Specs¶
Short name | Classes/AmbiguousStatic |
Rulesets | Analyze, All |
Exakat since | 1.0.3 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.34. 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.34.1. Suggestions¶
- Sync visibilities for both properties, in the different classes
- Use different names for properties with different usages
1.2.34.2. Specs¶
Short name | Classes/AmbiguousVisibilities |
Rulesets | Analyze, All |
Exakat since | 1.3.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Examples | Typo3 |
Available in | Entreprise Edition, Exakat Cloud |
1.2.35. Anonymous Classes¶
Anonymous classes.
<?php
// Anonymous class, available since PHP 7.0
$object = new class { function __construct() { echo __METHOD__; } };
?>
1.2.35.1. Specs¶
Short name | Classes/Anonymous |
Rulesets | Appinfo, CE, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, All |
Exakat since | 0.8.4 |
PHP Version | With PHP 7.0 and more recent |
Severity | Major |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.36. Avoid option arrays in constructors¶
Avoid option arrays in constructors. Use one parameter per injected element.
<?php
class Foo {
// Distinct arguments, all typehinted if possible
function __constructor(A $a, B $b, C $c, D $d) {
$this->a = $a;
$this->b = $b;
$this->c = $c;
$this->d = $d;
}
}
class Bar {
// One argument, spread over several properties
function __constructor(array $options) {
$this->a = $options['a'];
$this->b = $options['b'];
$this->c = $options['c'];
$this->d = $options['d'];
}
}
?>
See also Avoid option arrays in constructors and PHP RFC: Named Arguments (Type-safe and documented options).
1.2.36.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.36.2. Specs¶
Short name | Classes/AvoidOptionArrays |
Rulesets | Analyze, ClassReview, All |
Exakat since | 1.7.9 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Medium |
Available in | Entreprise Edition, Exakat Cloud |
1.2.37. 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.37.1. Suggestions¶
- Use a null object to fill any missing value
- Make sure the property is set at constructor time
1.2.37.2. Specs¶
Short name | Classes/AvoidOptionalProperties |
Rulesets | Analyze, All |
Exakat since | 0.12.0 |
PHP Version | All |
Severity | Major |
Time To Fix | Slow (1 hour) |
Precision | High |
Examples | ChurchCRM, Dolibarr |
Available in | Entreprise Edition, Exakat Cloud |
1.2.38. Custom Class Usage¶
List of usage of custom classes throughout the code.
Name | Default | Type | Description |
forbiddenClasses | ini_hash | List of classes to be avoided |
1.2.38.1. Specs¶
Short name | Classes/AvoidUsing |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Major |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.39. 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.39.1. Suggestions¶
- Drop the common method, and the cancelled methods in the children
- Fill the children’s methods with actual code
1.2.39.2. Specs¶
Short name | Classes/CancelCommonMethod |
Rulesets | ClassReview, Suggestions, All |
Exakat since | 2.1.8 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.40. 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.40.1. Suggestions¶
- Remove the final keyword
- Remove the extending class
1.2.40.2. Specs¶
Short name | Classes/CantExtendFinal |
Rulesets | Analyze, Dead code, All |
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.41. 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.41.1. Suggestions¶
- Avoid inheriting abstract methods for compatibility beyond 7.2 (and older)
1.2.41.2. Specs¶
Short name | Classes/CantInheritAbstractMethod |
Rulesets | CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71, All |
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 |
Available in | Entreprise Edition, Exakat Cloud |
1.2.42. Cant Instantiate Class¶
When constructor is not public, it is not possible to instantiate such a class. Either this is a conception choice, or there are factories to handle that. Either way, it is not possible to call new on such class.
PHP reports an error similar to this one : ‘Call to private Y::__construct() from invalid context’.
<?php
//This is the way to go
$x = X::factory();
//This is not possible
$x = new X();
class X {
//This is also the case with proctected __construct
private function __construct() {}
static public function factory() {
return new X();
}
}
?>
See also In a PHP5 class, when does a private constructor get called?, Named Constructors in PHP and PHP Constructor Best Practices And The Prototype Pattern.
1.2.42.1. Specs¶
Short name | Classes/CantInstantiateClass |
Rulesets | Analyze, All |
Exakat since | 1.2.8 |
PHP Version | All |
Severity | Critical |
Time To Fix | Quick (30 mins) |
Precision | High |
Examples | WordPress |
Available in | Entreprise Edition, Exakat Cloud |
1.2.43. 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.43.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.43.2. Specs¶
Short name | Classes/CheckOnCallUsage |
Rulesets | Analyze, CE, CI-checks, All |
Exakat since | 1.7.2 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.44. 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.44.1. Specs¶
Short name | Classes/ChildRemoveTypehint |
Rulesets | CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71, Typechecks, All |
Exakat since | 0.12.4 |
PHP Version | With PHP 7.2 and more recent |
Severity | Major |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.45. 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.45.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.45.2. Specs¶
Short name | Classes/CitSameName |
Rulesets | Analyze, All |
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.46. Usage Of class_alias()¶
class_alias
creates dynamically an alias for classes.
<?php
class foo { }
class_alias('foo', 'bar');
$a = new foo;
$b = new bar;
// the objects are the same
var_dump($a == $b, $a === $b);
var_dump($a instanceof $b);
// the classes are the same
var_dump($a instanceof foo);
var_dump($a instanceof bar);
var_dump($b instanceof foo);
var_dump($b instanceof bar);
?>
See also class_alias.
1.2.46.1. Specs¶
Short name | Classes/ClassAliasUsage |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.47. 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.
<?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.
1.2.47.1. Suggestions¶
- Use a getter to reach inside the other object private properties
1.2.47.2. Specs¶
Short name | Classes/ClassOverreach |
Rulesets | Appinfo, All |
Exakat since | 2.2.2 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | Medium |
Features | visibility |
Available in | Entreprise Edition, Exakat Cloud |
1.2.48. 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.48.1. Specs¶
Short name | Classes/ClassUsage |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | Very high |
Features | class |
Available in | Entreprise Edition, Exakat Cloud |
1.2.49. 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(){} }
?>
1.2.49.1. Specs¶
Short name | Classes/Classnames |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.50. 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.50.1. Suggestions¶
- Only clone containers (like variables, properties…)
- Add typehint to injected properties, so they are checked as objects.
1.2.50.2. Specs¶
Short name | Classes/CloneWithNonObject |
Rulesets | Analyze, LintButWontExec, All |
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.51. Clone Usage¶
List of all clone situations.
<?php
$dateTime = new DateTime();
echo (clone $dateTime)->format('Y');
?>
See also Object cloning.
1.2.51.1. Specs¶
Short name | Classes/CloningUsage |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.52. Const Visibility Usage¶
Visibility for class constant controls the accessibility to class constant.
A public constant may be used anywhere in the code; a protected constant usage is restricted to the class and its relatives; a private constant is restricted to itself.
This feature was introduced in PHP 7.1. It is recommended to use explicit visibility, and, whenever possible, make the visibility private.
<?php
class x {
public const a = 1;
protected const b = 2;
private const c = 3;
const d = 4;
}
interface i {
public const a = 1;
const d = 4;
}
?>
See also Class Constants and PHP RFC: Support Class Constant Visibility.
1.2.52.1. Suggestions¶
- Add constant visibility, at least ‘public’.
1.2.52.2. Specs¶
Short name | Classes/ConstVisibilityUsage |
Rulesets | Appinfo, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70, All |
Exakat since | 1.3.0 |
PHP Version | With PHP 7.1 and more recent |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Features | class-constant-visibility |
Available in | Entreprise Edition, Exakat Cloud |
1.2.53. Constant Class¶
A class or an interface only made up of constants. Constants usually have to be used in conjunction of some behavior (methods, class…) and never alone.
<?php
class ConstantClass {
const KBIT = 1000;
const MBIT = self::KBIT * 1000;
const GBIT = self::MBIT * 1000;
const PBIT = self::GBIT * 1000;
}
?>
As such, they should be PHP constants (build with define or const), or included in a class with other methods and properties.
See also PHP Classes containing only constants.
1.2.53.1. Suggestions¶
- Make the class an interface
- Make the class an abstract class, to avoid its instantiation
1.2.53.2. Specs¶
Short name | Classes/ConstantClass |
Rulesets | CE, ClassReview, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.54. Constant Definition¶
List of class constants being defined.
<?php
// traditional way of making constants
define('aConstant', 1);
// modern way of making constants
const anotherConstant = 2;
class foo {
// Not a constant, a class constant.
const aClassConstant = 3;
}
?>
See also PHP Constants.
1.2.54.1. Specs¶
Short name | Classes/ConstantDefinition |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.55. Constant Used Below¶
Mark class constants that are used in children classes.
<?php
class foo {
// This constant is used in children
protected PROTECTEDPROPERTY = 1;
// This constant is not used in children
protected LOCALPROTECTEDPROPERTY = 1;
private function foobar() {
// PROTECTEDPROPERTY is used here, but defined in parent
echo self::LOCALPROTECTEDPROPERTY;
}
}
class foofoo extends foo {
private function bar() {
// protectedProperty is used here, but defined in parent
print self::PROTECTEDPROPERTY;
}
}
?>
This analysis marks constants at their definition, not the current class, nor the (grand-)`parent <https://www.php.net/manual/en/language.oop5.paamayim-nekudotayim.php>`_.
1.2.55.1. Specs¶
Short name | Classes/ConstantUsedBelow |
Rulesets | All |
Exakat since | 0.12.10 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.56. Constructors¶
Mark methods as constructors.
<?php
class x {
// Normal constructor
function __construct() {}
}
class y {
// Old style constructor, obsolete since PHP 7.1
function y() {}
}
class z {
// Normal constructor
function __construct() {}
// Old style constructor, but with lower priority
function z() {}
}
?>
See also Constructors and Destructors.
1.2.56.1. Specs¶
Short name | Classes/Constructor |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.57. Could Be Abstract Class¶
An abstract class is never instantiated, and has children class that are. As such, a ‘parent’ class that is never instantiated by itself, but has its own children instantiated could be marked as abstract.
That will prevent new code to try to instantiate it.
<?php
// Example code would actually be split over multiple files.
// That class could be abstract
class motherClass {}
// Those classes shouldn't be abstract
class firstChildren extends motherClass {}
class secondChildren extends motherClass {}
class thirdChildren extends motherClass {}
new firstChildren();
new secondChildren();
new thirdChildren();
//Not a single : new motherClass()
?>
See also Class Abstraction Abstract classes and methods.
1.2.57.1. Suggestions¶
- Make this class an abstract class
1.2.57.2. Specs¶
Short name | Classes/CouldBeAbstractClass |
Rulesets | Analyze, ClassReview, All |
Exakat since | 1.3.9 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Examples | Edusoho, shopware |
Available in | Entreprise Edition, Exakat Cloud |
1.2.58. Could Be Class Constant¶
When a property is defined and read, but never modified, it could be turned into a constant.
<?php
class foo {
// $this->bar is never modified.
private $bar = 1;
// $this->foofoo is modified, at least once
private $foofoo = 2;
function method($a) {
$this->foofoo = $this->bar + $a + $this->foofoo;
return $this->foofoo;
}
}
?>
Starting with PHP 5.6, array() may be defined as constants. See also Class Constants https://www.php.net/manual/en/language.oop5.constants.php.
1.2.58.1. Specs¶
Short name | Classes/CouldBeClassConstant |
Rulesets | ClassReview, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Features | class-constant, visibility |
Available in | Entreprise Edition, Exakat Cloud |
1.2.59. 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.
1.2.59.1. Suggestions¶
- Make the class final
- Extends the class
1.2.59.2. Specs¶
Short name | Classes/CouldBeFinal |
Rulesets | Analyze, ClassReview, All |
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.60. Could Be Parent Method¶
<?php
// The parent class
class x { }
// The children class
class y1 extends x {
// foo is common to y1 and y2, so it shall be also a method in x
function foo() {}
// fooY1 is specific to y1
function fooY1() {}
}
class y2 extends x {
function foo() {}
// fooY2 is specific to y1
function fooY2() {}
}
?>
Only the name of the method is used is for gathering purposes. If the code has grown organically, the signature (default values, typehint, argument names) may have followed different path, and will require a refactorisation.
Name | Default | Type | Description |
minChildren | 4 | integer | Minimal number of children using this method. |
1.2.60.1. Suggestions¶
- Create an abstract method in the parent
- Create an concrete method in the parent, and move default behavior there by removing it in children classes
1.2.60.2. Specs¶
Short name | Classes/CouldBeParentMethod |
Rulesets | ClassReview, All |
Exakat since | 2.1.7 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.61. Property Could Be Private Property¶
The following properties are never used outside their class of definition Given the analyzed code, they could be set as private.
<?php
class foo {
public $couldBePrivate = 1;
public $cantdBePrivate = 1;
function bar() {
// couldBePrivate is used internally.
$this->couldBePrivate = 3;
}
}
class foo2 extends foo {
function bar2() {
// cantdBePrivate is used in a child class.
$this->cantdBePrivate = 3;
}
}
//$couldBePrivate is not used outside
$foo = new foo();
//$cantdBePrivate is used outside the class
$foo->cantdBePrivate = 2;
?>
Note that dynamic properties (such as $x->$y) are not taken into account.
1.2.61.1. Suggestions¶
- Remove the unused property
- Use the private property
- Change the visibility to allow access the property from other part of the code
1.2.61.2. Specs¶
Short name | Classes/CouldBePrivate |
Rulesets | ClassReview, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.62. Could Be Private Class Constant¶
Class constant may useprivate
visibility.
Since PHP 7.1, constants may also have a public/protected/private visibility. This restrict their usage to anywhere, class and children or class.
As a general rule, it is recommended to make constant private
by default, and to relax this restriction as needed. PHP makes them public by default.
<?php
class foo {
// pre-7.1 style
const PRE_71_CONSTANT = 1;
// post-7.1 style
private const PRIVATE_CONSTANT = 2;
public const PUBLIC_CONSTANT = 3;
function bar() {
// PRIVATE CONSTANT may only be used in its class
echo self::PRIVATE_CONSTANT;
}
}
// Other constants may be used anywhere
function x($a = foo::PUBLIC_CONSTANT) {
echo $a.' '.foo:PRE_71_CONSTANT;
}
?>
Constant shall stay public
when the code has to be compatible with PHP 7.0 and older.
They also have to be public in the case of component : some of those constants have to be used by external actors, in order to configure the component.
See also Class Constants.
1.2.62.1. Specs¶
Short name | Classes/CouldBePrivateConstante |
Rulesets | ClassReview, All |
Exakat since | 0.12.10 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Examples | Phinx |
Available in | Entreprise Edition, Exakat Cloud |
1.2.63. Method Could Be Private Method¶
The following methods are never used outside their class of definition. Given the analyzed code, they could be set as private.
<?php
class foo {
public function couldBePrivate() {}
public function cantdBePrivate() {}
function bar() {
// couldBePrivate is used internally.
$this->couldBePrivate();
}
}
class foo2 extends foo {
function bar2() {
// cantdBePrivate is used in a child class.
$this->cantdBePrivate();
}
}
//couldBePrivate() is not used outside
$foo = new foo();
//cantdBePrivate is used outside the class
$foo->cantdBePrivate();
?>
Note that dynamic properties (such as $x->$y) are not taken into account.
1.2.63.1. Specs¶
Short name | Classes/CouldBePrivateMethod |
Rulesets | ClassReview, All |
Exakat since | 0.12.11 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.64. Could Be Protected Class Constant¶
Class constant may use ‘protected’ visibility.
Since PHP 7.1, constants may also have a public/protected/private visibility. This restrict their usage to anywhere, class and children or class.
As a general rule, it is recommended to make constant ‘private’ by default, and to relax this restriction as needed. PHP makes them public by default.
<?php
class foo {
// pre-7.1 style
const PRE_71_CONSTANT = 1;
// post-7.1 style
protected const PROTECTED_CONSTANT = 2;
public const PUBLIC_CONSTANT = 3;
}
class foo2 extends foo {
function bar() {
// PROTECTED_CONSTANT may only be used in its class or its children
echo self::PROTECTED_CONSTANT;
}
}
class foo3 extends foo {
function bar() {
// PROTECTED_CONSTANT may only be used in its class or any of its children
echo self::PROTECTED_CONSTANT;
}
}
// Other constants may be used anywhere
function x($a = foo::PUBLIC_CONSTANT) {
echo $a.' '.foo:PRE_71_CONSTANT;
}
?>
1.2.64.1. Specs¶
Short name | Classes/CouldBeProtectedConstant |
Rulesets | ClassReview, All |
Exakat since | 0.12.11 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.65. Could Be Protected Method¶
Those methods are declared public, but are never used publicly. They may be made protected.
<?php
class foo {
// Public, and used publicly
public publicMethod() {}
// Public, but never used outside the class or its children
public protectedMethod() {}
private function bar() {
$this->protectedMethod();
}
}
$foo = new Foo();
$foo->publicMethod();
?>
These properties may even be made private.
1.2.65.1. Specs¶
Short name | Classes/CouldBeProtectedMethod |
Rulesets | ClassReview, All |
Exakat since | 0.12.11 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.66. Could Be Protected Property¶
Those properties are declared public, but are never used publicly. They may be made protected.
<?php
class foo {
// Public, and used publicly
public $publicProperty;
// Public, but never used outside the class or its children
public $protectedProperty;
function bar() {
$this->protectedProperty = 1;
}
}
$foo = new Foo();
$foo->publicProperty = 3;
?>
This property may even be made private.
1.2.66.1. Specs¶
Short name | Classes/CouldBeProtectedProperty |
Rulesets | ClassReview, All |
Exakat since | 0.9.7 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.67. Method Could Be Static¶
While static methods are usually harder to handle, recognizing the static status is a first step before turning the method into a standalone function.
<?php
class foo {
static $property = 1;
// legit static method
static function staticMethod() {
return self::$property;
}
// This is not using $this, and could be static
function nonStaticMethod() {
return self::$property;
}
// This is not using $this nor self, could be a standalone function
function nonStaticMethod() {
return self::$property;
}
}
?>
1.2.67.1. Suggestions¶
- Make the method static
- Make the method a standalone function
- Make use of $this in the method : may be it was forgotten.
1.2.67.2. Specs¶
Short name | Classes/CouldBeStatic |
Rulesets | Analyze, ClassReview, All |
Exakat since | 1.5.7 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Examples | FuelCMS, ExpressionEngine |
Available in | Entreprise Edition, Exakat Cloud |
1.2.68. Could Be Stringable¶
Stringable
is an interface that marks classes with a custom method to cast the object as a string. It was introduced in PHP 8.0.
Classes that defined a __toString() magic method may be turned into a string when the typehint, argument, return or property, requires it. This is not the case when strict_types is activated. Yet, until PHP 8.0, there was nothing to identify a class as such.
<?php
// This class may implement Stringable
class x {
function __tostring() {
return 'asd';
}
}
echo (new x);
?>
See also PHP RFC: Add Stringable interface and The Stringable interface.
1.2.68.1. Suggestions¶
- Add implements stringable to the class definition
- Add extends stringable to the interface definition
1.2.68.2. Specs¶
Short name | Classes/CouldBeStringable |
Rulesets | ClassReview, LintButWontExec, All, PHP recommendations |
Exakat since | 2.1.9 |
PHP Version | With PHP 8.0 and more recent |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Features | stringable, string, magic-method |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.69. Cyclic References¶
Avoid cyclic references.
Cyclic references happen when an object points to another object, which reciprocate. This is particularly possible with classes, when the child class has to keep a reference to the parent class.
<?php
class a {
private $p = null;
function foo() {
$this->p = new b();
// the current class is stored in the child class
$this->p->m($this);
}
}
class b {
private $pb = null;
function n($a) {
// the current class keeps a link to its parent
$this->pb = $a;
}
}
?>
Cyclic references, or circular references, are memory intensive : only the garbage collector can understand when they may be flushed from memory, which is a costly operation. On the other hand, in an acyclic reference code, the reference counter will know immediately know that an object is free or not.
See also About circular references in PHP and A Journey to find a memory leak.
1.2.69.1. Suggestions¶
- Use a different object when calling the child objects.
- Refactor your code to avoid the cyclic reference.
1.2.69.2. Specs¶
Short name | Classes/CyclicReferences |
Rulesets | Analyze, ClassReview, All |
Exakat since | 2.1.3 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.70. Defined Class Constants¶
Checks if class constants are defined. This includes class constants, one level of parent (extended) or interfaces (implemented).
This analysis takes into account native PHP, extension and stubs class definitions.
<?php
class X {
const Y = 2;
function foo() {
// This is defined on the line above
echo self::Y;
// This is not defined in the current code
echo X::X;
}
}
?>
1.2.70.1. Specs¶
Short name | Classes/DefinedConstants |
Rulesets | CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | Very high |
Features | class-constant |
Configurable by | php_core, php_extensions, stubs |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.71. Defined Parent MP¶
<?php
class foo {
protected function parentDefined() {}
protected function unusedParentMethod() {}
// visibility is checked too
protected function unusuableParentMethod() {}
}
class bar extends foo {
private function someMethod() {
// reported
parent::parentDefined();
// not reported, as method is unreachable in parent
parent::unusuableParentMethod();
// not reported, as method is undefined in parent
parent::parentUndefined();
}
protected function parentDefined2() {}
}
?>
1.2.71.1. Specs¶
Short name | Classes/DefinedParentMP |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.72. Defined Properties¶
List of properties that are explicitly defined in the class, its parents or traits.
<?php
class foo {
// property definition
private bar = 2;
}
?>
See also Properties.
1.2.72.1. Specs¶
Short name | Classes/DefinedProperty |
Rulesets | CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.73. Defined static:: Or self::¶
<?php
class x {
static public function definedStatic() {}
private definedStatic = 1;
public function method() {
self::definedStatic();
self::undefinedStatic();
static::definedStatic;
static::undefinedStatic;
}
}
?>
1.2.73.1. Specs¶
Short name | Classes/DefinedStaticMP |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.74. Law of Demeter¶
The law of Demeter specifies a number of constraints to apply to methodcalls from within an method, so as to keep dependencies to a minimum.
<?php
class x {
function foo($arg) {
$this->foo(); // calling oneself is OK
$this->x->bar(); // calling one's property is OK
$arg->bar2(); // calling arg's methods is OK
$local = new y();
$z = $y->bar3(); // calling a local variable is OK
$z->bar4(); // calling a method on a previous result is wrong
}
}
?>
See also Do your objects talk to strangers? and Law of Demeter.
1.2.74.1. Specs¶
Short name | Classes/DemeterLaw |
Rulesets | All |
Exakat since | 1.6.7 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.75. Dependant Abstract Classes¶
Abstract classes should be autonomous. It is recommended to avoid depending on methods, constant or properties that should be made available in inheriting classes, without explicitly abstracting them.
The following abstract classes make usage of constant, methods and properties, static or not, that are not defined in the class. This means the inheriting classes must provide those constants, methods and properties, but there is no way to enforce this.
This may also lead to dead code : when the abstract class is removed, the host class have unused properties and methods.
<?php
// autonomous abstract class : all it needs is within the class
abstract class c {
private $p = 0;
function foo() {
return ++$this->p;
}
}
// dependant abstract class : the inheriting classes needs to provide some properties or methods
abstract class c2 {
function foo() {
// $p must be provided by the extending class
return ++$this->p;
}
}
class c3 extends c2 {
private $p = 0;
}
?>
See also Traits/DependantTrait.
1.2.75.1. Suggestions¶
- Make the class only use its own resources
- Split the class in autonomous classes
- Add local property definitions to make the class independent
1.2.75.2. Specs¶
Short name | Classes/DependantAbstractClass |
Rulesets | Analyze, ClassReview, All |
Exakat since | 1.8.6 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.76. Different Argument Counts¶
Two methods with the same name shall have the same number of compulsory argument. PHP accepts different number of arguments between two methods, if the extra arguments have default values. Basically, they shall be called interchangeably with the same number of arguments.
The number of compulsory arguments is often mistaken for the same number of arguments. When this is the case, it leads to confusion between the two signatures. It will also create more difficulties when refactoring the signature.
While this code is legit, it is recommended to check if the two signatures could be synchronized, and reduce future surprises.
<?php
class x {
function foo($a ) {}
}
class y extends x {
// This method is compatible with the above, its signature is different
function foo($a, $b = 1) {}
}
?>
1.2.76.1. Suggestions¶
- Extract the extra arguments into other methods
- Remove the extra arguments
- Add the extra arguments to all the signatures
1.2.76.2. Specs¶
Short name | Classes/DifferentArgumentCounts |
Rulesets | Analyze, ClassReview, All |
Exakat since | 2.1.6 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.77. No Direct Call To Magic Method¶
PHP features magic methods, which are methods related to operators.
Magic methods, such as __get(), related to =, or __clone(), related to clone
, are supposed to be used in an object environment, and not with direct call.
It is recommended to use the magic method with its intended usage, and not to call it directly. For example, typecast to string
instead of calling the __toString() method.
<?php
// Write
print $x->a;
// instead of
print $x->__get('a');
class Foo {
private $b = secret;
public function __toString() {
return strtoupper($this->b);
}
}
$bar = new Foo();
echo (string) $bar;
?>
Accessing those methods in a static way is also discouraged.
See also Magic Methods and Magical PHP: `__call <https://www.garfieldtech.com/blog/magical-php-call>`_.
1.2.77.1. Specs¶
Short name | Classes/DirectCallToMagicMethod |
Rulesets | Analyze, CE, CI-checks, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Major |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.78. Disconnected Classes¶
One class is extending the other, but they do not use any features from one another. Basically, those two classes are using extends, but they are completely independent and may be separated.
When using the ‘extends’ keyword, the newly created classes are now acting together and making one. This should be visible in calls from one class to the other, or simply by property usage : they can’t live without each other.
On the other hand, two completely independent classes that are merged, although they should be kept separated.
<?php
class A {
private $pa = 1;
function fooA() {
$this->pa = 2;
}
}
// class B and Class A are totally independent
class B extends A {
private $pb = 1;
function fooB() {
$this->pb = 2;
}
}
// class C makes use of class A : it is dependent on the parent class
class C extends A {
private $pc = 1;
function fooB() {
$this->pc = 2 + $this->fooA();
}
}
?>
1.2.78.1. Suggestions¶
- Remove the extension
- Make actual usage of the classes, at least from one of them
1.2.78.2. Specs¶
Short name | Classes/DisconnectedClasses |
Rulesets | ClassReview, All |
Exakat since | 1.8.9 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Features | class |
Examples | WordPress |
Available in | Entreprise Edition, Exakat Cloud |
1.2.79. Don’t Send $this In Constructor¶
Don’t use$this
as an argument while in the __construct(). Until the constructor is finished, the object is not finished, and may be in an unstable state. Providing it to another code may lead to error.
This is true when the receiving structure puts the incoming object immediately to work, and don’t store it for later use.
<?php
// $this is only provided when Foo is constructed
class Foo {
private $bar = null;
private $data = array();
static public function build($data) {
$foo = new Foo($data);
// Can't build in one call. Must make it separate.
$foo->finalize();
}
private function __construct($data) {
// $this is provided too early
$this->data = $data;
}
function finalize() {
$this->bar = new Bar($this);
}
}
// $this is provided too early, leading to error in Bar
class Foo2 extends Foo {
private $bar = null;
private $data = array();
function __construct($data) {
// $this is provided too early
$this->bar = new Bar($this);
$this->data = $data;
}
}
class Bar {
function __construct(Foo $foo) {
// the cache is now initialized with a wrong
$this->cache = $foo->getIt();
}
}
?>
See also Don’t pass this out of a constructor.
1.2.79.1. Suggestions¶
- Finish the constructor first, then call an external object.
- Sending $this should be made accessible in a separate method, so external objects may call it.
- Sending the current may be the responsibility of the method creating the object.
1.2.79.2. Specs¶
Short name | Classes/DontSendThisInConstructor |
Rulesets | Analyze, All |
Exakat since | 1.0.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Examples | Woocommerce, Contao |
Available in | Entreprise Edition, Exakat Cloud |
1.2.80. Don’t Unset Properties¶
Don’t unset properties. They would go undefined, and raise warnings of undefined properties, even though the property is explicitly defined in the original class.
When getting rid of a property, assign it to null. This keeps the property defined in the object, yet allows existence check without errors.
<?php
class Foo {
public $a = 1;
}
$a = new Foo();
var_dump((array) $a) ;
// la propriété est reportée, et null
// ['a' => null]
unset($a->a);
var_dump((array) $a) ;
//Empty []
// Check if a property exists
var_dump($a->b === null);
// Same result as above, but with a warning
var_dump($a->c === null);
?>
This analysis works on properties and static properties. It also reports magic properties being unset.
Thanks for Benoit Burnichon for the original idea.
1.2.80.1. Suggestions¶
- Set the property to null or its default value
- Make the property an array, and set/unset its index
1.2.80.2. Specs¶
Short name | Classes/DontUnsetProperties |
Rulesets | Analyze, CE, CI-checks, php-cs-fixable, Top10, All |
Exakat since | 1.2.3 |
PHP Version | All |
Severity | Major |
Time To Fix | Slow (1 hour) |
Precision | Very high |
Features | property |
Examples | Vanilla, Typo3 |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.81. Dynamic Classes¶
Dynamic calls of classes.
<?php
class x {
static function staticMethod() {}
}
$class = 'x';
$class::staticMethod();
?>
1.2.81.1. Specs¶
Short name | Classes/DynamicClass |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.82. Dynamic Class Constant¶
Dynamic calls to class constants.
Constant may be dynamically called with the constant() function.
<?php
// Dynamic access to 'E_ALL'
echo constant('E_ALL');
interface i {
const MY_CONSTANT = 1;
}
// Dynamic access to 'E_ALL'
echo constant('i::MY_CONSTANT');
?>
1.2.82.1. Specs¶
Short name | Classes/DynamicConstantCall |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.83. Dynamic Methodcall¶
Dynamic calls to class methods.
<?php
class x {
static public function foo() {}
public function bar() {}
}
$staticmethod = 'foo';
// dynamic static method call to x::foo()
x::$staticmethod();
$method = 'bar';
// dynamic method call to bar()
$object = new x();
$object->$method();
?>
1.2.83.1. Specs¶
Short name | Classes/DynamicMethodCall |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Features | dynamic-call, method |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.84. Dynamic New¶
Dynamic instantiation of classes.
<?php
$object = new $classname()
?>
.
1.2.84.1. Specs¶
Short name | Classes/DynamicNew |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.85. Dynamic Property¶
Dynamic access to class property.
<?php
class x {
static public $foo = 1;
public $bar = 2;
}
$staticproperty = 'foo';
// dynamic static property call to x::$foo
echo x::${$staticproperty};
$property = 'bar';
// dynamic property call to bar()
$object = new x();
$object->$property = 4;
?>
1.2.85.1. Specs¶
Short name | Classes/DynamicPropertyCall |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.86. Dynamic Self Calls¶
A class that calls itself dynamically. This may be property or methods.
Calling itself dynamically happens when a class is configured to call various properties (container) or methods.
<?php
class x {
function foo() {
$f = 'goo';
return $this->$f();
}
function goo() {
return rand(1, 10);
}
}
?>
This rule is mostly useful internally, to side some special situations.
1.2.86.1. Specs¶
Short name | Classes/DynamicSelfCalls |
Rulesets | All |
Exakat since | 2.1.1 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.87. Empty Classes¶
Classes that do no define anything at all. This is probably dead code.
Classes that are directly derived from an exception are omitted.
<?php
//Empty class
class foo extends bar {}
//Not an empty class
class foo2 extends bar {
const FOO = 2;
}
//Not an empty class, as derived from Exception
class barException extends \Exception {}
?>
1.2.87.1. Suggestions¶
- Remove an empty class :it is probably dead code.
- Add some code to the class to make it concrete.
1.2.87.2. Specs¶
Short name | Classes/EmptyClass |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Examples | WordPress |
Available in | Entreprise Edition, Exakat Cloud |
1.2.88. 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.88.1. Specs¶
Short name | Classes/FinalByOcramius |
Rulesets | ClassReview, All |
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.89. Final Private Methods¶
PHP’s private methods cannot be overwritten, as they are dedicated to the current class. That way, thefinal
keyword is useless.
PHP 8.0 warns when it finds such a method.
<?php
class foo {
// Final and private both prevent child classes to overwrite the method
final private function bar() {}
// Final and protected (or public) keep this method available, but not overwritable
final protected function bar() {}
}
?>
See also Final Keyword.
1.2.89.1. Suggestions¶
- Remove the final keyword
- Relax visibility
1.2.89.2. Specs¶
Short name | Classes/FinalPrivate |
Rulesets | CE, ClassReview, CompatibilityPHP80, All |
Exakat since | 2.2.0 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.90. Final Class Usage¶
List of all final classes being used.
final may be applied to classes and methods.
<?php
class BaseClass {
public function test() {
echo 'BaseClass::test() called'.PHP_EOL;
}
final public function moreTesting() {
echo 'BaseClass::moreTesting() called'.PHP_EOL;
}
}
class ChildClass extends BaseClass {
public function moreTesting() {
echo 'ChildClass::moreTesting() called'.PHP_EOL;
}
}
// Results in Fatal error: Cannot override final method BaseClass::moreTesting()
?>
See also Final Keyword.
1.2.90.1. Specs¶
Short name | Classes/Finalclass |
Rulesets | ClassReview, LintButWontExec, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.91. Final Methods Usage¶
List of all final methods being used.
final may be applied to classes and methods.
<?php
class BaseClass {
public function test() {
echo 'BaseClass::test() called'.PHP_EOL;
}
final public function moreTesting() {
echo 'BaseClass::moreTesting() called'.PHP_EOL;
}
}
class ChildClass extends BaseClass {
public function moreTesting() {
echo 'ChildClass::moreTesting() called'.PHP_EOL;
}
}
// Results in Fatal error: Cannot override final method BaseClass::moreTesting()
?>
See also Final Keyword.
1.2.91.1. Specs¶
Short name | Classes/Finalmethod |
Rulesets | ClassReview, LintButWontExec, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.92. Fossilized Method¶
A method is fossilized when it is overwritten so often that changing a default value, a return type or an argument type is getting difficult.
This happens when a class is extended. When a method is overwritten once, it may be easy to update the signature in two places. The more methods are overwriting a parent method, the more difficult it is to update it.
This analysis counts the number of times a method is overwritten, and report any method that is overwritten more than 6 times. This threshold may be configured.
<?php
class x1 {
// foo1() is never overwritten. It is easy to update.
function foo1() {}
// foo7() is overwritten seven times. It is hard to update.
function foo7() {}
}
// classes x2 to x7, all overwrite foo7();
// Only x2 is presente here.
class x2 extends x1 {
function foo7() {}
}
?>
Name | Default | Type | Description |
fossilizationThreshold | 6 | integer | Minimal number of overwriting methods to consider a method difficult to update. |
1.2.92.1. Specs¶
Short name | Classes/FossilizedMethod |
Rulesets | ClassReview, Typechecks, All |
Exakat since | 2.0.6 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.93. 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.93.1. Specs¶
Short name | Classes/HasFluentInterface |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.94. Has Magic Method¶
The class has defined one of the magic methods.
The magic methods are : __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() and __debugInfo().
__construct() and __destruct() are omitted here.
<?php
class WithMagic {
// some more methods, const or properties
public function __get() {
// doSomething();
}
}
?>
See also Property overloading..
1.2.94.1. Specs¶
Short name | Classes/HasMagicProperty |
Rulesets | CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Features | magic-method |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.96. Identical Methods¶
When the parent class and the child class have the same method, the child might omit it. This reduces code duplication.
Duplicate code in methods is often the results of code evolution, where a method was copied with the hierarchy, but the original wasn’t removed.
This doesn’t apply to private methods, which are reserved for one class.
<?php
class a {
public function foo() {
return rand(0, 100);
}
}
class b extends a {
public function foo() {
return rand(0, 100);
}
}
?>
1.2.96.1. Suggestions¶
- Drop the method from the parent class, in particular if only one child uses the method.
- Drop the method from the child class, in particular if there are several children class
- Use an abstract method, and make sure every child has its own implementation
- Modify one of the methods so they are different
1.2.96.2. Specs¶
Short name | Classes/IdenticalMethods |
Rulesets | All |
Exakat since | 1.8.2 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Medium |
Features | method |
Available in | Entreprise Edition, Exakat Cloud |
1.2.97. Immutable Signature¶
Overwrites makes refactoring a method signature difficult. PHP enforces compatible signature, by checking if arguments have the same type, reference and default values.
In PHP 7.3, typehint had to be the same, or dropped. In PHP 7.4, typehint may be contravariant (arguments), or covariant (returntype).
This analysis may be configured with maxOverwrite
. By default, a minimum of 8 overwritten methods is considered difficult to update.
<?php
// Changing any of the four foo() method signature will trigger a PHP warning
class a {
function foo($a) {}
}
class ab1 extends a {
// four foo() methods have to be refactored at the same time!
function foo($ab1) {}
}
class ab2 extends a {
function foo($ab2) {}
}
class ab3 extends ab1 {
function foo($abc1) {}
}
?>
When refactoring a method, all the related methodcall may have to be updated too. Adding a type, a default value, or a new argument with default value won’t affect the calls, but only the definitions. Otherwise, calls will also have to be updated.
IDE may help with signature refactoring, such as Refactoring code.
Name | Default | Type | Description |
maxOverwrite | 8 | integer | Minimal number of method overwrite to consider that any refactor on the method signature is now hard. |
See also Covariance and contravariance (computer science) and extends.
1.2.97.1. Specs¶
Short name | Classes/ImmutableSignature |
Rulesets | Appinfo, CE, All |
Exakat since | 1.9.9 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Features | overwrite |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.98. Implement Is For Interface¶
With class heritage, implements should be used for interfaces, and extends with classes.
PHP defers the implements check until execution : the code in example does lint, but won,t run.
<?php
class x {
function foo() {}
}
interface y {
function foo();
}
// Use implements with an interface
class z implements y {}
// Implements is for an interface, not a class
class z implements x {}
?>
1.2.98.1. Suggestions¶
- Create an interface from the class, and use it with the implements keyword
1.2.98.2. Specs¶
Short name | Classes/ImplementIsForInterface |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Features | implements, interface |
Available in | Entreprise Edition, Exakat Cloud |
1.2.99. Implemented Methods Are Public¶
Class methods that are defined in an interface must be public. They cannot be either private, nor protected.
This error is not reported by lint, but is reported at execution time.
<?php
interface i {
function foo();
}
class X {
// This method is defined in the interface : it must be public
protected function foo() {}
// other methods may be private
private function bar() {}
}
?>
See also Interfaces and Interfaces - the next level of abstraction.
1.2.99.1. Suggestions¶
- Make the implemented method public
1.2.99.2. Specs¶
Short name | Classes/ImplementedMethodsArePublic |
Rulesets | Analyze, All |
Exakat since | 0.11.5 |
PHP Version | All |
Severity | Major |
Time To Fix | Instant (5 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.100. Incompatible Signature Methods¶
Methods should have the same signature when being overwritten.
The same signatures means the children class must have :
- the same name
- the same visibility or less restrictive
- the same typehint or removed
- the same default value or removed
- a reference like its parent
This problem emits a fatal error, for abstract methods, or a warning error, for normal methods. Yet, it is difficult to lint, because classes are often stored in different files. As such, PHP do lint each file independently, as unknown parent classes are not checked if not present. Yet, when executing the code, PHP lint the actual code and may encounter a fatal error.
<?php
class a {
public function foo($a = 1) {}
}
class ab extends a {
// foo is overloaded and now includes a default value for $a
public function foo($a) {}
}
?>
See also Object Inheritance.
1.2.100.1. Suggestions¶
- Make signatures compatible again
1.2.100.2. Specs¶
Short name | Classes/IncompatibleSignature |
Rulesets | Analyze, LintButWontExec, All |
Exakat since | 1.3.3 |
PHP Version | With PHP 7.4 and older |
Severity | Critical |
Time To Fix | Quick (30 mins) |
Precision | Medium |
Examples | SuiteCrm |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.101. Incompatible Signature Methods With Covariance¶
Methods should have the compatible signature when being overwritten.
The same signatures means the children class must have :
- the same name
- the same visibility or less restrictive
- the same contravariant typehint or removed
- the same covariant return typehint or removed
- the same default value or removed
- a reference like its parent
This problem emits a fatal error, for abstract methods, or a warning error, for normal methods. Yet, it is difficult to lint, because classes are often stored in different files. As such, PHP do lint each file independently, as unknown parent classes are not checked if not present. Yet, when executing the code, PHP lint the actual code and may encounter a fatal error.
<?php
class a {
public function foo($a = 1) {}
}
class ab extends a {
// foo is overloaded and now includes a default value for $a
public function foo($a) {}
}
?>
See also Object Inheritance, PHP RFC: Covariant Returns and Contravariant Parameters and Classes/IncompatibleSignature.
1.2.101.1. Suggestions¶
- Make signatures compatible again
1.2.101.2. Specs¶
Short name | Classes/IncompatibleSignature74 |
Rulesets | Analyze, All |
Exakat since | 1.3.3 |
PHP Version | With PHP 7.4 and more recent |
Severity | Critical |
Time To Fix | Quick (30 mins) |
Precision | Medium |
Features | type-covariance, type-contravariance |
Examples | SuiteCrm |
Available in | Entreprise Edition, Exakat Cloud |
1.2.102. Inherited Property Type Must Match¶
Properties that are inherited between classes must match.
This affect public and protected properties. Private properties are immune to this rule, as they actually are distinct properties.
<?php
class A {
private A $a;
protected array $b;
public $c;
}
class B extends A {
private A $a; // OK, as it is private
protected int $b; // type must match with the previous definition
public $c; // no type behaves just like a type : it must match too.
}
?>
See also Properties.
1.2.102.1. Suggestions¶
- Remove the definition in the child class
- Synchronize the definition of the property in the child class
1.2.102.2. Specs¶
Short name | Classes/InheritedPropertyMustMatch |
Rulesets | Analyze, ClassReview, LintButWontExec, All |
Exakat since | 2.2.2 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.103. Instantiating Abstract Class¶
PHP cannot instantiate an abstract class.
The classes are actually abstract classes, and should be derived into a concrete class to be instantiated.
<?php
abstract class Foo {
protected $a;
}
class Bar extends Foo {
protected $b;
}
// instantiating a concrete class.
new Bar();
// instantiating an abstract class.
// In real life, this is not possible also because the definition and the instantiation are in the same file
new Foo();
?>
See also Class Abstraction.
1.2.103.1. Specs¶
Short name | Classes/InstantiatingAbstractClass |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Major |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.104. Insufficient Property Typehint¶
The typehint used for a class property doesn’t cover all it usage.
The typehint is insufficient when a undefined method is called, or if members are access while the typehint is an interface.
<?php
class A {
function a1() {}
}
// PHP 7.4 and more recent
class B {
private A $a = null;
function b2() {
// this method is available in A
$this->a->a1();
// this method is NOT available in A
$this->a->a2();
}
}
// Supported by all PHP versions
class C {
private $a = null;
function __construct(A $a) {
$this->a = $a;
}
function b2() {
// this method is available in A
$this->a->a1();
// this method is NOT available in A
$this->a->a2();
}
}
?>
This analysis relies on typehinted properties, as introduced in PHP 7.4. It also relies on typehinted assignations at construct time : the typehint of the assigned argument will be used as the property typehint. Getters and setters are not considered here.
1.2.104.1. Suggestions¶
- Change the typehint to match the actual usage of the object in the class.
1.2.104.2. Specs¶
Short name | Classes/InsufficientPropertyTypehint |
Rulesets | ClassReview, All |
Exakat since | 2.0.2 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.105. Integer As Property¶
It is backward incompatible to use integers are property names. This feature was introduced in PHP 7.2.
If the code must be compatible with previous versions, avoid casting arrays to object.
<?php
// array to object
$arr = [0 => 1];
$obj = (object) $arr;
var_dump(
$obj,
$obj->{'0'}, // PHP 7.2+ accessible
$obj->{0} // PHP 7.2+ accessible
$obj->{'b'}, // always been accessible
);
?>
See also PHP RFC: Convert numeric keys in object/array casts.
1.2.105.1. Specs¶
Short name | Classes/IntegerAsProperty |
Rulesets | CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, CompatibilityPHP70, CompatibilityPHP71, All |
Exakat since | 1.0.4 |
PHP Version | With PHP 7.2 and older |
Severity | Major |
Time To Fix | Slow (1 hour) |
Precision | High |
Features | property |
Available in | Entreprise Edition, Exakat Cloud |
1.2.106. Is An Extension Class¶
Those classes belongs to a PHP Extensions.
<?php
// This is a native PHP class
$o = new Stdclass();
// This is not a native PHP class
$o = new Elephpant();
?>
1.2.106.1. Specs¶
Short name | Classes/IsExtClass |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | Very high |
Features | class, extension |
Available in | Entreprise Edition, Exakat Cloud |
1.2.107. Is Interface Method¶
Mark a method as part of an interface that the current class implements.
<?php
interface i {
function i20();
}
class x implements i {
// This is an interface method
function i20() {}
// This is not an interface method
function x20() {}
}
?>
1.2.107.1. Specs¶
Short name | Classes/IsInterfaceMethod |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.108. Is Not Class Family¶
Mark a static method call as inside the family of classes. Children are not considered here.
<?php
class a {
function familyMethod() {}
}
classs b {
function foo() {
self::familyMethod(); // This is a call to a family method
b::notAFamilyMethod(); // This is a call to a method of a class outside the family
}
}
?>
1.2.108.1. Specs¶
Short name | Classes/IsNotFamily |
Rulesets | CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.109. Is Upper Family¶
Does the static call is made within the current hierarchy of class, or, is it made in the class, in the children or outside.
This applies to static methodcalls, property accesses and class constants.
<?php
class AAA { function inAAA() {} } // upper family : grand-parent
class AA extends AAA { function inAA() {} } // upper family : parent
class A extends AA { function inA() {} } // current family
class B extends A { function inB() {} } // lower family
class C { function inC() {} } // outside family
?>
1.2.109.1. Specs¶
Short name | Classes/IsUpperFamily |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.110. Is A PHP Magic Property¶
Mark properties usage when they are actually a magic call.
<?php
class magicProperty {
public $b;
function __get($name) {
// do something with the value
}
function foo() {
$this->a;
$this->b;
}
}
?>
See also Magic Methods.
1.2.110.1. Specs¶
Short name | Classes/IsaMagicProperty |
Rulesets | CE, All |
Exakat since | 0.12.17 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.111. Locally Unused Property¶
Those properties are defined in a class, and this class doesn’t have any method that makes use of them.
While this is syntactically correct, it is unusual that defined resources are used in a child class. It may be worth moving the definition to another class, or to move accessing methods to the class.
<?php
class foo {
public $unused, $used;// property $unused is never used in this class
function bar() {
$this->used++; // property $used is used in this method
}
}
class foofoo extends foo {
function bar() {
$this->unused++; // property $unused is used in this method, but defined in the parent class
}
}
?>
1.2.111.1. Suggestions¶
- Move the property definition to the child classes
- Move some of the child method, using the property, to the parent class
1.2.111.2. Specs¶
Short name | Classes/LocallyUnusedProperty |
Rulesets | Dead code, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.112. Locally Used Property¶
Properties that are used in the class where they are defined.
<?php
class foo {
public $unused, $used;// property $unused is never used in this class
function bar() {
$this->used++; // property $used is used in this method
}
}
$foo = new Foo();
$foo->unused = 'here'; // property $unused is used outside the class definition
?>
1.2.112.1. Specs¶
Short name | Classes/LocallyUsedProperty |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.113. Magic Methods¶
List of PHP magic methods being used. The magic methods are
__call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state(), __clone() and __debugInfo().
__construct
and __destruct
are omitted here, as they are routinely used to create and destroy objects.
<?php
class foo{
// PHP Magic method, called when cloning an object.
function __clone() {}
}
?>
See also Magic Method.
1.2.113.1. Specs¶
Short name | Classes/MagicMethod |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.114. Magic Properties¶
List of magic properties used in the code
1.2.114.1. Specs¶
Short name | Classes/MagicProperties |
Rulesets | Inventory, All |
Exakat since | 1.9.5 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.115. 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.
1.2.115.1. Suggestions¶
- Add a default value whenever possible. This is easy for scalars, and array()
1.2.115.2. Specs¶
Short name | Classes/MakeDefault |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Instant (5 mins) |
Precision | High |
ClearPHP | use-properties-default-values |
Examples | LiveZilla, phpMyAdmin |
Available in | Entreprise Edition, Exakat Cloud |
1.2.116. Make Global A Property¶
Calling global (or $GLOBALS) in methods is slower and less testable than setting the global to a property, and using this property.
Using properties is slightly faster than calling global or $GLOBALS, though the gain is not important.
Setting the property in the constructor (or in a factory), makes the class easier to test, as there is now a single point of configuration.
<?php
// Wrong way
class fooBad {
function x() {
global $a;
$a->do();
// Or $GLOBALS['a']->do();
}
}
class fooGood {
private $bar = null;
function __construct() {
global $bar;
$this->bar = $bar;
// Even better, do this via arguments
}
function x() {
$this->a->do();
}
}
?>
1.2.116.1. Suggestions¶
- Avoid using global variables, and use properties instead
- Remove the usage of these global variables
1.2.116.2. Specs¶
Short name | Classes/MakeGlobalAProperty |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Features | class, global-variable |
Available in | Entreprise Edition, Exakat Cloud |
1.2.117. Make Magic Concrete¶
Speed up execution by replacing magic calls by concrete properties.
Magic properties are managed dynamically, with __get() and __set(). They replace property access by a methodcall, and they are much slower than the first.
When a property name is getting used more often, it is worth creating a concrete property, and skip the method call. The threshold for ‘magicMemberUsage’ is 1, by default.
<?php
class x {
private $values = array('a' => 1,
'b' => 2);
function __get($name) {
return $this->values[$name] ?? '';
}
}
$x = new x();
// Access to 'a' is repeated in the code, at least 'magicMemberUsage' time (cf configuration below)
echo $x->a;
?>
Name | Default | Type | Description |
magicMemberUsage | 1 | integer | Minimal number of magic member usage across the code, to trigger a concrete property. |
See also Performances/MemoizeMagicCall.
1.2.117.1. Suggestions¶
- Make frequently used properties concrete; keep the highly dynamic as magic
1.2.117.2. Specs¶
Short name | Classes/MakeMagicConcrete |
Rulesets | Performances, All |
Exakat since | 1.8.3 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.118. Method Is Overwritten¶
This rule marks a method that is overwritten in a child class.
<?php
class A {
function intactMethodA() {} // Not overwritten in any children
function overwrittenMethodInAA() {} // overwritten in AA
}
class AA extends A {
function intactMethodAA() {} // Not overwritten, because no extends
function overwrittenMethodInAA() {} // Not overwritten, because no extends
}
?>
1.2.118.1. Specs¶
Short name | Classes/MethodIsOverwritten |
Rulesets | All |
Exakat since | 0.10.9 |
PHP Version | All |
Severity | |
Time To Fix | Slow (1 hour) |
Precision | Very high |
Features | inheritance |
Available in | Entreprise Edition, Exakat Cloud |
1.2.119. Method Signature Must Be Compatible¶
Make sure methods signature are compatible.
PHP generates the infamous Fatal error at execution : Declaration of FooParent\:\:Bar() must be compatible with FooChildren\:\:Bar()
<?php
class x {
function xa() {}
}
class xxx extends xx {
function xa($a) {}
}
?>
1.2.119.1. Suggestions¶
- Fix the child class method() signature.
- Fix the parent class method() signature, after checking that it won’t affect the other children.
1.2.119.2. Specs¶
Short name | Classes/MethodSignatureMustBeCompatible |
Rulesets | Analyze, LintButWontExec, All |
Exakat since | 1.2.9 |
PHP Version | All |
Severity | Critical |
Time To Fix | Quick (30 mins) |
Precision | High |
Features | typehint, type-covariance, type-contravariance |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.120. Method Used Below¶
Mark methods that are used in children classes.
<?php
class foo {
// This method is used in children
protected function protectedMethod() {}
// This method is not used in children
protected function localProtectedMethod() {}
private function foobar() {
// protectedMethod is used here, but defined in parent
$this->localProtectedMethod();
}
}
class foofoo extends foo {
private function bar() {
// protectedMethod is used here, but defined in parent
$this->protectedMethod();
}
}
?>
This doesn’t mark the current class, nor the (grand-)`parent <https://www.php.net/manual/en/language.oop5.paamayim-nekudotayim.php>`_ ones.
1.2.120.1. Specs¶
Short name | Classes/MethodUsedBelow |
Rulesets | All |
Exakat since | 0.12.11 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.121. Mismatch Properties Typehints¶
Properties must match within the same family.
When a property is declared both in a parent class, and a child class, they must have the same type. The same type includes a possible null value.
This doesn’t apply to private properties, which are only visible locally.
<?php
// property $p is declared as an object of type a
class x {
protected A $p;
}
// property $p is declared again, this time without a type
class a extends x {
protected $p;
}
?>
This code will lint, but not execute.
1.2.121.1. Suggestions¶
- Remove some of the property declarations, and only keep it in the highest ranking parent
- Match the typehints of the property declarations
- Make the properties private
- Remove the child class (or the parent class)
1.2.121.2. Specs¶
Short name | Classes/MismatchProperties |
Rulesets | Analyze, ClassReview, LintButWontExec, All |
Exakat since | 2.1.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.122. Missing Abstract Method¶
Abstract methods must have a non-abstract version for the class to be complete. A class that is missing one abstract definition cannot be instantiated.
<?php
// This is a valid definition
class b extends a {
function foo() {}
function bar() {}
}
// This compiles, but will emit a fatal error if instantiated
class c extends a {
function bar() {}
}
// This illustration lint but doesn't run.
// moving this class at the beginning of the code will make lint fail
abstract class a {
abstract function foo() ;
}
?>
See also Classes Abstraction.
1.2.122.1. Suggestions¶
- Implement the missing methods
- Remove the partially implemented class
- Mark the partially implemented class abstract
1.2.122.2. Specs¶
Short name | Classes/MissingAbstractMethod |
Rulesets | Analyze, ClassReview, All |
Exakat since | 2.1.0 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.123. Multiple Classes In One File¶
It is regarded as a bad practice to store several classes in the same file. This is usually done to make life of __autoload() easier.
It is often unexpected to find class foo
in the bar.php
file. This is also the case for interfaces and traits.
<?php
// three classes in the same file
class foo {}
class bar {}
class foobar{}
?>
One good reason to have multiple classes in one file is to reduce include time by providing everything into one nice include.
See also Is it a bad practice to have multiple classes in the same file?.
1.2.123.1. Suggestions¶
- Split the file into smaller files, one for each class
1.2.123.2. Specs¶
Short name | Classes/MultipleClassesInFile |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.124. Multiple Class Declarations¶
It is possible to declare several times the same class in the code. PHP will not mention it until execution time, since declarations may be conditional.
<?php
$a = 1;
// Conditional declaration
if ($a == 1) {
class foo {
function method() { echo 'class 1';}
}
} else {
class foo {
function method() { echo 'class 2';}
}
}
(new foo())->method();
?>
It is recommended to avoid declaring several times the same class in the code. The best practice is to separate them with namespaces, they are for here for that purpose. In case those two classes are to be used interchangeably, the best is to use an abstract class or an interface.
1.2.124.1. Suggestions¶
- Store classes with different names in different namespaces
- Change the name of the classes and give them a common interface to allow from common behavior
1.2.124.2. Specs¶
Short name | Classes/MultipleDeclarations |
Rulesets | Analyze, CE, CI-checks, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Major |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.125. Multiple Property Declaration On One Line¶
Multiple properties are defined on the same line. They could be defined independently, on separate expressions.
Keeping properties separate helps documenting and refactoring them independently.
<?php
// multiple definition on one expression
class point {
private $x, $y, $z;
// more code
}
// one line, one definition
class point2 {
private $x;
private $y;
private $z;
// more code
}
?>
1.2.125.1. Suggestions¶
- Split the definitions to one by line
1.2.125.2. Specs¶
Short name | Classes/MultiplePropertyDeclarationOnOneLine |
Rulesets | All |
Exakat since | 2.2.2 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Available in | Entreprise Edition, Exakat Cloud |
1.2.126. Multiple Identical Trait Or Interface¶
There is no need to use the same trait, or implements the same interface more than once.
Up to PHP 7.1 (at least), this doesn’t raise any warning. Traits are only imported once, and interfaces may be implemented as many times as wanted.
<?php
class foo {
use t3,t3,t3;
}
class bar implements i,i,i {
}
?>
1.2.126.1. Suggestions¶
- Remove the duplicate trait or interfaces
1.2.126.2. Specs¶
Short name | Classes/MultipleTraitOrInterface |
Rulesets | Analyze, CE, CI-checks, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Instant (5 mins) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.127. 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.127.1. Specs¶
Short name | Classes/MutualExtension |
Rulesets | ClassReview, LintButWontExec, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Major |
Time To Fix | Quick (30 mins) |
Precision | High |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.128. New On Functioncall Or Identifier¶
Object instantiation with new works with or without arguments. Both are valid in PHP.
The analyzed code has less than 10% of one of the two forms : for consistency reasons, it is recommended to make them all the same.
<?php
$a = new stdClass();
// Parenthesis are used when arguments are compulsory
$mysql = new MySQLI($host, $user, $pass);
// Parenthesis are omitted when no arguments are available
// That also makes the instantiation look different
$b = new stdClass;
?>
1.2.128.1. Specs¶
Short name | Classes/NewOnFunctioncallOrIdentifier |
Rulesets | Preferences, All |
Exakat since | 0.9.8 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.129. No Magic Method With Array¶
Magic method__set()
doesn’t work for array syntax.
When overloading properties, they can only be used for scalar values, excluding arrays. Under the hood, PHP uses __get()
to reach for the name of the property, and doesn’t recognize the following index as an array. It yields an error : Indirect modification of overloaded property.
<?php
class c {
private $a;
private $o = array();
function __get($name) {
return $this->o[$name];
}
function foo() {
// property b doesn't exists
$this->b['a'] = 3;
print_r($this);
}
// This method has no impact on the issue
function __set($name, $value) {
$this->o[$name] = $value;
}
}
$c = new c();
$c->foo();
?>
It is possible to use the array syntax with a magic property : by making the __get
returns an array, the syntax will actually extract the expected item in the array.
This is not reported by linting.
In this analysis, only properties that are found to be magic are reported. For example, using the b property outside the class scope is not reported, as it would yield too many false-positives.
See also Overload.
1.2.129.1. Suggestions¶
- Use a distinct method to append a new value to that property
- Assign the whole array, and not just one of its elements
1.2.129.2. Specs¶
Short name | Classes/NoMagicWithArray |
Rulesets | Analyze, CE, CI-checks, LintButWontExec, All |
Exakat since | 0.12.4 |
PHP Version | All |
Severity | Major |
Time To Fix | Slow (1 hour) |
Precision | Medium |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.130. self, parent, static Outside Class¶
self, parent and static may be used in a trait : their actual value will be only known at execution time, when the trait is used.
<?php
// In the examples, self, parent and static may be used interchangeably
// This raises a Fatal error
//Fatal error: Uncaught Error: Cannot access static:: when no class scope is active
new static();
// static calls
echo self::CONSTANTE;
echo self::$property;
echo self::method();
// as a type hint
function foo(static $x) {
doSomething();
}
// as a instanceof
if ($x instanceof static) {
doSomething();
}
?>
Such syntax problem is only revealed at execution time : PHP raises a Fatal error.
The origin of the problem is usually a method that was moved outside a class, at least temporarily.
See also Scope Resolution Operator (::).
1.2.130.1. Specs¶
Short name | Classes/NoPSSOutsideClass |
Rulesets | Analyze, LintButWontExec, All |
Exakat since | 0.10.3 |
PHP Version | All |
Severity | Major |
Time To Fix | Slow (1 hour) |
Precision | High |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.131. 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 finally executed.
<?php
class x {
function foo() {
parent::foo();
}
}
?>
1.2.131.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.131.2. Specs¶
Short name | Classes/NoParent |
Rulesets | Analyze, CE, CI-checks, ClassReview, All |
Exakat since | 1.9.0 |
PHP Version | With PHP 7.4 and older |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.132. No Public Access¶
The properties below are declared with public access, but are never used publicly. They can be made protected or private.
<?php
class foo {
public $bar = 1; // Public, and used in public space
public $neverInPublic = 3; // Public, but never used in outside the class
function bar() {
$neverInPublic++;
}
}
$x = new foo();
$x->bar = 3;
$x->bar();
?>
1.2.132.1. Specs¶
Short name | Classes/NoPublicAccess |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.133. No Self Referencing Constant¶
It is not possible to use a constant to define itself in a class. It yields a fatal error at runtime.
The PHP error reads : Cannot declare `self <https://www.php.net/manual/en/language.oop5.paamayim-nekudotayim.php>`_-referencing constant '`self <https://www.php.net/manual/en/language.oop5.paamayim-nekudotayim.php>`_\:\:C2'
. Unlike PHP which is self-referencing, self referencing variables can’t have a value : just don’t use that.
<?php
class a {
const C1 = 1; // fully defined constant
const C2 = self::C2; // self referencing constant
const C3 = a::C3 + 2; // self referencing constant
}
?>
The code may access an already declared constant with self or with its class name.
<?php
class a {
const C1 = 1;
const C2 = a::C1;
}
?>
This error is not detected by linting. It is only detected at instantiation time : if the class is not used, it won’t appear.
1.2.133.1. Suggestions¶
- Give a literal value to this constant
- Give a constant value to this constant : other class constants or constant are allowed here.
1.2.133.2. Specs¶
Short name | Classes/NoSelfReferencingConstant |
Rulesets | Analyze, ClassReview, LintButWontExec, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.134. Non Nullable Getters¶
A getter needs to be nullable when a property is injected.
In particular, if the injection happens with a separate method, there is a time where the object is not consistent, and the property holds a default non-object value.
<?php
class Consistent {
private $db = null;
function __construct(Db $db) {
$this->db = $db;
// Object is immediately consistent
}
// Db might be null
function getDb() {
return $this->db;
}
}
class Inconsistent {
private $db = null;
function __construct() {
// No initialisation
}
// This might be called on time, or not
// This typehint cannot be nullable, nor use null as default
function setDb(DB $db) {
return $this->db;
}
// Db might be null
function getDb() {
return $this->db;
}
}
?>
1.2.134.1. Suggestions¶
- Remove the nullable option and the tests on
null
.
1.2.134.2. Specs¶
Short name | Classes/NonNullableSetters |
Rulesets | Analyze, ClassReview, All |
Exakat since | 1.9.6 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.135. Forgotten Visibility¶
Some classes elements (property, method, constant) are missing their explicit visibility.
By default, it is public. It should at least be mentioned as public, or may be reviewed as protected or private.
Class constants support also visibility since PHP 7.1.
final, static and abstract are not counted as visibility. Only public, private and protected. The PHP 4 var keyword is counted as undefined.
Traits, classes and interfaces are checked.
<?php
// Explicit visibility
class X {
protected sconst NO_VISIBILITY_CONST = 1; // For PHP 7.2 and later
private $noVisibilityProperty = 2;
public function Method() {}
}
// Missing visibility
class X {
const NO_VISIBILITY_CONST = 1; // For PHP 7.2 and later
var $noVisibilityProperty = 2; // Only with var
function NoVisibilityForMethod() {}
}
?>
See also Visibility and Understanding The Concept Of Visibility In Object Oriented PHP.
1.2.135.1. Suggestions¶
- Always add explicit visibility to methods and constants in a class
- Always add explicit visibility to properties in a class, after PHP 7.4
1.2.135.2. Specs¶
Short name | Classes/NonPpp |
Rulesets | Analyze, CE, CI-checks, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Instant (5 mins) |
Precision | Very high |
ClearPHP | always-have-visibility |
Examples | FuelCMS, LiveZilla |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.136. Non Static Methods Called In A Static¶
PHP 7.0, and more recent versions, yield a deprecated error : Non-`static <https://www.php.net/manual/en/language.oop5.static.php>`_ method A\:\:B() should not be called statically
.
PHP 5 and older doesn’t check that a method is static or not : at any point, the code may call one method statically.
<?php
class x {
static public function sm( ) { echo __METHOD__.\n; }
public public sm( ) { echo __METHOD__.\n; }
}
x::sm( ); // echo x::sm
// Dynamic call
['x', 'sm']();
[\x::class, 'sm']();
$s = 'x::sm';
$s();
?>
It is a bad idea to call non-static method statically. Such method may make use of special variable $this, which will be undefined. PHP will not check those calls at compile time, nor at running time.
It is recommended to update this situation : make the method actually static, or use it only in object context.
Note that this analysis reports all static method call made on a non-static method, even within the same class or class hierarchy. PHP silently accepts static call to any in-family method.
<?php
class x {
public function foo( ) { self::bar() }
public function bar( ) { echo __METHOD__.\n; }
}
?>
See also Static Keyword <https://www.php.net/manual/en/language.oop5.`static.php>`_.
1.2.136.1. Suggestions¶
- Call the method the correct way
- Define the method as static
1.2.136.2. Specs¶
Short name | Classes/NonStaticMethodsCalledStatic |
Rulesets | Analyze, CE, CI-checks, CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Medium |
Examples | Dolphin, Magento |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.137. Normal Methods¶
Spot normal Methods.
<?php
class foo{
// Normal method
private function bar() {}
// Static method
private static function barbar() {}
}
?>
1.2.137.1. Specs¶
Short name | Classes/NormalMethods |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.138. Null On New¶
Until PHP 7, some classes instantiation could yield null, instead of throwing an exception.
After issuing a ‘new’ with those classes, it was important to check if the returned object were null or not. No exception were thrown.
<?php
// Example extracted from the wiki below
$mf = new MessageFormatter('en_US', '{this was made intentionally incorrect}');
if ($mf === null) {
echo 'Surprise!';
}
?>
This inconsistency has been cleaned in PHP 7 : see See Internal Constructor Behavior See also PHP RFC: Constructor behaviour of internal classes.
1.2.138.1. Suggestions¶
- Remove the check on null after a new instantiation
1.2.138.2. Specs¶
Short name | Classes/NullOnNew |
Rulesets | CompatibilityPHP53, CompatibilityPHP54, CompatibilityPHP55, CompatibilityPHP56, All |
Exakat since | 0.8.4 |
PHP Version | With PHP 7.0 and older |
Severity | Major |
Time To Fix | Instant (5 mins) |
Precision | Very high |
Features | class, new, null |
Available in | Entreprise Edition, Exakat Cloud |
1.2.139. Old Style Constructor¶
PHP classes used to have the method bearing the same name as the class acts as the constructor. That was PHP 4, and early PHP 5.
The manual issues a warning about this syntax : Old style constructors are DEPRECATED in PHP 7.0, and will be removed in a future version. You should always use `__construct() <https://www.php.net/manual/en/language.oop5.decon.php>`_ in new code.
<?php
namespace {
// Global namespace is important
class foo {
function foo() {
// This acts as the old-style constructor, and is reported by PHP
}
}
class bar {
function __construct() { }
function bar() {
// This doesn't act as constructor, as bar has a __construct() method
}
}
}
namespace Foo\Bar{
class foo {
function foo() {
// This doesn't act as constructor, as bar is not in the global namespace
}
}
}
?>
This is no more the case in PHP 5, which relies on __construct()
to do so. Having this old style constructor may bring in confusion, unless you are also supporting old time PHP 4.
Note that classes with methods bearing the class name, but inside a namespace are not following this convention, as this is not breaking backward compatibility. Those are excluded from the analyze.
See also Constructors and Destructors.
1.2.139.1. Suggestions¶
- Remove old style constructor and make it
__construct()
- Remove old libraries and use a modern component
1.2.139.2. Specs¶
Short name | Classes/OldStyleConstructor |
Rulesets | Analyze, Appinfo, CE, CompatibilityPHP80, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
ClearPHP | no-php4-class-syntax |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.140. Var Keyword¶
Var was used in PHP 4 to mark properties as public. Nowadays, new keywords are available : public, protected, private. Var is equivalent to public.
It is recommended to avoid using var, and explicitly use the new keywords.
<?php
class foo {
public $bar = 1;
// Avoid var
//var $bar = 1;
}
?>
See also Visibility.
1.2.140.1. Suggestions¶
- It is recommended to avoid using var, and explicitly use the new keywords : private, protected, public
1.2.140.2. Specs¶
Short name | Classes/OldStyleVar |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
ClearPHP | no-php4-class-syntax |
Examples | xataface |
Available in | Entreprise Edition, Exakat Cloud |
1.2.141. One Object Operator Per Line¶
Avoid using more than one operator -> per line, to prevent information overload.
<?php
// Spread operators on multiple lines
$object->firstMethodCall()
->property
->secondMethodCall();
// This is not readable
$object->firstMethodCall()->property->secondMethodCall();
// This is OK, as objects are different.
$a2->b2($c2->d2, $e2->f2);
?>
1.2.141.1. Specs¶
Short name | Classes/OneObjectOperatorPerLine |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.142. Only Static Methods¶
Marks a class that has only static methods.
1.2.142.1. Specs¶
Short name | Classes/OnlyStaticMethods |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.143. Order Of Declaration¶
The order used to declare members and methods has a great impact on readability and maintenance. However, practices varies greatly. As usual, being consistent is the most important and useful.
The suggested order is the following : traits, constants, properties, methods. Optional characteristics, like final, static… are not specified. Special methods names are not specified.
<?php
class x {
use traits;
const CONSTANTS = 1;
const CONSTANTS2 = 1;
const CONSTANTS3 = 1;
private $property = 2;
private $property2 = 2;
private $property3 = 2;
public function foo() {}
public function foo2() {}
public function foo3() {}
public function foo4() {}
}
?>
1.2.143.1. Suggestions¶
- Always declare class elements (traits, constants, properties, methods) in the same order.
1.2.143.2. Specs¶
Short name | Classes/OrderOfDeclaration |
Rulesets | All |
Exakat since | 0.11.7 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | Medium |
Available in | Entreprise Edition, Exakat Cloud |
1.2.144. Overwritten Class Constants¶
Those class constants are overwriting a parent class’s constant. This may lead to confusion, as the value of the constant may change depending on the way it is called.
<?php
class foo {
const C = 1;
}
class bar extends foo {
const C = 2;
function x() {
// depending on the access to C, value is different.
print self::C.' '.static::C.' '.parent::C;
}
}
?>
1.2.144.1. Specs¶
Short name | Classes/OverwrittenConst |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Major |
Time To Fix | Slow (1 hour) |
Precision | High |
Features | class-constant, overwrite |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.145. Properties Declaration Consistence¶
Properties may be declared all at once, or one by one.
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 choosing unique declarations or multiple depends on coding style and files.
<?php
class x {
// Some declarations are made by batch
private $a1 = 1,
$a2 = 2;
public $c = 1, $c2 = 2, $c4 = 3;
// Most declarations are made one by one
protected $b = 1;
protected $b1 = 1;
protected $b2 = 1;
protected $b3 = 1;
protected $b4 = 1;
protected $b5 = 1;
protected $b6 = 1;
protected $b7 = 1;
protected $b8 = 1;
protected $b9 = 1;
protected $b10 = 1;
protected $b11 = 1;
protected $b12 = 1;
protected $b13 = 1;
protected $b14 = 1;
protected $b15 = 1;
protected $b16 = 1;
protected $b17 = 1;
protected $b18 = 1;
protected $b19 = 1;
}
?>
See also Properties.
1.2.145.1. Specs¶
Short name | Classes/PPPDeclarationStyle |
Rulesets | Preferences, All |
Exakat since | 1.2.1 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | Very high |
Features | property |
Available in | Entreprise Edition, Exakat Cloud |
1.2.146. Parent First¶
<?php
class father {
protected $name = null;
function __construct() {
$this->name = init();
}
}
class goodSon {
function __construct() {
// parent is build immediately,
parent::__construct();
echo my name is.$this->name;
}
}
class badSon {
function __construct() {
// This will fail.
echo my name is.$this->name;
// parent is build later,
parent::__construct();
}
}
?>
This analysis doesn’t apply to Exceptions.
1.2.146.1. Suggestions¶
- Use
parent::__construct
as the first call in the constructor.
1.2.146.2. Specs¶
Short name | Classes/ParentFirst |
Rulesets | Analyze, Suggestions, All |
Exakat since | 1.0.5 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Examples | shopware, PrestaShop |
Available in | Entreprise Edition, Exakat Cloud |
1.2.147. Property Could Be Local¶
A property only used in one method may be turned into a local variable.
Public an protected properties are omitted here : they may be modified somewhere else, in the code. This analysis may be upgraded to support those properties, when tracking of such properties becomes available.
Classes where only one non-magic method is available are omitted.
Traits with private properties are processed the same way.
<?php
class x {
private $foo = 1;
// Magic method, and constructor in particular, are omitted.
function __construct($foo) {
$this->foo = $foo;
}
function bar() {
$this->foo++;
return $this->foo;
}
function barbar() {}
}
?>
1.2.147.1. Suggestions¶
- Remove the property and make it an argument in the method
- Use that property elsewhere
1.2.147.2. Specs¶
Short name | Classes/PropertyCouldBeLocal |
Rulesets | Analyze, ClassReview, All |
Exakat since | 1.1.7 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Examples | Mautic, Typo3 |
Available in | Entreprise Edition, Exakat Cloud |
1.2.148. Property Names¶
Variables are used in property definitions, when they are located in a class.
<?php
static $x; // not a property, a static variable
class foo {
static $x; // now, this is a static property
public $y, $z = 1; // normal properties
public function bar() {
static $x; // again, a static variable
}
}
?>
See also Properties.
1.2.148.1. Specs¶
Short name | Classes/PropertyDefinition |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.149. Never Used Properties¶
Properties that are never used. They are defined in a class or a trait, but they never actually used.
Properties are considered used when they are used locally, in the same class as their definition, or in a parent class : a parent class is always included with the current class.
On the other hand, properties which are defined in a class, but only used in children classes is considered unused, since children may also avoid using it.
<?php
class foo {
public $usedProperty = 1;
// Never used anywhere
public $unusedProperty = 2;
function bar() {
// Used internally
++$this->usedProperty;
}
}
class foo2 extends foo {
function bar2() {
// Used in child class
++$this->usedProperty;
}
}
// Used externally
++$this->usedProperty;
?>
1.2.149.1. Suggestions¶
- Drop unused properties
- Change the name of the unused properties
- Move the properties to children classes
- Find usage for unused properties
1.2.149.2. Specs¶
Short name | Classes/PropertyNeverUsed |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Features | property |
Examples | WordPress |
Available in | Entreprise Edition, Exakat Cloud |
1.2.150. Property Used Above¶
It may also be used in the current class, or its children, though this is not reported by this analyzer.
<?php
class A {
public function foo() {
$this->pb++;
}
}
class B extends A {
protected $pb = 0; // property used above
protected $pb2 = 0; // property NOT used above
}
?>
See also Classes/PropertyUsedBelow.
1.2.150.1. Suggestions¶
- Move the definition of the property to the upper class
- Move the usage of the property to the lower class
1.2.150.2. Specs¶
Short name | Classes/PropertyUsedAbove |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | |
Time To Fix | Slow (1 hour) |
Precision | Medium |
Features | property, inheritance |
Available in | Entreprise Edition, Exakat Cloud |
1.2.151. Property Used Below¶
This rule marks properties that are used in children classes.
<?php
class foo {
// This property is used in children
protected protectedProperty = 1;
// This property is not used in children
protected localProtectedProperty = 1;
private function foobar() {
// protectedProperty is used here, but defined in parent
$this->localProtectedProperty = 3;
}
}
class foofoo extends foo {
private function bar() {
// protectedProperty is used here, but defined in parent
$this->protectedProperty = 3;
}
}
?>
This analysis doesn’t mark the current class, nor the parent or grand parent classes. See also Classes/PropertyUsedAbove.
1.2.151.1. Specs¶
Short name | Classes/PropertyUsedBelow |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | Medium |
Features | property |
Available in | Entreprise Edition, Exakat Cloud |
1.2.152. Property Used In One Method Only¶
Properties should be used in several methods. When a property is used in only one method, this should have be of another shape.
Properties used in one method only may be used several times, and read only. This may be a class constant. Such properties are meant to be overwritten by an extending class, and that’s possible with class constants.
Properties that read and written may be converted into a variable, static to the method. This way, they are kept close to the method, and do not pollute the object’s properties.
<?php
class foo {
private $once = 1;
const ONCE = 1;
private $counter = 0;
function bar() {
// $this->once is never used anywhere else.
someFunction($this->once);
someFunction(self::ONCE); // Make clear that it is a
}
function bar2() {
static $localCounter = 0;
$this->counter++;
// $this->once is only used here, for distinguising calls to someFunction2
if ($this->counter > 10) { // $this->counter is used only in bar2, but it may be used several times
return false;
}
someFunction2($this->counter);
// $localCounter keeps track for all the calls
if ($localCounter > 10) {
return false;
}
someFunction2($localCounter);
}
}
?>
Note : properties used only once are not returned by this analysis. They are omitted, and are available in the analysis Used Once Property.
1.2.152.1. Suggestions¶
- Drop the property, and inline the value
- Drop the property, and make the property a local variable
- Use the property in another method
1.2.152.2. Specs¶
Short name | Classes/PropertyUsedInOneMethodOnly |
Rulesets | Analyze, All |
Exakat since | 0.10.3 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Examples | Contao |
Available in | Entreprise Edition, Exakat Cloud |
1.2.153. Internally Used Properties¶
Properties that are used internally.
<?php
class x {
public $internallyUsedProperty = 1;
public $externallyUsedProperty = 1;
public $alsoExternallyUsedProperty = 1;
function foo() {
$this->internallyUsedProperty = 2;
}
}
class y extends x {
function bar() {
$this->externallyUsedProperty = 3;
}
}
$X = new x();
$X->alsoExternallyUsedProperty = 3;
?>
1.2.153.1. Specs¶
Short name | Classes/PropertyUsedInternally |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Instant (5 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.154. Parent, Static Or Self Outside Class¶
PHP 7.0 and later detect their usage at compile time, and emits a fatal error.
<?php
class x {
const Y = 1;
function foo() {
// self is \x
echo self::Y;
}
}
const Z = 1;
// This lint but won't anymore
echo self::Z;
?>
Static may be used in a function or a closure, but not globally.
1.2.154.1. Specs¶
Short name | Classes/PssWithoutClass |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | With PHP 7.0 and older |
Severity | Major |
Time To Fix | Quick (30 mins) |
Precision | High |
Features | parent, self, static, class |
Available in | Entreprise Edition, Exakat Cloud |
1.2.155. Raised Access Level¶
A property’s visibility may be lowered, but not raised.
This error may be detected when the classes are all in the same file : then, PHP reports the problem. However, when the classes are separated in different files, as it is customary, PHP won’t check this at linting time, yielding a fatal error at execution time.
First file.
<?php
class Foo {
public $publicProperty;
protected $protectedProperty;
private $privateProperty;
}
?>
Second file.
<?php
class Bar extends Foo {
private $publicProperty;
private $protectedProperty;
private $privateProperty; // This one is OK
}
?>
See also Visibility and Understanding the concept of visibility in object oriented php.
1.2.155.1. Suggestions¶
- Lower the visibility in the child class
- Raise the visibility in the parent class
1.2.155.2. Specs¶
Short name | Classes/RaisedAccessLevel |
Rulesets | ClassReview, LintButWontExec, All |
Exakat since | 0.10.0 |
PHP Version | All |
Severity | Critical |
Time To Fix | Quick (30 mins) |
Precision | High |
Note | This issue may lint but will not run |
Available in | Entreprise Edition, Exakat Cloud |
1.2.156. Redefined Class Constants¶
Redefined class constants.
Class constants may be redefined, though it is prone to errors when using them, as it is now crucial to use the right class name to access the right value.
<?php
class a {
const A = 1;
}
class b extends a {
const A = 2;
}
class c extends c { }
echo a::A, ' ', b::A, ' ', c::A;
// 1 2 2
?>
It is recommended to use distinct names.
1.2.156.1. Specs¶
Short name | Classes/RedefinedConstants |
Rulesets | CE, CI-checks, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.157. Redefined Default¶
Classes allows properties to be set with a default value. When those properties get, unconditionally, another value at constructor time, then one of the default value are useless. One of those definition should go : it is better to define properties outside the constructor.
<?php
class foo {
public $redefined = 1;
public function __construct( ) {
$this->redefined = 2;
}
}
?>
1.2.157.1. Suggestions¶
- Move the default assignation to the property definition
- Drop the reassignation in the constructor
1.2.157.2. Specs¶
Short name | Classes/RedefinedDefault |
Rulesets | CE, CI-checks, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Major |
Time To Fix | Slow (1 hour) |
Precision | High |
Examples | Piwigo |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.158. Redefined Methods¶
Redefined methods are overwritten methods. Those methods are defined in different classes that are part of the same classes hierarchy.
Protected and public redefined methods replace each other. Private methods are kept separated, and depends on the caller to be distinguished.
<?php
class foo {
function method() {
return 1;
}
}
class bar extends foo {
function method() {
return 2;
}
}
?>
See also Object Inheritance.
1.2.158.1. Specs¶
Short name | Classes/RedefinedMethods |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.159. Redefined Private Property¶
Private properties are local to their defined class. PHP doesn’t forbid the re-declaration of a private property in a child class.
However, having two or more properties with the same name, in the class hierarchy tends to be error prone.
<?php
class A {
private $isReady = true;
}
class B {
private $isReady = false;
}
?>
1.2.159.1. Specs¶
Short name | Classes/RedefinedPrivateProperty |
Rulesets | All |
Exakat since | 1.2.3 |
PHP Version | All |
Severity | Major |
Time To Fix | Slow (1 hour) |
Precision | High |
Examples | Zurmo |
Available in | Entreprise Edition, Exakat Cloud |
1.2.160. Redefined Property¶
Property redefined in a parent class.
Using heritage, it is possible to define several times the same property, at different levels of the hierarchy.
<?php
class foo {
protected $aProperty = 1;
}
class bar extends foo {
// This property is redefined in the parent class, leading to potential confusion
protected $aProperty = 1;
}
?>
When this is the case, it is difficult to understand which class will actually handle the property.
In the case of a private property, the different instances will stay distinct. In the case of protected or public properties, they will all share the same value.
It is recommended to avoid redefining the same property in a hierarchy.
1.2.160.1. Suggestions¶
- Remove of the definition
1.2.160.2. Specs¶
Short name | Classes/RedefinedProperty |
Rulesets | ClassReview, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | Very high |
Available in | Entreprise Edition, Exakat Cloud |
1.2.161. Not Same Name As File¶
The class, interface or trait in this file as a different name, case included, than the file name.
In the following example, the file name is Foo.php
.
<?php
// normal host of this file
class Foo {
// some code
}
// case-typo this file
class foo {
// some code
}
// strangely stored class
class foo {
// some code
}
// This is valid name, but there is also a Foo class, and other classe in this file.
interface Foo {}
?>
1.2.161.1. Specs¶
Short name | Classes/SameNameAsFile |
Rulesets | All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.162. Scalar Or Object Property¶
Property shouldn’t use both object and scalar syntaxes. When a property may be an object, it is recommended to implement the Null Object pattern : instead of checking if the property is scalar, make it always object.
<?php
class x {
public $display = 'echo';
function foo($string) {
if (is_string($this->display)) {
echo $this->string;
} elseif ($this->display instanceof myDisplayInterface) {
$display->display();
} else {
print Error when displaying\n;
}
}
}
interface myDisplayInterface {
public function display($string); // does the display in its own way
}
class nullDisplay implements myDisplayInterface {
// implements myDisplayInterface but does nothing
public function display($string) {}
}
class x2 {
public $display = null;
public function __construct() {
$this->display = new nullDisplay();
}
function foo($string) {
// Keep the check, as $display is public, and may get wrong values
if ($this->display instanceof myDisplayInterface) {
$display->display();
} else {
print Error when displaying\n;
}
}
}
// Simple class for echo
class echoDisplay implements myDisplayInterface {
// implements myDisplayInterface but does nothing
public function display($string) {
echo $string;
}
}
?>
See also Null Object Pattern. and The Null Object Pattern.
1.2.162.1. Suggestions¶
- Only use one type of syntax with your properties.
1.2.162.2. Specs¶
Short name | Classes/ScalarOrObjectProperty |
Rulesets | Analyze, All |
Exakat since | 0.12.3 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Examples | SugarCrm |
Available in | Entreprise Edition, Exakat Cloud |
1.2.163. Should Deep Clone¶
By default, PHP makes a shallow clone. It only clone the scalars, and keep the reference to any object already referenced. This means that the cloned object and its original share any object they hold as property.
This is where the magic method __clone() comes into play. It is called, when defined, at clone time, so that the cloned object may clone all the needed sub-objects.
It is recommended to use the __clone() method whenever the objects hold objects.
<?php
class a {
public $b = null;
function __construct() {
$this->b = new Stdclass();
$this->b->c = 1;
}
}
class ab extends a {
function __clone() {
$this->b = clone $this->b;
}
}
// class A is shallow clone, so $a->b is not cloned
$a = new a();
$b = clone $a;
$a->b->c = 3;
echo $b->b->c;
// displays 3
// class Ab is deep clone, so $a->b is cloned
$a = new ab();
$b = clone $a;
$a->b->c = 3;
echo $b->b->c;
// displays 1
?>
See also PHP Clone and Shallow vs Deep Copying and Cloning objects.
1.2.163.1. Specs¶
Short name | Classes/ShouldDeepClone |
Rulesets | Suggestions, All |
Exakat since | 1.7.0 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.164. Should Have Destructor¶
PHP destructors are called when the object has to be destroyed. By default, PHP calls recursively the destructor on internal objects, until everything is unset.
Unsetting objects and resources explicitly in the destructor is a good practice to reduce the amount of memory in use. It helps PHP resource counter to keep the numbers low, and easier to clean. This is a major advantage for long running scripts.
<?php
class x {
function __construct() {
$this->p = new y();
}
function __destruct() {
print __METHOD__.PHP_EOL;
unset($this->p);
}
}
class y {
function __construct() {
print __METHOD__.PHP_EOL;
$this->p = new y();
}
function __destruct() {
print __METHOD__.PHP_EOL;
unset($this->p);
}
}
$a = (new x);
sleep(1);
// This increment the resource counter by one for the property.
$p = $a->p;
unset($a);
sleep(3);
print 'end'.PHP_EOL;
// Y destructor is only called here, as the object still exists in $p.
?>
See also Destructor, and Php Destructors.
1.2.164.1. Suggestions¶
- Add a destruct method to the class to help clean at destruction time.
1.2.164.2. Specs¶
Short name | Classes/ShouldHaveDestructor |
Rulesets | Suggestions, All |
Exakat since | 1.5.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Exakat Cloud |
1.2.165. Could Use self¶
self
keyword refers to the current class, or any of its parents. Using it is just as fast as the full class name, it is as readable and it is will not be changed upon class or namespace change.
It is also routinely used in traits : there, self
represents the class in which the trait is used, or the trait itself.
<?php
class x {
const FOO = 1;
public function bar() {
return self::FOO;
// same as return x::FOO;
}
}
?>
See also Scope Resolution Operator (::).
1.2.165.1. Suggestions¶
- replace the explicit name with self
1.2.165.2. Specs¶
Short name | Classes/ShouldUseSelf |
Rulesets | Analyze, ClassReview, Suggestions, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Instant (5 mins) |
Precision | High |
Features | self, class |
Examples | WordPress, LiveZilla |
Available in | Entreprise Edition, Exakat Cloud |
1.2.166. Should Use Local Class¶
Methods should use the defining class, or be functions.
Methods should use $this
with another method or a property, or call parent\:\:
. Static methods should call another static method, or a static property.
Methods which are overwritten by a child class are omitted : the parent class act as a default value for the children class, and this is correct.
<?php
class foo {
public function __construct() {
// This method should do something locally, or be removed.
}
}
class bar extends foo {
private $a = 1;
public function __construct() {
// Calling parent:: is sufficient
parent::__construct();
}
public function barbar() {
// This is acting on the local object
$this->a++;
}
public function barfoo($b) {
// This has no action on the local object. It could be a function or a closure where needed
return 3 + $b;
}
}
?>
Note that a method using a class constant is not considered as using the local class, for this analyzer.
1.2.166.1. Suggestions¶
- Make this method a function
- Actually use $this, or any related attributes of the class
1.2.166.2. Specs¶
Short name | Classes/ShouldUseThis |
Rulesets | Analyze, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
ClearPHP | not-a-method |
Available in | Entreprise Edition, Exakat Cloud |
1.2.167. Static Methods Can’t Contain $this¶
<?php
class foo {
// Static method may access other static methods, or property, or none.
static function staticBar() {
// This is not possible in a static method
return self::otherStaticBar() . static::$staticProperty;
}
static function bar() {
// This is not possible in a static method
return $this->property;
}
}
?>
Either this is not a static method, which is fixed by removing the static
keyword, or replace all $this mention by static properties Class\:\:$property
.
See also Static Keyword <https://www.php.net/manual/en/language.oop5.`static.php>`_
1.2.167.1. Suggestions¶
- Remove any $this usage
- Turn any $this usage into a static call : $this->foo() => self::foo()
1.2.167.2. Specs¶
Short name | Classes/StaticContainsThis |
Rulesets | Analyze, CE, CI-checks, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Major |
Time To Fix | Quick (30 mins) |
Precision | High |
ClearPHP | no-static-this |
Examples | xataface, SugarCrm |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.168. Static Methods¶
List of all static methods.
<?php
class foo {
static public function staticMethod() {
}
public function notStaticMethod() {
}
private function method() {
// This is not a property
new static();
}
}
?>
1.2.168.1. Specs¶
Short name | Classes/StaticMethods |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.169. Static Methods Called From Object¶
Besides this, static methods are normal methods that may be called directly from object context, to perform some utility task.
To maintain code readability, it is recommended to call static method in a static way, rather than within object context.
<?php
class x {
static function y( ) {}
}
$z = new x( );
$z->y( ); // Readability : no one knows it is a static call
x::y( ); // Readability : here we know
?>
1.2.169.1. Suggestions¶
- Switch to static method syntax
- Remove the static option from the method
1.2.169.2. Specs¶
Short name | Classes/StaticMethodsCalledFromObject |
Rulesets | Analyze, CE, CI-checks, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Quick (30 mins) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.170. Static Properties¶
List of all static properties.
<?php
class foo {
static public $staticProperty = 1;
public $notStaticProperty = 2;
private function method() {
// This is not a property
new static();
}
}
function bar() {
// This is not a static property
static $staticVariable;
//....
}
?>
1.2.170.1. Specs¶
Short name | Classes/StaticProperties |
Rulesets | Appinfo, CE, All |
Exakat since | 0.8.4 |
PHP Version | All |
Severity | Minor |
Time To Fix | Slow (1 hour) |
Precision | High |
Available in | Entreprise Edition, Community Edition, Exakat Cloud |
1.2.171. Strange Names In Classes¶
Those methods, properties or constants should have another name.
Ever wondered why the __constructor
is never called? Or the __consturct
?
Those errors most often originate from typos, or quick fixes that where not fully tested. Other times, they were badly chosen, or ran into PHP’s own reserved keywords.
<?php
class foo {
// The real constructor
function __construct() {}
// The fake constructor
function __constructor() {}
// The 'typo'ed' constructor
function __consturct() {}
// This doesn't clone
function clone() {}
}
?>
1.2.171.1. Suggestions¶
- Use the proper name
- Remove the method, when it is not used and tests still pass.
1.2.171.2. Specs¶
Short name | Classes/StrangeName |
Rulesets | All |
Exakat since | 0.10.1 |
PHP Version | All |
Severity | Major |
Time To Fix | Slow (1 hour) |
Precision | Medium |
Available in | Entreprise Edition, Exakat Cloud |
1.2.172. Swapped Arguments¶
Overwritten methods must be compatible, but argument names is not part of that compatibility.
Methods with the same name, in two classes of the same hierarchy, must be compatible for typehint, default value, reference. The name of the argument is not taken into account when checking such compatibility, at least until PHP 7.4.
<?php
class x {
function foo($a, $b) {}
function bar($a, $b) {}
}
class y extends x {
// foo is compatible (identical) with the above class
function foo($a, $b) {}
// bar is compatible with the above class, yet, the argument might not receive what they expect.
function bar($b, $a) {}
}
?>
This analysis reports argument lists that differs in ordering. This analysis doesn’t report argument lists that also differs in argument names.
1.2.172.1. Suggestions¶
- Make sure the names of the argument are in the same order in all classes and interfaces