Adding Zero
[Since 0.8.4] - [ -P Structures/AddZero ] - [ Online docs ]
Adding 0 is useless, as 0 is the neutral element for addition. Besides, when one of the operands is an integer, PHP silently triggers a cast to integer for the other operand.
This rule also report using + with variables, proeprties, etc. which triggers an automated conversion to integer.
It is recommended to make the cast explicit with (int) .
<?php
// Explicit cast
$a = (int) foo();
// Useless addition
$a = foo() + 0;
$a = 0 + foo();
// Also works with minus
$b = 0 - $c; // drop the 0, but keep the minus
$b = $c - 0; // drop the 0 and the minus
$a += 0;
$a -= 0;
$z = '12';
print +$z + 1;
?>
Alternatives :
- Remove the +/- 0, may be the whole assignation
- Use an explicit type casting operator (int)
Ambiguous Array Index
[Since 0.8.4] - [ -P Arrays/AmbiguousKeys ] - [ Online docs ]
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).
They are indeed distinct, but may lead to confusion.
<?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;
?>
Alternatives :
- Only use string or integer as key for an array.
- Use transtyping operator (string) and (int) to make sure of the type
Array Index
[Since 0.8.4] - [ -P Arrays/Arrayindex ] - [ Online docs ]
List of all indexes used in arrays. The indexes are strings or integers. They are accessed with different syntaxes: either the square brackets, or the => operator.
<?php
// Index
$x['index'] = 1;
// in array creation
$a = array('index2' => 1);
$a2 = ['index3' => 2];
?>
Multidimensional Arrays
[Since 0.8.4] - [ -P Arrays/Multidimensional ] - [ Online docs ]
Multidimensional arrays are arrays of arrays. Each level of array is called a dimension. The number of dimensions is arbitrary, though it is recommende not to abuse it beyond 4.
<?php
$x[1][2] = $x[2][3][4];
?>
See also Type array and Using Multidimensional Arrays in PHP.
Multiple Index Definition
[Since 0.8.4] - [ -P Arrays/MultipleIdenticalKeys ] - [ Online docs ]
This rules lists the indexes that are defined multiple times in the same array.
In reality, they are overwriting each other. This is most probably a typo or a failed copy/paste.
<?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 is the index 2
2 => 4); // this index is overwritten
?>
Alternatives :
- Review the code and check that arrays only have keys defined once.
- Review carefully the code and check indirect values, like constants and static constants.
PHP Arrays Index
[Since 0.8.4] - [ -P Arrays/Phparrayindex ] - [ Online docs ]
List of indexes used when manipulating PHP arrays in the code. These indices usually carry semantic meanings, and should always be readable.
<?php
// HTTP_HOST is a PHP array index.
$ip = 'http'.$_SERVER['HTTP_HOST'].'/'.$row['path'];
//'path' is not a PHP index
?>
Classes Names
[Since 0.8.4] - [ -P Classes/Classnames ] - [ Online docs ]
List of all classes, as defined in the application.
<?php
// foo is in the list
class foo {}
// Anonymous classes are not in the list
$o = class { function foo(){} }
?>
See also class.
Constant Definition
[Since 0.8.4] - [ -P Classes/ConstantDefinition ] - [ Online docs ]
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 and PHP OOP Constants.
Empty Classes
[Since 0.8.4] - [ -P Classes/EmptyClass ] - [ Online docs ]
Classes that do no define anything at all : no property, method nor constant. This is possibly dead code.
Empty classes are sometimes used to group classes; an interface may be used here for the same purpose, without inserting an extra level in the class hierarchy.
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 {}
?>
Alternatives :
- Remove the empty class: it is possibly dead code.
- Add some code to the class to make it concrete.
- Turn the class into an interface.
Magic Methods
[Since 0.8.4] - [ -P Classes/MagicMethod ] - [ Online docs ]
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() {}
}
?>
Forgotten Visibility
[Since 0.8.4] - [ -P Classes/NonPpp ] - [ Online docs ]
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() {}
}
?>
Alternatives :
- Always add explicit visibility to methods and constants in a class
- Always add explicit visibility to properties in a class, after PHP 7.4
Non Static Methods Called In A Static
[Since 0.8.4] - [ -P Classes/NonStaticMethodsCalledStatic ] - [ Online docs ]
Static methods have to be declared as such (using the static keyword). Then, one may call them without instantiating the object.
PHP 7.0, and more recent versions, yield a deprecated error : Non-static method A::B() should not be called statically .
PHP 5 and older doesn't check that a method is static or not : at any point, the code may call one method statically.
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 {
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();
?>
Alternatives :
- Call the method the correct way
- Define the method as static
Old Style Constructor
[Since 0.8.4] - [ -P Classes/OldStyleConstructor ] - [ Online docs ]
PHP classes used to have the method bearing the same name as the class acts as the constructor. That was PHP 4, and early PHP 5.
The manual issues a warning about this syntax : Old style constructors are DEPRECATED in PHP 7.0, and will be removed in a future version. You should always use __construct() in new code.
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.
<?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
}
}
}
?>
Alternatives :
- Remove old style constructor and make it __construct()
- Remove old libraries and use a modern component
Static Methods
[Since 0.8.4] - [ -P Classes/StaticMethods ] - [ Online docs ]
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();
}
}
?>
Static Methods Called From Object
[Since 0.8.4] - [ -P Classes/StaticMethodsCalledFromObject ] - [ Online docs ]
Static methods may be called without instantiating an object. As such, they never interact with the special variable '$this', as they do not depend on object existence.
Besides this, static methods are normal methods that may be called directly from object context, to perform some utility task.
To maintain code readability, it is recommended to call static method in a static way, rather than within object context.
<?php
class x {
static function y( ) {}
}
$z = new x( );
$z->y( ); // Readability : no one knows it is a static call
x::y( ); // Readability : here we know
?>
Alternatives :
- Switch to static method syntax
- Remove the static option from the method
Static Properties
[Since 0.8.4] - [ -P Classes/StaticProperties ] - [ Online docs ]
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;
//....
}
?>
Constants Usage
[Since 0.8.4] - [ -P Constants/ConstantUsage ] - [ Online docs ]
List of constants in use in the source code. Constants are T_STRING, localised in specific part of the code.
For example, they can't be followed by a parenthesis, as this is a function call; nor preceded by a new operator, as this is an object instantiation.
<?php
const MY_CONST = 'Hello';
// PHP_EOL (native PHP Constant)
// MY_CONST (custom constant)
echo PHP_EOL . MY_CONST;
// Here, MY_CONST is actually a function name, and is omitted in this analysis
MY_CONST();
?>
Magic Constant Usage
[Since 0.8.4] - [ -P Constants/MagicConstantUsage ] - [ Online docs ]
There are eight magical constants that change depending on where they are used. For example, the value of __LINE__ depends on the line that it's used on in your script. These special constants are case-insensitive.
- __LINE__
- __FILE__
- __DIR__
- __FUNCTION__
- __CLASS__
- __TRAIT__
- __METHOD__
- __NAMESPACE__
<?php
echo 'This code is in file '__FILE__.', line '.__LINE__;
?>
See also Magic Constants and .
PHP Constant Usage
[Since 0.8.4] - [ -P Constants/PhpConstantUsage ] - [ Online docs ]
List of PHP constants being used.
<?php
const MY_CONST = 'Hello';
// PHP_EOL (native PHP Constant)
// MY_CONST (custom constant, not reported)
echo PHP_EOL . MY_CONST;
?>
See also Predefined Constants.
Defined Exceptions
[Since 0.8.4] - [ -P Exceptions/DefinedExceptions ] - [ Online docs ]
This is the list of defined exceptions. Those exceptions are custom to the code, and shall be thrown at one point or more in the code.
<?php
class myException extends \Exception {}
// A defined exception
throw new myException();
// not a defined exception : it is already defined.
throw new \RuntimeException();
?>
See also Predefined Exceptions and Exceptions.
Thrown Exceptions
[Since 0.8.4] - [ -P Exceptions/ThrownExceptions ] - [ Online docs ]
This rules reports the usage of the throw keyword. This means all these exceptions are raised at some point in the code.
<?php
throw new MyException("An error happened");
?>
See also Exceptions.
ext/apc
[Since 0.8.4] - [ -P Extensions/Extapc ] - [ Online docs ]
Extension Alternative PHP Cache.
The Alternative PHP Cache (APC) is a free and open opcode cache for PHP. Its goal is to provide a free, open, and robust framework for caching and optimizing PHP intermediate code.
This extension is considered unmaintained and dead.
<?php
$bar = 'BAR';
apc_add('foo', $bar);
var_dump(apc_fetch('foo'));
echo PHP_EOL;
$bar = 'NEVER GETS SET';
apc_add('foo', $bar);
var_dump(apc_fetch('foo'));
echo PHP_EOL;
?>
See also Alternative PHP Cache.
ext/bcmath
[Since 0.8.4] - [ -P Extensions/Extbcmath ] - [ Online docs ]
Extension BC Math.
For arbitrary precision mathematics PHP offers the Binary Calculator which supports numbers of any size and precision up to 2147483647-1 (or 0x7FFFFFFF-1 ) decimals, represented as strings.
<?php
echo bcpow('2', '123');
//10633823966279326983230456482242756608
echo 2**123;
//1.0633823966279E+37
?>
See also BC Math Functions.
ext/bzip2
[Since 0.8.4] - [ -P Extensions/Extbzip2 ] - [ Online docs ]
Extension ext/bzip2.
Bzip2 Functions for PHP.
<?php
$file = '/tmp/foo.bz2';
$bz = bzopen($file, 'r') or die('Couldn\'t open $file for reading');
bzclose($bz);
?>
See also Bzip2 Functions and bzip2.
ext/calendar
[Since 0.8.4] - [ -P Extensions/Extcalendar ] - [ Online docs ]
Extension ext/calendar.
The calendar extension presents a series of functions to simplify converting between different calendar formats.
<?php
$number = cal_days_in_month(CAL_GREGORIAN, 8, 2003); // 31
echo "There were {$number} days in August 2003";
?>
See also Calendar Functions.
ext/crypto
[Since 0.8.4] - [ -P Extensions/Extcrypto ] - [ Online docs ]
Extension ext/crypto (PECL).
Objective PHP binding of OpenSSL Crypto library.
<?php
use Crypto\Cipher;
use Crypto\AlgorihtmException;
$algorithm = 'aes-256-cbc';
if (!Cipher::hasAlgorithm($algorithm)) {
die('Algorithm $algorithm not found' . PHP_EOL);
}
try {
$cipher = new Cipher($algorithm);
// Algorithm method for retrieving algorithm
echo 'Algorithm: ' . $cipher->getAlgorithmName() . PHP_EOL;
// Params
$key_len = $cipher->getKeyLength();
$iv_len = $cipher->getIVLength();
echo 'Key length: ' . $key_len . PHP_EOL;
echo 'IV length: ' . $iv_len . PHP_EOL;
echo 'Block size: ' . $cipher->getBlockSize() . PHP_EOL;
// This is just for this example. You should never use such key and IV!
$key = str_repeat('x', $key_len);
$iv = str_repeat('i', $iv_len);
// Test data
$data1 = 'Test';
$data2 = 'Data';
$data = $data1 . $data2;
// Simple encryption
$sim_ct = $cipher->encrypt($data, $key, $iv);
// init/update/finish encryption
$cipher->encryptInit($key, $iv);
$iuf_ct = $cipher->encryptUpdate($data1);
$iuf_ct .= $cipher->encryptUpdate($data2);
$iuf_ct .= $cipher->encryptFinish();
// Raw data output (used base64 format for printing)
echo 'Ciphertext (sim): ' . base64_encode($sim_ct) . PHP_EOL;
echo 'Ciphertext (iuf): ' . base64_encode($iuf_ct) . PHP_EOL;
// $iuf_out == $sim_out
$ct = $sim_ct;
// Another way how to create a new cipher object (using the same algorithm and mode)
$cipher = Cipher::aes(Cipher::MODE_CBC, 256);
// Simple decryption
$sim_text = $cipher->decrypt($ct, $key, $iv);
// init/update/finish decryption
$cipher->decryptInit($key, $iv);
$iuf_text = $cipher->decryptUpdate($ct);
$iuf_text .= $cipher->decryptFinish();
// Raw data output ($iuf_out == $sim_out)
echo 'Text (sim): ' . $sim_text . PHP_EOL;
echo 'Text (iuf): ' . $iuf_text . PHP_EOL;
}
catch (AlgorithmException $e) {
echo $e->getMessage() . PHP_EOL;
}
?>
See also pecl crypto and php-crypto.
ext/ctype
[Since 0.8.4] - [ -P Extensions/Extctype ] - [ Online docs ]
Extension ext/ctype.
Ext/ctype checks whether a character or string falls into a certain character class according to the current locale.
<?php
$strings = array('AbCd1zyZ9', 'foo!#$bar');
foreach ($strings as $testcase) {
if (ctype_alnum($testcase)) {
echo "The string $testcase consists of all letters or digits.\n";
} else {
echo "The string $testcase does not consist of all letters or digits.\n";
}
}
?>
See also Ctype functions.
ext/curl
[Since 0.8.4] - [ -P Extensions/Extcurl ] - [ Online docs ]
Extension curl.
PHP supports libcurl, a library created by Daniel Stenberg. It allows the connection and communication to many different types of servers with many different types of protocols.
<?php
$ch = curl_init("http://www.example.com/");
$fp = fopen("example_homepage.txt", "w");
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
fclose($fp);
?>
See also Curl for PHP and curl.
ext/date
[Since 0.8.4] - [ -P Extensions/Extdate ] - [ Online docs ]
Extension ext/date.
These functions allows the manipulation of date and time from the server where the PHP scripts are running.
<?php
$dt = new DateTime('2015-11-01 00:00:00', new DateTimeZone('America/New_York'));
echo 'Start: ', $dt->format('Y-m-d H:i:s P'), PHP_EOL;
$dt->add(new DateInterval('PT3H'));
echo 'End: ', $dt->format('Y-m-d H:i:s P'), PHP_EOL;
?>
See also Date and Time.
ext/dba
[Since 0.8.4] - [ -P Extensions/Extdba ] - [ Online docs ]
Extension ext/dba.
These functions build the foundation for accessing Berkeley DB style databases.
<?php
$id = dba_open('/tmp/test.db', 'n', 'db2');
if (!$id) {
echo 'dba_open failed'.PHP_EOL;
exit;
}
dba_replace('key', 'This is an example!', $id);
if (dba_exists('key', $id)) {
echo dba_fetch('key', $id);
dba_delete('key', $id);
}
dba_close($id);
?>
See also Database (dbm-style) Abstraction Layer.
ext/dom
[Since 0.8.4] - [ -P Extensions/Extdom ] - [ Online docs ]
Extension Document Object Model.
The DOM extension allows the manipulation of XML documents through the DOM API with PHP.
<?php
$dom = new DOMDocument('1.0', 'utf-8');
$element = $dom->createElement('test', 'This is the root element!');
// We insert the new element as root (child of the document)
$dom->appendChild($element);
echo $dom->saveXML();
?>
See also Document Object Model.
ext/enchant
[Since 0.8.4] - [ -P Extensions/Extenchant ] - [ Online docs ]
Extension Enchant.
Enchant is the PHP binding for the Enchant spelling library. Enchant steps in to provide uniformity and conformity on top of all spelling libraries, and implement certain features that may be lacking in any individual provider library.
<?php
$tag = 'en_US';
$r = enchant_broker_init();
$bprovides = enchant_broker_describe($r);
echo 'Current broker provides the following backend(s):'.PHP_EOL;
print_r($bprovides);
$dicts = enchant_broker_list_dicts($r);
print_r($dicts);
if (enchant_broker_dict_exists($r,$tag)) {
$d = enchant_broker_request_dict($r, $tag);
$dprovides = enchant_dict_describe($d);
echo 'dictionary $tag provides:'.PHP_EOL;
$wordcorrect = enchant_dict_check($d, 'soong');
print_r($dprovides);
if (!$wordcorrect) {
$suggs = enchant_dict_suggest($d, 'soong');
echo 'Suggestions for "soong":';
print_r($suggs);
}
enchant_broker_free_dict($d);
} else {
}
enchant_broker_free($r);
?>
See also Enchant spelling library and Enchant.
ext/exif
[Since 0.8.4] - [ -P Extensions/Extexif ] - [ Online docs ]
Extension EXIF : Exchangeable image file format.
The EXIF extension manipulates image meta data.
<?php
echo 'test1.jpg:<br />';
$exif = exif_read_data('tests/test1.jpg', 'IFD0');
echo $exif===false ? 'No header data found.<br />' : 'Image contains headers<br />';
$exif = exif_read_data('tests/test2.jpg', 0, true);
echo 'test2.jpg:<br />';
foreach ($exif as $key => $section) {
foreach ($section as $name => $val) {
echo $key.$name.': '.$val.'<br />';
}
}
?>
See also Exchangeable image information.
ext/fileinfo
[Since 0.8.4] - [ -P Extensions/Extfileinfo ] - [ Online docs ]
Extension ext/fileinfo.
This module guesses the content type and encoding of a file by looking for certain magic byte sequences at specific positions within the file.
<?php
$finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
foreach (glob('*') as $filename) {
echo finfo_file($finfo, $filename) . PHP_EOL;
}
finfo_close($finfo);
?>
See also Filinfo.
ext/filter
[Since 0.8.4] - [ -P Extensions/Extfilter ] - [ Online docs ]
Extension filter.
This extension filters data by either validating or sanitizing it.
<?php
$email_a = 'joe@example.com';
$email_b = 'bogus';
if (filter_var($email_a, FILTER_VALIDATE_EMAIL)) {
echo 'This ($email_a) email address is considered valid.'.PHP_EOL;
}
if (filter_var($email_b, FILTER_VALIDATE_EMAIL)) {
echo 'This ($email_b) email address is considered valid.'.PHP_EOL;
} else {
echo 'This ($email_b) email address is considered invalid.'.PHP_EOL;
}
?>
See also Data filtering.
ext/ftp
[Since 0.8.4] - [ -P Extensions/Extftp ] - [ Online docs ]
Extension FTP.
The functions in this extension implement client access to files servers speaking the File Transfer Protocol (FTP) as defined in RFC 959.
<?php
// set up basic connection
$conn_id = ftp_connect($ftp_server);
// login with username and password
$login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
// check connection
if ((!$conn_id) || (!$login_result)) {
echo 'FTP connection has failed!';
echo 'Attempted to connect to $ftp_server for user $ftp_user_name';
exit;
} else {
echo 'Connected to $ftp_server, for user $ftp_user_name';
}
// upload the file
$upload = ftp_put($conn_id, $destination_file, $source_file, FTP_BINARY);
// check upload status
if (!$upload) {
echo 'FTP upload has failed!';
} else {
echo 'Uploaded $source_file to $ftp_server as $destination_file';
}
// close the FTP stream
ftp_close($conn_id);
?>
See also FTP.
ext/gd
[Since 0.8.4] - [ -P Extensions/Extgd ] - [ Online docs ]
Extension GD for PHP.
This extension allows PHP to create and manipulate image files in a variety of different image formats, including GIF, PNG, JPEG, WBMP, and XPM.
<?php
header("Content-type: image/png");
$string = $_GET['text'];
$im = imagecreatefrompng("images/button1.png");
$orange = imagecolorallocate($im, 220, 210, 60);
$px = (imagesx($im) - 7.5 * strlen($string)) / 2;
imagestring($im, 3, $px, 9, $string, $orange);
imagepng($im);
imagedestroy($im);
?>
See also Image Processing and GD.
ext/gmp
[Since 0.8.4] - [ -P Extensions/Extgmp ] - [ Online docs ]
Extension ext/gmp.
These functions allow for arbitrary-length integers to be worked with using the GNU MP library.
<?php
$pow1 = gmp_pow('2', 131);
echo gmp_strval($pow1) . PHP_EOL;
$pow2 = gmp_pow('0', 0);
echo gmp_strval($pow2) . PHP_EOL;
$pow3 = gmp_pow('2', -1); // Negative exp, generates warning
echo gmp_strval($pow3) . PHP_EOL;
?>
See also GMP and GNU MP library.
ext/gnupgp
[Since 0.8.4] - [ -P Extensions/Extgnupg ] - [ Online docs ]
Extension GnuPG.
This module allows you to interact with gnupg.
<?php
// init gnupg
$res = gnupg_init();
// not really needed. Clearsign is default
gnupg_setsignmode($res,GNUPG_SIG_MODE_CLEAR);
// add key with passphrase 'test' for signing
gnupg_addsignkey($res,"8660281B6051D071D94B5B230549F9DC851566DC","test");
// sign
$signed = gnupg_sign($res,"just a test");
echo $signed;
?>
See also Gnupg Function for PHP and GnuPG.
ext/hash
[Since 0.8.4] - [ -P Extensions/Exthash ] - [ Online docs ]
Extension for HASH Message Digest Framework.
Message Digest (hash) engine. Allows direct or incremental processing of arbitrary length messages using a variety of hashing algorithms.
<?php
/* Create a file to calculate hash of */
file_put_contents('example.txt', 'The quick brown fox jumped over the lazy dog.');
echo hash_file('md5', 'example.txt');
?>
See also HASH Message Digest Framework.
ext/iconv
[Since 0.8.4] - [ -P Extensions/Exticonv ] - [ Online docs ]
Extension ext/iconv.
With this module, you can turn a string represented by a local character set into the one represented by another character set, which may be the Unicode character set.
<?php
$text = "This is the Euro symbol '€'.";
echo 'Original : ', $text, PHP_EOL;
echo 'TRANSLIT : ', iconv("UTF-8", "ISO-8859-1//TRANSLIT", $text), PHP_EOL;
echo 'IGNORE : ', iconv("UTF-8", "ISO-8859-1//IGNORE", $text), PHP_EOL;
echo 'Plain : ', iconv("UTF-8", "ISO-8859-1", $text), PHP_EOL;
?>
See also Iconv and libiconv.
ext/json
[Since 0.8.4] - [ -P Extensions/Extjson ] - [ Online docs ]
Extension JSON.
This extension implements the JavaScript Object Notation (JSON) data-interchange format. PHP implements a superset of JSON as specified in the original RFC 7159.
<?php
$arr = array('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5);
echo json_encode($arr);
?>
See also JavaScript Object Notation and JSON.
ext/ldap
[Since 0.8.4] - [ -P Extensions/Extldap ] - [ Online docs ]
Extension ext/ldap.
LDAP is the Lightweight Directory Access Protocol, and is a protocol used to access 'Directory Servers'. The Directory is a special kind of database that holds information in a tree structure.
<?php
// basic sequence with LDAP is connect, bind, search, interpret search
// result, close connection
echo '<h3>LDAP query test</h3>';
echo 'Connecting ...';
$ds=ldap_connect('localhost'); // must be a valid LDAP server!
echo 'connect result is ' . $ds . '<br />';
if ($ds) {
echo 'Binding ...';
$r=ldap_bind($ds); // this is an 'anonymous' bind, typically
// read-only access
echo 'Bind result is ' . $r . '<br />';
echo 'Searching for (sn=S*) ...';
// Search surname entry
$sr=ldap_search($ds, 'o=My Company, c=US', 'sn=S*');
echo 'Search result is ' . $sr . '<br />';
echo 'Number of entries returned is ' . ldap_count_entries($ds, $sr) . '<br />';
echo 'Getting entries ...<p>';
$info = ldap_get_entries($ds, $sr);
echo 'Data for ' . $info['count'] . ' items returned:<p>';
for ($i=0; $i<$info['count']; $i++) {
echo 'dn is: ' . $info[$i]['dn'] . '<br />';
echo 'first cn entry is: ' . $info[$i]['cn'][0] . '<br />';
echo 'first email entry is: ' . $info[$i]['mail'][0] . '<br /><hr />';
}
echo 'Closing connection';
ldap_close($ds);
} else {
echo '<h4>Unable to connect to LDAP server</h4>';
}
?>
See also Lightweight Directory Access Protocol.
ext/libxml
[Since 0.8.4] - [ -P Extensions/Extlibxml ] - [ Online docs ]
Extension libxml.
These functions/constants are available as of PHP 5.1.0, and the following core extensions rely on this libxml extension: DOM, libxml, SimpleXML, SOAP, WDDX, XSL, XML, XMLReader, XMLRPC and XMLWriter.
<?php
// $xmlstr is a string, containing a XML document.
$doc = simplexml_load_string($xmlstr);
$xml = explode(PHP_EOL, $xmlstr);
if ($doc === false) {
$errors = libxml_get_errors();
foreach ($errors as $error) {
echo display_xml_error($error, $xml);
}
libxml_clear_errors();
}
function display_xml_error($error, $xml)
{
$return = $xml[$error->line - 1] . PHP_EOL;
$return .= str_repeat('-', $error->column) . '^'.PHP_EOL;
switch ($error->level) {
case LIBXML_ERR_WARNING:
$return .= 'Warning ',$error->code.': ';
break;
case LIBXML_ERR_ERROR:
$return .= 'Error '.$error->code.': ';
break;
case LIBXML_ERR_FATAL:
$return .= 'Fatal Error '.$error->code.': ';
break;
}
$return .= trim($error->message) .
PHP_EOL.' Line: '.$error->line .
PHP_EOL.' Column: '.$error->column;
if ($error->file) {
$return .= "\n File: $error->file";
}
return $return.PHP_EOL.PHP_EOL.'--------------------------------------------'.PHP_EOL.PHP_EOL;
}
?>
See also libxml.
ext/mbstring
[Since 0.8.4] - [ -P Extensions/Extmbstring ] - [ Online docs ]
Extension ext/mbstring .
mbstring provides multibyte specific string functions that help you deal with multibyte encodings in PHP.
<?php
/* Convert internal character encoding to SJIS */
$str = mb_convert_encoding($str, "SJIS");
/* Convert EUC-JP to UTF-7 */
$str = mb_convert_encoding($str, "UTF-7", "EUC-JP");
/* Auto detect encoding from JIS, eucjp-win, sjis-win, then convert str to UCS-2LE */
$str = mb_convert_encoding($str, "UCS-2LE", "JIS, eucjp-win, sjis-win");
/* "auto" is expanded to "ASCII,JIS,UTF-8,EUC-JP,SJIS" */
$str = mb_convert_encoding($str, "EUC-JP", "auto");
?>
See also Mbstring.
ext/mcrypt
[Since 0.8.4] - [ -P Extensions/Extmcrypt ] - [ Online docs ]
Extension for mcrypt.
This extension has been deprecated as of PHP 7.1.0 and moved to PECL as of PHP 7.2.0.
This is an interface to the mcrypt library, which supports a wide variety of block algorithms such as DES, TripleDES, Blowfish (default), 3-WAY, SAFER-SK64, SAFER-SK128, TWOFISH, TEA, RC2 and GOST in CBC, OFB, CFB and ECB cipher modes. Additionally, it supports RC6 and IDEA which are considered 'non-free'. CFB/OFB are 8bit by default.
<?php
# --- ENCRYPTION ---
# the key should be random binary, use scrypt, bcrypt or PBKDF2 to
# convert a string into a key
# key is specified using hexadecimal
$key = pack('H*', 'bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3');
# show key size use either 16, 24 or 32 byte keys for AES-128, 192
# and 256 respectively
$key_size = strlen($key);
echo 'Key size: ' . $key_size . PHP_EOL;
$plaintext = 'This string was AES-256 / CBC / ZeroBytePadding encrypted.';
# create a random IV to use with CBC encoding
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
# creates a cipher text compatible with AES (Rijndael block size = 128)
# to keep the text confidential
# only suitable for encoded input that never ends with value 00h
# (because of default zero padding)
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,
$plaintext, MCRYPT_MODE_CBC, $iv);
# prepend the IV for it to be available for decryption
$ciphertext = $iv . $ciphertext;
# encode the resulting cipher text so it can be represented by a string
$ciphertext_base64 = base64_encode($ciphertext);
echo $ciphertext_base64 . PHP_EOL;
# === WARNING ===
# Resulting cipher text has no integrity or authenticity added
# and is not protected against padding oracle attacks.
# --- DECRYPTION ---
$ciphertext_dec = base64_decode($ciphertext_base64);
# retrieves the IV, iv_size should be created using mcrypt_get_iv_size()
$iv_dec = substr($ciphertext_dec, 0, $iv_size);
# retrieves the cipher text (everything except the $iv_size in the front)
$ciphertext_dec = substr($ciphertext_dec, $iv_size);
# may remove 00h valued characters from end of plain text
$plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key,
$ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);
echo $plaintext_dec . PHP_EOL;
?>
See also extension mcrypt and mcrypt.
ext/mongo
[Since 0.8.4] - [ -P Extensions/Extmongo ] - [ Online docs ]
Extension MongoDB driver (legacy).
Note : this is not the MongoDB driver. This is the legacy extension.
<?php
// connect
$m = new MongoClient();
// select a database
$db = $m->comedy;
// select a collection (analogous to a relational database\'s table)
$collection = $db->cartoons;
// add a record
$document = array( 'title' => 'Calvin and Hobbes', 'author' => 'Bill Watterson' );
$collection->insert($document);
// add another record, with a different 'shape'
$document = array( 'title' => 'XKCD', 'online' => true );
$collection->insert($document);
// find everything in the collection
$cursor = $collection->find();
// iterate through the results
foreach ($cursor as $document) {
echo $document['title'] . PHP_EOL;
}
?>
See also ext/mongo manual and MongdDb.
ext/mssql
[Since 0.8.4] - [ -P Extensions/Extmssql ] - [ Online docs ]
Extension MSSQL, Microsoft SQL Server.
These functions allow you to access MS SQL Server database.
<?php
// Connect to MSSQL
$link = mssql_connect('KALLESPC\SQLEXPRESS', 'sa', 'phpfi');
if (!$link || !mssql_select_db('php', $link)) {
die('Unable to connect or select database!');
}
// Do a simple query, select the version of
// MSSQL and print it.
$version = mssql_query('SELECT @@VERSION');
$row = mssql_fetch_array($version);
echo $row[0];
// Clean up
mssql_free_result($version);
?>
See also Microsoft SQL Server and Microsoft PHP Driver for SQL Server.
ext/mysql
[Since 0.8.4] - [ -P Extensions/Extmysql ] - [ Online docs ]
Extension for MySQL (Original MySQL API).
This extension is deprecated as of PHP 5.5.0, and has been removed as of PHP 7.0.0. Instead, either the mysqli or PDO_MySQL extension should be used.
See also the MySQL API Overview for further help while choosing a MySQL API.
<?php
$result = mysql_query('SELECT * WHERE 1=1');
if (!$result) {
die('Invalid query: ' . mysql_error());
}
?>
See also Original MySQL API and MySQL.
ext/mysqli
[Since 0.8.4] - [ -P Extensions/Extmysqli ] - [ Online docs ]
Extension mysqli for MySQL.
The mysqli extension allows you to access the functionality provided by MySQL 4.1 and above.
<?php
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'world');
/* check connection */
if (mysqli_connect_errno()) {
printf('Connect failed: %s\n', mysqli_connect_error());
exit();
}
$city = 'Amersfoort';
/* create a prepared statement */
if ($stmt = $mysqli->prepare('SELECT District FROM City WHERE Name=?')) {
/* bind parameters for markers */
$stmt->bind_param('s', $city);
/* execute query */
$stmt->execute();
/* bind result variables */
$stmt->bind_result($district);
/* fetch value */
$stmt->fetch();
printf('%s is in district %s\n', $city, $district);
/* close statement */
$stmt->close();
}
/* close connection */
$mysqli->close();
?>
See also MySQL Improved Extension, MySQL and Mariadb.
ext/odbc
[Since 0.8.4] - [ -P Extensions/Extodbc ] - [ Online docs ]
Extension ODBC.
In addition to normal ODBC support, the Unified ODBC functions in PHP allow you to access several databases that have borrowed the semantics of the ODBC API to implement their own API. Instead of maintaining multiple database drivers that were all nearly identical, these drivers have been unified into a single set of ODBC functions.
<?php
$a = 1;
$b = 2;
$c = 3;
$stmt = odbc_prepare($conn, 'CALL myproc(?,?,?)');
$success = odbc_execute($stmt, array($a, $b, $c));
?>
See also ODBC (Unified), Unixodbc and IODBC.
ext/openssl
[Since 0.8.4] - [ -P Extensions/Extopenssl ] - [ Online docs ]
Extension Openssl.
This extension binds functions of OpenSSL library for symmetric and asymmetric encryption and decryption, PBKDF2 , PKCS7 , PKCS12 , X509 and other cryptographic operations. In addition to that it provides implementation of TLS streams.
<?php
// $data and $signature are assumed to contain the data and the signature
// fetch public key from certificate and ready it
$pubkeyid = openssl_pkey_get_public("file://src/openssl-0.9.6/demos/sign/cert.pem");
// state whether signature is okay or not
$ok = openssl_verify($data, $signature, $pubkeyid);
if ($ok == 1) {
echo "good";
} elseif ($ok == 0) {
echo "bad";
} else {
echo "ugly, error checking signature";
}
// free the key from memory
openssl_free_key($pubkeyid);
?>
See also ext/OpenSSL and OpenSSL.
ext/pcre
[Since 0.8.4] - [ -P Extensions/Extpcre ] - [ Online docs ]
Extension ext/pcre. PCRE stands for Perl Compatible Regular Expression. It is a standard PHP extension.
<?php
$zip_code = $_GET['zip'];
// Canadian Zip code H2M 3J1
$zip_ca = '/^([a-zA-Z]\d[a-zA-Z])\ {0,1}(\d[a-zA-Z]\d)$/';
// French Zip code 75017
$zip_fr = '/^\d{5}$/';
// Chinese Zip code 590615
$zip_cn = '/^\d{6}$/';
var_dump(preg_match($_GET['zip']));
?>
See also Regular Expressions (Perl-Compatible).
ext/pdo
[Since 0.8.4] - [ -P Extensions/Extpdo ] - [ Online docs ]
Generic extension PDO.
The PHP Data Objects (PDO) extension defines a lightweight, consistent interface for accessing databases in PHP.
<?php
/* Execute a prepared statement by passing an array of values */
$sql = 'SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour';
$sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':calories' => 150, ':colour' => 'red'));
$red = $sth->fetchAll();
$sth->execute(array(':calories' => 175, ':colour' => 'yellow'));
$yellow = $sth->fetchAll();
?>
See also PHP Data Object.
ext/pgsql
[Since 0.8.4] - [ -P Extensions/Extpgsql ] - [ Online docs ]
Extension PostGreSQL.
PostgreSQL is an open source descendant of this original Berkeley code. It provides SQL92/SQL99 language support, transactions, referential integrity, stored procedures and type extensibility.
<?php
// Connect to a database named 'mary'
$dbconn = pg_connect('dbname=mary');
// Prepare a query for execution
$result = pg_prepare($dbconn, 'my_query', 'SELECT * FROM shops WHERE name = $1');
// Execute the prepared query. Note that it is not necessary to escape
// the string 'Joe's Widgets' in any way
$result = pg_execute($dbconn, 'my_query', array('Joe\'s Widgets'));
// Execute the same prepared query, this time with a different parameter
$result = pg_execute($dbconn, 'my_query', array('Clothes Clothes Clothes'));
?>
See also PostgreSQL and PostgreSQL: The world's most advanced open source database.
ext/phar
[Since 0.8.4] - [ -P Extensions/Extphar ] - [ Online docs ]
Extension phar.
The phar extension provides a way to put entire PHP applications into a single file called a phar (PHP Archive) for easy distribution and installation.
<?php
try {
$p = new Phar('/path/to/my.phar', 0, 'my.phar');
$p['myfile.txt'] = 'hi';
$file = $p['myfile.txt'];
var_dump($file->isCompressed(Phar::BZ2));
$p['myfile.txt']->compress(Phar::BZ2);
var_dump($file->isCompressed(Phar::BZ2));
} catch (Exception $e) {
echo 'Create/modify operations on my.phar failed: ', $e;
}
?>
See also phar.
ext/posix
[Since 0.8.4] - [ -P Extensions/Extposix ] - [ Online docs ]
Extension POSIX.
Ext/posix contains an interface to those functions defined in the IEEE 1003.1 (POSIX.1) standards document which are not accessible through other means.
<?php
posix_kill(999459,SIGKILL);
echo 'Your error returned was '.posix_get_last_error(); //Your error was ___
?>
See also 1003.1-2008 - IEEE Standard for Information Technology - Portable Operating System Interface (POSIX(R)).
ext/readline
[Since 0.8.4] - [ -P Extensions/Extreadline ] - [ Online docs ]
Extension readline.
The readline functions implement an interface to the GNU Readline library. These are functions that provide editable command lines.
<?php
//get 3 commands from user
for ($i=0; $i < 3; $i++) {
$line = readline("Command: ");
readline_add_history($line);
}
//dump history
print_r(readline_list_history());
//dump variables
print_r(readline_info());
?>
See also ext/readline and The GNU Readline Library.
ext/reflection
[Since 0.8.4] - [ -P Extensions/Extreflection ] - [ Online docs ]
Extension Reflection.
PHP comes with a complete reflection API that adds the ability to reverse-engineer classes, interfaces, functions, methods and extensions. Additionally, the reflection API offers ways to retrieve doc comments for functions, classes and methods.
<?php
/**
* A simple counter
*
* @return int
*/
function counter1()
{
static $c = 0;
return ++$c;
}
/**
* Another simple counter
*
* @return int
*/
$counter2 = function()
{
static $d = 0;
return ++$d;
};
function dumpReflectionFunction($func)
{
// Print out basic information
printf(
PHP_EOL.'===> The %s function '%s''.PHP_EOL.
' declared in %s'.PHP_EOL.
' lines %d to %d'.PHP_EOL,
$func->isInternal() ? 'internal' : 'user-defined',
$func->getName(),
$func->getFileName(),
$func->getStartLine(),
$func->getEndline()
);
// Print documentation comment
printf('---> Documentation:'.PHP_EOL.' %s',PHP_EOL, var_export($func->getDocComment(), 1));
// Print static variables if existant
if ($statics = $func->getStaticVariables())
{
printf('---> Static variables: %s',PHP_EOL, var_export($statics, 1));
}
}
// Create an instance of the ReflectionFunction class
dumpReflectionFunction(new ReflectionFunction('counter1'));
dumpReflectionFunction(new ReflectionFunction($counter2));
?>
See also Reflection.
ext/sem
[Since 0.8.4] - [ -P Extensions/Extsem ] - [ Online docs ]
Extension Semaphore, Shared Memory and IPC.
This module provides wrappers for the System V IPC family of functions. It includes semaphores, shared memory and inter-process messaging (IPC).
<?php
$key = ftok(__FILE__,'a');
$semaphore = sem_get($key);
sem_acquire($semaphore);
sem_release($semaphore);
sem_remove($semaphore);
?>
See also Semaphore, Shared Memory and IPC.
ext/session
[Since 0.8.4] - [ -P Extensions/Extsession ] - [ Online docs ]
Extension ext/session.
Session support in PHP consists of a way to preserve certain data across subsequent accesses.
<?php
session_start();
if (!isset($_SESSION['count'])) {
$_SESSION['count'] = 0;
} else {
$_SESSION['count']++;
}
?>
See also Session.
ext/shmop
[Since 0.8.4] - [ -P Extensions/Extshmop ] - [ Online docs ]
Extension ext/shmop.
Shmop is an easy to use set of functions that allows PHP to read, write, create and delete Unix shared memory segments.
<?php
// Create a temporary file and return its path
$tmp = tempnam('/tmp', 'PHP');
// Get the file token key
$key = ftok($tmp, 'a');
// Attach the SHM resource, notice the cast afterwards
$id = shm_attach($key);
if ($id === false) {
die('Unable to create the shared memory segment');
}
// Cast to integer, since prior to PHP 5.3.0 the resource id
// is returned which can be exposed when casting a resource
// to an integer
$id = (integer) $id;
?>
See also Semaphore, Shared Memory and IPC.
ext/simplexml
[Since 0.8.4] - [ -P Extensions/Extsimplexml ] - [ Online docs ]
Extension SimpleXML .
The SimpleXML extension provides a very simple and easily usable toolset to convert XML to an object that can be processed with normal property selectors and array iterators.
<?php
$xml = <<<'XML'
<?xml version='1.0' standalone='yes' ? >
<movies>
<movie>
<title>PHP: Behind the Parser</title>
<characters>
<character>
<name>Ms. Coder</name>
<actor>Onlivia Actora</actor>
</character>
<character>
<name>Mr. Coder</name>
<actor>El ActÓr</actor>
</character>
</characters>
<plot>
So, this language. It's like, a programming language. Or is it a
scripting language? All is revealed in this thrilling horror spoof
of a documentary.
</plot>
<great-lines>
<line>PHP solves all my web problems</line>
</great-lines>
<rating type="thumbs">7</rating>
<rating type="stars">5</rating>
</movie>
</movies>
XML;
$movies = new SimpleXMLElement($xml);
echo $movies->movie[0]->plot;
?>
See also SimpleXML.
ext/snmp
[Since 0.8.4] - [ -P Extensions/Extsnmp ] - [ Online docs ]
Extension SNMP.
The SNMP extension provides a very simple and easily usable toolset for managing remote devices via the Simple Network Management Protocol.
<?php
$nameOfSecondInterface = snmp3_get('localhost', 'james', 'authPriv', 'SHA', 'secret007', 'AES', 'secret007', 'IF-MIB::ifName.2');
?>
See also Net SNMP and SNMP.
ext/soap
[Since 0.8.4] - [ -P Extensions/Extsoap ] - [ Online docs ]
Extension SOAP.
The SOAP extension can be used to write SOAP Servers and Clients. It supports subsets of » SOAP 1.1, » SOAP 1.2 and » WSDL 1.1 specifications.
<?php
$client = new SoapClient("some.wsdl");
$client = new SoapClient("some.wsdl", array('soap_version' => SOAP_1_2));
$client = new SoapClient("some.wsdl", array('login' => "some_name",
'password' => "some_password"));
?>
See also SOAP and SOAP specifications.
ext/sockets
[Since 0.8.4] - [ -P Extensions/Extsockets ] - [ Online docs ]
Extension socket.
The socket extension implements a low-level interface to the socket communication functions based on the popular BSD sockets, providing the possibility to act as a socket server as well as a client.
<?php
//Example #2 Socket example: Simple TCP/IP client
//From the PHP manual
error_reporting(E_ALL);
echo "<h2>TCP/IP Connection</h2>\n";
/* Get the port for the WWW service. */
$service_port = getservbyname('www', 'tcp');
/* Get the IP address for the target host. */
$address = gethostbyname('www.example.com');
/* Create a TCP/IP socket. */
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
echo 'socket_create() failed: reason: ' . socket_strerror(socket_last_error()) . PHP_EOL;
} else {
echo 'OK.'.PHP_EOL;
}
echo 'Attempting to connect to '$address' on port '$service_port'...';
$result = socket_connect($socket, $address, $service_port);
if ($result === false) {
echo 'socket_connect() failed.\nReason: ($result) ' . socket_strerror(socket_last_error($socket)) . '\n';
} else {
echo 'OK.'.PHP_EOL;
}
$in = "HEAD / HTTP/1.1\r\n";
$in .= "Host: www.example.com\r\n";
$in .= "Connection: Close\r\n\r\n";
$out = '';
echo 'Sending HTTP HEAD request...';
socket_write($socket, $in, strlen($in));
echo "OK.\n";
echo 'Reading response:\n\n';
while ($out = socket_read($socket, 2048)) {
echo $out;
}
echo 'Closing socket...';
socket_close($socket);
echo 'OK.\n\n';
?>
See also Sockets.
ext/spl
[Since 0.8.4] - [ -P Extensions/Extspl ] - [ Online docs ]
SPL extension.
The Standard PHP Library (SPL) is a collection of interfaces and classes that are meant to solve common problems.
<?php
// Example with FilesystemIterator
$files = new FilesystemIterator('/path/to/dir');
foreach($files as $file) {
echo $file->getFilename() . PHP_EOL;
}
?>
See also Standard PHP Library (SPL).
ext/sqlite
[Since 0.11.3] - [ -P Extensions/Extsqlite ] - [ Online docs ]
Extension Sqlite 2.
Support for SQLite version 2 databases. The support for this version of Sqlite has ended. It is recommended to use SQLite3 .
<?php
if ($db = sqlite_open('mysqlitedb', 0666, $sqliteerror)) {
sqlite_query($db, 'CREATE TABLE foo (bar varchar(10))');
sqlite_query($db, 'INSERT INTO foo VALUES ("fnord")');
$result = sqlite_query($db, 'select bar from foo');
var_dump(sqlite_fetch_array($result));
} else {
die($sqliteerror);
}
?>
See also ext/sqlite and SQLite.
ext/sqlite3
[Since 0.8.4] - [ -P Extensions/Extsqlite3 ] - [ Online docs ]
Extension Sqlite3.
This extension adds support for SQLite version 3 databases. There used to be a Sqlite2 extension, which have been discontinued: this is the replacement.
<?php
$db = new SQLite3('mysqlitedb.db');
$results = $db->query('SELECT bar FROM foo');
while ($row = $results->fetchArray()) {
var_dump($row);
}
?>
See also ext/sqlite3 and Sqlite.
ext/ssh2
[Since 0.8.4] - [ -P Extensions/Extssh2 ] - [ Online docs ]
Extension ext/ssh2.
<?php
/* Notify the user if the server terminates the connection */
function my_ssh_disconnect($reason, $message, $language) {
printf("Server disconnected with reason code [%d] and message: %s\n",
$reason, $message);
}
$methods = array(
'kex' => 'diffie-hellman-group1-sha1',
'client_to_server' => array(
'crypt' => '3des-cbc',
'comp' => 'none'),
'server_to_client' => array(
'crypt' => 'aes256-cbc,aes192-cbc,aes128-cbc',
'comp' => 'none'));
$callbacks = array('disconnect' => 'my_ssh_disconnect');
$connection = ssh2_connect('shell.example.com', 22, $methods, $callbacks);
if (!$connection) die('Connection failed');
?>
See also SSH2 functions and ext/ssh2 on PECL.
ext/standard
[Since 0.8.4] - [ -P Extensions/Extstandard ] - [ Online docs ]
Standards PHP functions.
This is not a real PHP extension : it covers the core functions.
<?php
/*
Our php.ini contains the following settings:
display_errors = On
register_globals = Off
post_max_size = 8M
*/
echo 'display_errors = ' . ini_get('display_errors') . PHP_EOL;
echo 'register_globals = ' . ini_get('register_globals') . PHP_EOL;
echo 'post_max_size = ' . ini_get('post_max_size') . PHP_EOL;
echo 'post_max_size+1 = ' . (ini_get('post_max_size')+1) . PHP_EOL;
echo 'post_max_size in bytes = ' . return_bytes(ini_get('post_max_size'));
function return_bytes($val) {
$val = trim($val);
$last = strtolower($val[strlen($val)-1]);
switch($last) {
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}
return $val;
}
?>
See also PHP Options/Info Functions.
ext/tidy
[Since 0.8.4] - [ -P Extensions/Exttidy ] - [ Online docs ]
Extension Tidy.
Tidy is a binding for the Tidy HTML clean and repair utility which allows you to not only clean and otherwise manipulate HTML documents, but also traverse the document tree.
a html document
<?php
ob_start();
?>
See also Tidy and HTML-tidy.
ext/tokenizer
[Since 0.8.4] - [ -P Extensions/Exttokenizer ] - [ Online docs ]
Extension Tokenizer.
The Tokenizer functions provide an interface to the PHP tokenizer embedded in the Zend Engine.
<?php
/*
* T_ML_COMMENT does not exist in PHP 5.
* The following three lines define it in order to
* preserve backwards compatibility.
*
* The next two lines define the PHP 5 only T_DOC_COMMENT,
* which we will mask as T_ML_COMMENT for PHP 4.
*/
if (!defined('T_ML_COMMENT')) {
define('T_ML_COMMENT', T_COMMENT);
} else {
define('T_DOC_COMMENT', T_ML_COMMENT);
}
$source = file_get_contents('example.php');
$tokens = token_get_all($source);
foreach ($tokens as $token) {
if (is_string($token)) {
// simple 1-character token
echo $token;
} else {
// token array
list($id, $text) = $token;
switch ($id) {
case T_COMMENT:
case T_ML_COMMENT: // we\'ve defined this
case T_DOC_COMMENT: // and this
// no action on comments
break;
default:
// anything else -> output 'as is'
echo $text;
break;
}
}
}
?>
See also tokenizer.
ext/wddx
[Since 0.8.4] - [ -P Extensions/Extwddx ] - [ Online docs ]
Extension WDDX.
The Web Distributed Data Exchange, or WDDX, is a free, open XML-based technology that allows Web applications created with any platform to easily exchange data with one another over the Web.
<?php
echo wddx_serialize_value("PHP to WDDX packet example", "PHP packet");
?>
See also Wddx on PHP and WDDX.
ext/xdebug
[Since 0.8.4] - [ -P Extensions/Extxdebug ] - [ Online docs ]
Xdebug extension.
The Xdebug is a extension PHP which provides debugging and profiling capabilities.
<?php
class Strings
{
static function fix_string($a)
{
echo
xdebug_call_class().
"::".
xdebug_call_function().
" is called at ".
xdebug_call_file().
":".
xdebug_call_line();
}
}
$ret = Strings::fix_string( 'Derick' );
?>
See also Xdebug.
ext/xmlreader
[Since 0.8.4] - [ -P Extensions/Extxmlreader ] - [ Online docs ]
Extension XMLReader.
The XMLReader extension is an XML Pull parser. The reader acts as a cursor going forward on the document stream and stopping at each node on the way.
<?php
$xmlreader = new XMLReader();
$xmlreader->xml("<xml><div>Content</div></xml>");
$xmlreader->read();
$xmlreader->read();
$xmlreader->readString();
?>
See also xmlreader.
ext/xmlrpc
[Since 0.8.4] - [ -P Extensions/Extxmlrpc ] - [ Online docs ]
Extension ext/xmlrpc.
This extension can be used to write XML-RPC servers and clients.
<?php
$request = xmlrpc_encode_request('method', array(1, 2, 3));
$context = stream_context_create(array('http' => array(
'method' => 'POST',
'header' => 'Content-Type: text/xml',
'content' => $request
)));
$file = file_get_contents('http://www.example.com/xmlrpc', false, $context);
$response = xmlrpc_decode($file);
if ($response && xmlrpc_is_fault($response)) {
trigger_error('xmlrpc: '.$response['faultString'].' ('.$response['faultCode']));
} else {
print_r($response);
}
?>
See also XML-RPC.
ext/xmlwriter
[Since 0.8.4] - [ -P Extensions/Extxmlwriter ] - [ Online docs ]
Extension ext/xmlwriter.
The XMLWriter extension wraps the libxml xmlWriter API inside PHP.
<?php
$xw = xmlwriter_open_memory();
xmlwriter_set_indent($xw, TRUE);
xmlwriter_start_document($xw, NULL, 'UTF-8');
xmlwriter_start_element($xw, 'root');
xmlwriter_write_attribute_ns($xw, 'prefix', '', 'http://www.php.net/uri');
xmlwriter_start_element($xw, 'elem1');
xmlwriter_write_attribute($xw, 'attr1', 'first');
xmlwriter_end_element($xw);
xmlwriter_full_end_element($xw);
xmlwriter_end_document($xw);
$output = xmlwriter_flush($xw, true);
print $output;
// write attribute_ns without start_element first
$xw = xmlwriter_open_memory();
var_dump(xmlwriter_write_attribute_ns($xw, 'prefix', 'id', 'http://www.php.net/uri', 'elem1'));
print xmlwriter_output_memory($xw);
?>
See also XMLWriter and Module xmlwriter from libxml2.
ext/xsl
[Since 0.8.4] - [ -P Extensions/Extxsl ] - [ Online docs ]
Extension XSL.
The XSL extension implements the XSL standard, performing XSLT transformations using the libxslt library.
<?php
// Example from the PHP manual
$xmldoc = new DOMDocument();
$xsldoc = new DOMDocument();
$xsl = new XSLTProcessor();
$xmldoc->loadXML('fruits.xml');
$xsldoc->loadXML('fruits.xsl');
libxml_use_internal_errors(true);
$result = $xsl->importStyleSheet($xsldoc);
if (!$result) {
foreach (libxml_get_errors() as $error) {
echo "Libxml error: {$error->message}\n";
}
}
libxml_use_internal_errors(false);
if ($result) {
echo $xsl->transformToXML($xmldoc);
}
?>
See also XSL extension.
ext/yaml
[Since 0.8.4] - [ -P Extensions/Extyaml ] - [ Online docs ]
Extension YAML.
This extension implements the YAML Ain't Markup Language (YAML) data serialization standard. Parsing and emitting are handled by the LibYAML library.
<?php
$addr = array(
'given' => 'Chris',
'family'=> 'Dumars',
'address'=> array(
'lines'=> '458 Walkman Dr.
Suite #292',
'city'=> 'Royal Oak',
'state'=> 'MI',
'postal'=> 48046,
),
);
$invoice = array (
'invoice'=> 34843,
'date'=> '2001-01-23',
'bill-to'=> $addr,
'ship-to'=> $addr,
'product'=> array(
array(
'sku'=> 'BL394D',
'quantity'=> 4,
'description'=> 'Basketball',
'price'=> 450,
),
array(
'sku'=> 'BL4438H',
'quantity'=> 1,
'description'=> 'Super Hoop',
'price'=> 2392,
),
),
'tax'=> 251.42,
'total'=> 4443.52,
'comments'=> 'Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.',
);
// generate a YAML representation of the invoice
$yaml = yaml_emit($invoice);
var_dump($yaml);
// convert the YAML back into a PHP variable
$parsed = yaml_parse($yaml);
// check that roundtrip conversion produced an equivalent structure
var_dump($parsed == $invoice);
?>
See also YAML.
ext/zip
[Since 0.8.4] - [ -P Extensions/Extzip ] - [ Online docs ]
Extension ext/zip.
This extension enables you to transparently read or write ZIP compressed archives and the files inside them.
<?php
$zip = new ZipArchive();
$filename = './test112.zip';
if ($zip->open($filename, ZipArchive::CREATE)!==TRUE) {
exit('cannot open <$filename>');
}
$zip->addFromString('testfilephp.txt' . time(), '#1 This is a test string added as testfilephp.txt.'.PHP_EOL);
$zip->addFromString('testfilephp2.txt' . time(), '#2 This is a test string added as testfilephp2.txt.'.PHP_EOL);
$zip->addFile($thisdir . '/too.php','/testfromfile.php');
echo 'numfiles: ' . $zip->numFiles . PHP_EOL;
echo 'status:' . $zip->status . PHP_EOL;
$zip->close();
?>
See also Zip.
ext/zlib
[Since 0.8.4] - [ -P Extensions/Extzlib ] - [ Online docs ]
Extension ext/zlib.
<?php
$filename = tempnam('/tmp', 'zlibtest') . '.gz';
echo "<html>\n<head></head>\n<body>\n<pre>\n";
$s = "Only a test, test, test, test, test, test, test, test!\n";
// open file for writing with maximum compression
$zp = gzopen($filename, 'w9');
// write string to file
gzwrite($zp, $s);
// close file
gzclose($zp);
?>
See also Zlib.
Closures Glossary
[Since 0.8.4] - [ -P Functions/Closures ] - [ Online docs ]
List of all the closures in the code.
<?php
// A closure is also a unnamed function
$closure = function ($arg) { return 'A'.strtolower($arg); }
?>
See also The Closure Class.
Empty Function
[Since 0.8.4] - [ -P Functions/EmptyFunction ] - [ Online docs ]
Function or method whose body is empty.
Such functions or methods are rarely useful. As a bare minimum, the function should return some useful value, even if constant.
A method is considered empty when it contains nothing, or contains expressions without impact.
Methods which overwrite another methods are omitted. Methods which are the concrete version of an abstract method are considered.
<?php
// classic empty function
function emptyFunction() {}
class bar {
// classic empty method
function emptyMethod() {}
// classic empty function
function emptyMethodWithParent() {}
}
class barbar extends bar {
// NOT an empty method : it overwrites the parent method
function emptyMethodWithParent() {}
}
?>
Alternatives :
- Fill the function with actual code
- Remove any usage of the function, then remove the function
Functions Glossary
[Since 0.8.4] - [ -P Functions/Functionnames ] - [ Online docs ]
List of all the defined functions in the code.
<?php
// A function
function aFunction() {}
// Closures (not reported)
$closure = function ($arg) { }
// Methods
class foo {
function aMethod() {}
}
?>
Recursive Functions
[Since 0.8.4] - [ -P Functions/Recursive ] - [ Online docs ]
Recursive methods are methods that calls itself.
Usually, the method call itself directly. In rarer occasions, the method calls another method which calls it back; such cycle are longer and not detected here.
Functions, methods, arrow functions and closures are identified as recursive. Higher level of recursion are not detected (function a() calls function b(), calls function a(), etc.).
Functions are easy to identify as recursive. Methods have some blind spots : when the injected argument is of the same class, it may lead to recursion too. On the other hand, calling the same method on a property is not sufficient, as the property might not be $this .
<?php
// a recursive function ; it calls itself
function factorial($n) {
if ($n == 1) { return 1; }
return factorial($n - 1) * $n;
}
?>
Redeclared PHP Functions
[Since 0.8.4] - [ -P Functions/RedeclaredPhpFunction ] - [ Online docs ]
Function that bear the same name as a PHP function, and that are declared.
This is useful when managing backward compatibility, like emulating an old function, or preparing for newer PHP versions, like emulating new upcoming function.
<?php
if (version_compare(PHP_VERSION, 7.0) > 0) {
function split($separator, $string) {
return explode($separator, $string);
}
}
print_r( split(' ', '2 3'));
?>
Alternatives :
- Check if it is still worth emulating that function
Typehints
[Since 0.8.4] - [ -P Functions/Typehints ] - [ Online docs ]
List of all the types (classes or scalar) used in Typehinting.
<?php
// here, array, myObject and string are all typehints.
function foo(array $array, myObject $x, string $string) {
}
?>
See also Type declarations.
Methods Without Return
[Since 0.8.4] - [ -P Functions/WithoutReturn ] - [ Online docs ]
List of all the functions, closures, methods that have no explicit return.
Functions with the void or never return types, are omitted.
<?php
// With return null : Explicitly not returning
function withExplicitReturn($a = 1) {
$a++;
return null;
}
// Without indication
function withoutExplicitReturn($a = 1) {
$a++;
}
// With return type void : Explicitly not returning
function withExplicitReturnType($a = 1) : void {
$a++;
}
?>
Alternatives :
- Add the returntype 'void' to make this explicit behavior
Empty Interfaces
[Since 0.8.4] - [ -P Interfaces/EmptyInterface ] - [ Online docs ]
Empty interfaces are a code smell. Interfaces should contains at least a method or a constant, and not be totally empty.
<?php
// an empty interface
interface empty {}
// an normal interface
interface normal {
public function i() ;
}
// a constants interface
interface constantsOnly {
const FOO = 1;
}
?>
Alternatives :
- Remove the interface
- Add some methods or constants to the interface
Interfaces Names
[Since 0.8.4] - [ -P Interfaces/Interfacenames ] - [ Online docs ]
List of all the defined interfaces in the code.
<?php
// interfaceName is reported
interface interfaceName {
function interfaceMethod() ;
}
?>
Aliases
[Since 0.8.4] - [ -P Namespaces/Alias ] - [ Online docs ]
This rule lists all aliases. Aliases are used file by file, although some classes may have different aliases depending on the context.
<?php
// This is an alias
use stdClass as aClass;
// This is not an alias : it is not explicit
use stdClass;
trait t {
// This is not an alias, it's a trait usage
use otherTrait;
}
?>
See also Using namespaces: Aliasing/Importing and A Complete Guide to PHP Namespaces.
Namespaces Glossary
[Since 0.8.4] - [ -P Namespaces/Namespacesnames ] - [ Online docs ]
List of all the defined namespaces in the code, using the namespace keyword.
Global namespaces are mentioned when they are explicitly used.
<?php
// One reported namespace
namespace one\name\space {}
// This global namespace is reported, as it is explicit
namespace { }
?>
Autoloading
[Since 0.8.4] - [ -P Php/AutoloadUsage ] - [ Online docs ]
Usage of the autoloading feature of PHP.
Defining the __autoload() function is obsolete since PHP 7.2.
<?php
spl_autoload_register('my_autoloader');
// Old way to autoload. Deprecated in PHP 7.2
function __autoload($class ) {}
?>
See also __autoload.
Goto Names
[Since 0.8.4] - [ -P Php/Gotonames ] - [ Online docs ]
This rule lists of all goto labels used in the code. The labels must match a goto call, although it is possible to create a label without a goto.
<?php
GOTO_NAME_1:
// reports the usage of GOTO_NAME_1
goto GOTO_NAME_1;
UNUSED_GOTO_NAME_1:
?>
See also goto.
__halt_compiler
[Since 0.8.4] - [ -P Php/Haltcompiler ] - [ Online docs ]
__halt_compiler() usage.
<?php
// open this file
$fp = fopen(__FILE__, 'r');
// seek file pointer to data
fseek($fp, __COMPILER_HALT_OFFSET__);
// and output it
var_dump(stream_get_contents($fp));
// the end of the script execution
__halt_compiler(); the installation data (eg. tar, gz, PHP, etc.)
?>
See also __halt_compiler.
Incompilable Files
[Since 0.8.4] - [ -P Php/Incompilable ] - [ Online docs ]
Files that cannot be compiled, and, as such, be run by PHP. Scripts are linted against various versions of PHP.
This is usually undesirable, as all code must compile before being executed. It may be that such files are not compilable because they are not yet ready for an upcoming PHP version.
Code that is not compilable with older PHP versions means that the code is breaking backward compatibility : good or bad is project decision.
When the code is used as a template for PHP code generation, for example at installation time, it is recommended to use a distinct file extension, so as to distinguish them from actual PHP code.
<?php
// Can't compile this : Print only accepts one argument
print $a, $b, $c;
?>
Alternatives :
- If this file is a template for PHP code, change the extension to something else than .php
- Fix the syntax so it works with various versions of PHP
Labels
[Since 0.8.4] - [ -P Php/Labelnames ] - [ Online docs ]
List of all labels used in the code.
<?php
// A is label.
goto A:
A:
// A label may be used by several gotos.
goto A:
?>
Throw
[Since 0.8.4] - [ -P Php/ThrowUsage ] - [ Online docs ]
List of thrown exceptions.
<?php
if ($divisor === 0) {
// Throw native exception
throw new DivisionByZeroError("Shouldn't divide by one");
}
if ($divisor === 1) {
// Throw custom exception
throw new DontDivideByOneException("Shouldn't divide by one");
}
?>
See also Exceptions.
Trigger Errors
[Since 0.8.4] - [ -P Php/TriggerErrorUsage ] - [ Online docs ]
List of situations where user errors are triggered.
PHP errors are triggered with trigger_error().
<?php
if ($divisor == 0) {
trigger_error('Cannot divide by zero', E_USER_ERROR);
}
?>
See also trigger_error.
Caught Expressions
[Since 0.8.4] - [ -P Php/TryCatchUsage ] - [ Online docs ]
This rule lists all the caught exceptions.
Exceptions may be caught by a code, while the same code never throw them.
<?php
// This analyzer reports MyException and Exception
try {
doSomething();
} catch (MyException $e) {
fixIt();
} catch (\Exception $e) {
fixIt();
}
?>
See also Exceptions.
error_reporting() With Integers
[Since 0.8.4] - [ -P Structures/ErrorReportingWithInteger ] - [ Online docs ]
Using named constants with error_reporting is strongly encouraged to ensure compatibility for future versions. As error levels are added, the range of integers increases, so older integer-based error levels will not always behave as expected. (Adapted from the documentation).
<?php
// This is ready for PHP next version
error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE & ~E_WARNING);
// This is not ready for PHP next version
error_reporting(2047);
// -1 and 0 are omitted, as they will be valid even is constants changes.
error_reporting(-1);
error_reporting(0);
?>
Alternatives :
- Always use the constant combination when configuring error_reporting or any PHP native function
Eval() Usage
[Since 0.8.4] - [ -P Structures/EvalUsage ] - [ Online docs ]
Using eval() is evil.
Using eval() is bad for performances (compilation time), for caches (it won't be compiled), and for security (if it includes external data).
Most of the time, it is possible to replace the code by some standard PHP, like variable variable for accessing a variable for which you have the name.
At worse, including a pregenerated file is faster and cacheable.
There are several situations where eval() is actually the only solution :
For PHP 7.0 and later, it is important to put eval() in a try..catch expression.
<?php
// Avoid using incoming data to build the eval() expression : any filtering error leads to PHP injection
$mathExpression = $_GET['mathExpression'];
$mathExpression = preg_replace('#[^0-9+-*/(/)]#is', '', $mathExpression); // expecting 1+2
$literalCode = '$a = '.$mathExpression.';';
eval($literalCode);
echo $a;
// If the code code given to eval() is known at compile time, it is best to put it inline
$literalCode = 'phpinfo();';
eval($literalCode);
?>
Alternatives :
- Use a dynamic feature of PHP to replace the dynamic code
- Store the code on the disk, and use include
- Replace create_function() with a closure!
Exit() Usage
[Since 0.8.4] - [ -P Structures/ExitUsage ] - [ Online docs ]
Using exit or die() in the code makes the code untestable (it will break unit tests). Moreover, if there is no reason or string to display, it may take a long time to spot where the application is stuck.
Try exiting the function/class with return, or throw exception that may be caught later in the code.
<?php
// Throw an exception, that may be caught somewhere
throw new Exception('error');
// Dying with error message.
die('error');
function foo() {
//exiting the function but not dying
if (somethingWrong()) {
return true;
}
}
?>
Alternatives :
- Avoid exit and die. Let the script finish.
- Throw an exception and let it be handled before finishing
Forgotten Whitespace
[Since 0.8.4] - [ -P Structures/ForgottenWhiteSpace ] - [ Online docs ]
Forgotten whitespaces brings unexpected error messages.
White spaces have been left at either end of a file : before the PHP opening tag, or after the closing tag.
Usually, such whitespaces are forgotten, and may end up summoning the infamous 'headers already sent' error. It is better to remove them.
<?php
// This script has no forgotten whitespace, not at the beginning
function foo() {}
// This script has no forgotten whitespace, not at the end
?>
Alternatives :
- Remove all whitespaces before and after a script. This doesn't apply to template, which may need to use those spaces.
- Remove the final tag, to prevent any whitespace to be forgotten at the end of the file. This doesn't apply to the opening PHP tag, which is always necessary.
Iffectations
[Since 0.8.4] - [ -P Structures/Iffectation ] - [ Online docs ]
Affectations that appears in a condition.
Iffectations are a way to do both a test and an affectations.
They may also be typos, such as if ($x = 3) { ... }, leading to a constant condition.
<?php
// an iffectation : assignation in a If condition
if($connexion = mysql_connect($host, $user, $pass)) {
$res = mysql_query($connexion, $query);
}
// Iffectation may happen in while too.
while($row = mysql_fetch($res)) {
$store[] = $row;
}
?>
Alternatives :
- Move the assignation inside the loop, and make an existence test in the condition.
- Move the assignation before the if/then, make an existence test in the condition.
Multiply By One
[Since 0.8.4] - [ -P Structures/MultiplyByOne ] - [ Online docs ]
Multiplying by 1 is a fancy type cast.
If it is used to type cast a value to number, then casting (int) or (float) is clearer. This behavior may change with PHP 7.1, which has unified the behavior of all hidden casts.
<?php
// Still the same value than $m, but now cast to integer or float
$m = $m * 1;
// Still the same value than $m, but now cast to integer or float
$n *= 1;
// make typecasting clear, and merge it with the producing call.
$n = (int) $n;
?>
Alternatives :
- Typecast to (int) or (float) for better readability
- Skip useless math operation altogether
@ Operator
[Since 0.8.4] - [ -P Structures/Noscream ] - [ Online docs ]
@ is the 'no scream' operator : it suppresses error output.
This operator is very slow : it processes the error, and finally decides not to display it. It is often faster to check the conditions first, then run the method without @ .
You may also set display_error to 0 in the php.ini : this avoids user's error display, and keeps the error in the PHP logs, for later processing.
The only situation where @ is useful is when a native PHP function displays errors messages and there is no way to check it from the code beforehand.
This was the case with fopen(), stream_socket_server(), token_get_all(). As of PHP 7.0, they are all hiding errors when @ is active.
<?php
// Set x with incoming value, or else null.
$x = @$_GET['x'];
?>
Alternatives :
- Remove the @ operator by default
Not Not
[Since 0.8.4] - [ -P Structures/NotNot ] - [ Online docs ]
Double not makes a boolean, not a true .
This is a wrong casting to boolean. PHP supports (boolean) to do the same, faster and cleaner.
<?php
// Explicit code
$b = (boolean) $x;
$b = (bool) $x;
// Wrong type casting
$b = !!$x;
?>
Alternatives :
- Use (bool) casting operator for that
- Don't typecast, and let PHP handle it. This works in situations where the boolean is immediately used.
include_once() Usage
[Since 0.8.4] - [ -P Structures/OnceUsage ] - [ Online docs ]
Usage of include_once() and require_once(). Those functions should be avoided for performances reasons.
Try using autoload for loading classes, or use include() or require() and make it possible to include several times the same file without errors.
<?php
// Including a library.
include 'lib/helpers.inc';
// Including a library, and avoiding double inclusion
include_once 'lib/helpers.inc';
?>
Alternatives :
- Avoid using include_once() whenever possible
- Use autoload() to load classes, and avoid loading them with include
Using Short Tags
[Since 0.8.4] - [ -P Structures/ShortTags ] - [ Online docs ]
The code makes use of short tags. Short tags are the following : . A full scripts looks like that : /* php code */ ?> . It is recommended to avoid using short tags, and use standard PHP tags. This makes PHP code compatible with XML standards. Short tags used to be popular, but have lost it.
- Use full tags
Strpos()-like Comparison
[Since 0.8.4] - [ -P Structures/StrposCompare ] - [ Online docs ]
The result of that function may be mistaken with an error.
strpos(), along with several PHP native functions, returns a string position, starting at 0, or false, in case of failure.
It is recommended to check the result of strpos() with === or !==, so as to avoid confusing 0 and false.
This analyzer list all the strpos()-like functions that are directly compared with == or !=. preg_match(), when its first argument is a literal, is omitted : this function only returns NULL in case of regex error.
The full list is the following :
- array_search()
- collator_compare()
- collator_get_sort_key()
- current()
- fgetc()
- file_get_contents()
- file_put_contents()
- fread()
- iconv_strpos()
- iconv_strrpos()
- imagecolorallocate()
- imagecolorallocatealpha()
- mb_strlen()
- next()
- pcntl_getpriority()
- preg_match()
- prev()
- readdir()
- stripos()
- strpos()
- strripos()
- strrpos()
- strtok()
- curl_exec()
In PHP 8.0, str_contains() will do the expected job of strpos(), with less confusion.
<?php
// This is the best comparison
if (strpos($string, 'a') === false) { }
// This is OK, as 2 won't be mistaken with false
if (strpos($string, 'a') == 2) { }
// strpos is one of the 26 functions that may behave this way
if (preg_match($regex, $string)) { }
// This works like above, catching the value for later reuse
if ($a = strpos($string, 'a')) { }
// This misses the case where 'a' is the first char of the string
if (strpos($string, 'a')) { }
// This misses the case where 'a' is the first char of the string, just like above
if (strpos($string, 'a') == 0) { }
?>
Alternatives :
- Use identity comparisons, for 0 values : === instead of ==, etc.
- Compare with other exact values than 0 : strpos() == 2
- Use str_contains()
Throws An Assignement
[Since 0.8.4] - [ -P Structures/ThrowsAndAssign ] - [ Online docs ]
It is possible to throw an exception, and, in the same time, assign this exception to a variable.
However, the variable will never be used, as the exception is thrown, and any following code is not executed, unless the exception is caught in the same scope.
<?php
// $e is useful, though not by much
$e = new() Exception();
throw $e;
// $e is useless
throw $e = new Exception();
?>
Alternatives :
- Drop the assignation
var_dump()... Usage
[Since 0.8.4] - [ -P Structures/VardumpUsage ] - [ Online docs ]
var_dump(), print_r() or var_export() should not be left in any production code. They are debugging functions.
They may be tolerated during development time, but must be removed so as not to have any chance to be run in production.
<?php
if ($error) {
// Debugging usage of var_dump
// And major security problem
var_dump($query);
// This is OK : the $query is logged, and not displayed
$this->log(print_r($query, true));
}
?>
Alternatives :
- Remove usage of var_dump(), print_r(), var_export() without second argument, and other debug functions.
- Push all logging to an external file, instead of the browser.
__toString() Throws Exception
[Since 0.8.4] - [ -P Structures/toStringThrowsException ] - [ Online docs ]
Magical method __toString() can't throw exceptions.
In fact, __toString() may not let an exception pass. If it throw an exception, but must catch it. If an underlying method throws an exception, it must be caught.
A fatal error is displayed, when an exception is not intercepted in the __toString() function.
<?php
class myString {
private $string = null;
public function __construct($string) {
$this->string = $string;
}
public function __toString() {
// Do not throw exceptions in __toString
if (!is_string($this->string)) {
throw new Exception("$this->string is not a string!!");
}
return $this->string;
}
}
?>
Alternatives :
- Remove any usage of exception from __toString() magic method
Binary Glossary
[Since 0.8.4] - [ -P Type/Binary ] - [ Online docs ]
List of all the integer values using the binary format.
<?php
$a = 0b10;
$b = 0B0101;
?>
See also Integer syntax and Mastering binary and bitwise in PHP.
Email Addresses
[Since 0.8.4] - [ -P Type/Email ] - [ Online docs ]
List of all the email addresses that were found in the code.
Emails are detected with regex : [_A-Za-z0-9-]+(\.[_A-Za-z0-9-]+)*@[A-Za-z0-9]+(\.[A-Za-z0-9]+)*(\.[A-Za-z]{2,})
<?php
$email = 'contact@exakat.io';
?>
Heredoc Delimiter Glossary
[Since 0.8.4] - [ -P Type/Heredoc ] - [ Online docs ]
List of all the delimiters used to build a Heredoc string.
In the example below, EOD is the delimiter.
<?php
$a = <<<EOD
heredoc
EOD;
?>
See also Heredoc.
Hexadecimal Glossary
[Since 0.8.4] - [ -P Type/Hexadecimal ] - [ Online docs ]
List of all the integer values, written in the hexadecimal format.
<?php
$hexadecimal = 0x10;
$anotherHexadecimal =0XAF;
?>
See also Integer Syntax.
Md5 Strings
[Since 0.8.4] - [ -P Type/Md5String ] - [ Online docs ]
List of all the MD5 values hard coded in the application.
MD5 values are detected as hexadecimal strings, of length 32. No attempt at recognizing the origin value is made, so any such strings, including dummy '11111111111111111111111111111111' are reported.
<?php
// 32
$a = '0cc175b9c0f1b6a831c399e269771111';
?>
See also MD5.
Nowdoc Delimiter Glossary
[Since 0.8.4] - [ -P Type/Nowdoc ] - [ Online docs ]
List of all the delimiters used to build a Nowdoc string.
<?php
$nowdoc = <<<'EOD'
EOD;
?>
See also Nowdoc and Heredoc.
Octal Glossary
[Since 0.8.4] - [ -P Type/Octal ] - [ Online docs ]
List of all the integer values using the octal format : an integer starting with an initial 0.
Putting an initial 0 is often innocuous, but in PHP, 0755 and 755 are not the same. The second is actually 1363 in octal, and will not provide the expected privileges.
<?php
$a = 1234; // decimal number
$a = 0123; // octal number (equivalent to 83 decimal)
// silently valid for PHP 5.x
$a = 01283; // octal number (equivalent to 10 decimal)
?>
See also Integers.
URL List
[Since 0.8.4] - [ -P Type/Url ] - [ Online docs ]
List of all the URL addresses that were found in the code.
<?php
// the first argument is recognized as an URL
ftp_connect('http://www.example.com/', $port, $timeout);
// the string argument is recognized as an URL
$source = 'https://www.other-example.com/';
?>
See also Uniform Resource Identifier.
Variable References
[Since 0.8.4] - [ -P Variables/References ] - [ Online docs ]
Variables that are holding references.
References are created with =& operators, and later propagated with the same operators, or via reference-arguments.
<?php
$a = '1'; // not a reference
$b = &$a; // a reference
?>
See also References.
Static Variables
[Since 0.8.4] - [ -P Variables/StaticVariables ] - [ Online docs ]
In PHP, variables may be static. They will survive after the function execution end, and will be available at the next function run. They are distinct from globals, which are available application wide, and from static properties, which are tied to a class of objects.
<?php
function foo() {
// static variable
static $count = 0;
echo ++$count;
}
class bar {
// This is not a static variable :
// it is a static property
static $property = 1;
}
?>
See also Using static variables.
Variables With Long Names
[Since 0.8.4] - [ -P Variables/VariableLong ] - [ Online docs ]
This analysis collects all variables with more than 20 characters longs. This may be configured with the variableLength parameter.
PHP has not limitation on variable name size. While short name are often obscure, long names are usually better. Yet, there exists a limit to convenient variable name length.
<?php
// Quite a long variable name
$There_is nothing_wrong_with_long_variable_names_They_tend_to_be_rare_and_that_make_them_noteworthy = 1;
?>
Alternatives :
- Try to use short variable names.
Non Ascii Variables
[Since 0.8.4] - [ -P Variables/VariableNonascii ] - [ Online docs ]
PHP allows certain characters in variable names. The variable name must only include letters, figures, underscores and ASCII characters from 128 to 255.
In practice, letters outside the scope of the intervalle [a-zA-Z0-9_] are rare, and require more care when editing the code or passing it from OS to OS.
Also, certain letter might appear similar to the roman ones, and be part of a different alphabet. This is the case, for example, of the cyrillic alphabet, where `А` (cyrillic A, U+0410) is actually different from `A` (Latin A, U+0041). Some dashes and spaces may be valid in PHP variable names, and look very confusing.
<?php
// person, in Simplified Chinese
class 人 {
// An actual working class in PHP.
public function __construct() {
echo __CLASS__;
}
}
// people = new person();
$人民 = new 人();
?>
Alternatives :
- Make sure those special chars have actual meaning.
PHP Variables
[Since 0.8.4] - [ -P Variables/VariablePhp ] - [ Online docs ]
This is the list of PHP predefined variables that are used in the application.
The web variables ( $_GET , $_COOKIE , $_FILES ) are quite commonly used, though sometimes replaced by some special accessors. Others are rarely used.
<?php
// Reading an incoming email, with sanitation
$email = filter_var($_GET['email'], FILTER_SANITIZE_EMAIL);
?>
See also Predefined Variables.
Used Once Variables
[Since 0.8.4] - [ -P Variables/VariableUsedOnce ] - [ Online docs ]
This is the list of used once variables.
Such variables are useless. Variables must be used at least twice : once for writing, once for reading, at least. It is recommended to remove them.
In special situations, variables may be used once :
- PHP predefined variables, as they are already initialized. They are omitted in this analyze.
- Interface function's arguments, since the function has no body; They are omitted in this analyze.
- Dynamically created variables ($$x, ${$this->y} or also using extract), as they are runtime values and can't be determined at static code time. They are reported for manual review.
- Dynamically included files will provide in-scope extra variables.
This rule counts variables at the application level, and not at a method scope level.
<?php
// The variables below never appear again in the code
$writtenOnce = 1;
foo($readOnce);
?>
Alternatives :
- Remove the variable
- Fix the name of variable
- Use the variable a second time, at least
Variable Variables
[Since 0.8.4] - [ -P Variables/VariableVariables ] - [ Online docs ]
A variable variable takes the value of a variable and treats that as the name of a variable.
PHP has the ability to dynamically use a variable.
They are also called 'dynamic variable'.
<?php
// Normal variable
$a = 'b';
$b = 'c';
// Variable variable
$d = $$b;
// Variable variable in string
$d = "$\{$b\}";
?>
See also Variable variables.
Abstract Class Usage
[Since 0.8.4] - [ -P Classes/Abstractclass ] - [ Online docs ]
This rule lists of all abstract classes defined. Abstract classes cannot be instanciated, and must be extended to be 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.
Abstract Methods Usage
[Since 0.8.4] - [ -P Classes/Abstractmethods ] - [ Online docs ]
This rule lists of all abstract methods. This methods serves as template for child classes to implement.
<?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.
Clone Usage
[Since 0.8.4] - [ -P Classes/CloningUsage ] - [ Online docs ]
This rule lists of all clone expressions. Cloning objects leads to creating a new object without calling the constructor, but rather the __clone method, when available.
<?php
$dateTime = new DateTime();
echo (clone $dateTime)->format('Y');
?>
See also Object cloning.
Bad Constants Names
[Since 0.8.4] - [ -P Constants/BadConstantnames ] - [ Online docs ]
PHP's manual recommends that developer do not use constants with the convention __NAME__ . Those are reserved for PHP future use.
For example, __TRAIT__ recently appeared in PHP, as a magic constant. In the future, other may appear.
The analyzer will report any constant which name is __.*.__ , or even _.*_ (only one underscore).
<?php
const __MY_APP_CONST__ = 1;
const __MY_APP_CONST__ = 1;
define('__MY_OTHER_APP_CONST__', 2);
?>
Alternatives :
- Avoid using names that doesn't comply with PHP's convention
Variable Constants
[Since 0.8.4] - [ -P Constants/VariableConstant ] - [ Online docs ]
Variable constants are constants whose value is accessed via the function constant(). Otherwise, there is no way to dynamically access a constant (aka, when the developer has the name of the constant as a incoming parameter, and it requires the value of it).
<?php
const A = 'constant_value';
$constant_name = 'A';
$variableConstant = constant($constant_name);
?>
See also `constant() Empty Traits
[Since 0.8.4] - [ -P Traits/EmptyTrait ] - [ Online docs ]
List of all empty trait defined in the code.
Such traits may be reserved for future use. They may also be forgotten, and dead code.
<?php
// empty trait
trait t { }
// Another empty trait
trait t2 {
use t;
}
?>
Alternatives :
- Add some code to the trait
- Remove the trait
Redefined PHP Traits
[Since 0.8.4] - [ -P Traits/Php ] - [ Online docs ]
List of all traits that bears name of a PHP trait. Although, at the moment (PHP 8.1), there are no PHP trait defined.
Traits Usage
[Since 0.8.4] - [ -P Traits/TraitUsage ] - [ Online docs ]
This is the list of traits that are actually 'used' in the code. There are classes or traits that 'use' them. Traits can only be accessed by calling them with the 'use' command. It is not possible to reach a trait element (method, constant, property) by refering to them with the trait name, even for static elements: the code must go through the host class.
<?php
trait t {
function t() {
echo 'I\'m in t';
}
}
class foo {
use t;
}
$x = new foo();
$x->t();
?>
See also Traits.
Trait Names
[Since 0.8.4] - [ -P Traits/Traitnames ] - [ Online docs ]
List all the trait's names.
<?php
// This trait is called 't'
trait t {}
?>
See also Traits.
PHP Alternative Syntax
[Since 0.8.4] - [ -P Php/AlternativeSyntax ] - [ Online docs ]
This rule identifies the usage of alternative syntax in the code, for if then, switch, while, for and foreach.
Alternative syntax is another way to write the same expression. Alternative syntax is less popular than the normal one, and associated with older coding practices.
<?php
// Normal syntax
if ($a == 1) {
print $a;
}
// Alternative syntax : identical to the previous one.
if ($a == 1) :
print $a;
endif;
?>
See also Alternative syntax.
Short Syntax For Arrays
[Since 0.8.4] - [ -P Arrays/ArrayNSUsage ] - [ Online docs ]
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.
Inclusions
[Since 0.8.4] - [ -P Structures/IncludeUsage ] - [ Online docs ]
List of all inclusions. Inclusions are made with include(), include_once(), require() and require_once().
<?php
include 'library.php';
// display is a function defined in 'library.php';
display('Message');
?>
See also Include and Require.
ext/file
[Since 0.8.4] - [ -P Extensions/Extfile ] - [ Online docs ]
Filesystem functions from standard.
Extension that handle access to file on the file system.
<?php
$row = 1;
if (($handle = fopen('test.csv', 'r')) !== FALSE) {
while (($data = fgetcsv($handle, 1000, ',')) !== FALSE) {
$num = count($data);
echo '<p> $num fields in line $row: <br /></p>'.PHP_EOL;
$row++;
for ($c=0; $c < $num; $c++) {
echo $data[$c] . '<br />'.PHP_EOL;
}
}
fclose($handle);
}
?>
See also filesystem.
Use With Fully Qualified Name
[Since 0.8.4] - [ -P Namespaces/UseWithFullyQualifiedNS ] - [ Online docs ]
Use statement doesn't require a fully qualified name.
PHP manual recommends not to use fully qualified name (starting with \) when using the 'use' statement : they are "the leading backslash is unnecessary and not recommended, as import names must be fully qualified, and are not processed relative to the current namespace".
<?php
// Recommended way to write a use statement.
use A\B\C\D as E;
// No need to use the initial \
use \A\B\C\D as F;
?>
Alternatives :
- Remove the initial \ in use expressions.
ext/array
[Since 0.8.4] - [ -P Extensions/Extarray ] - [ Online docs ]
Core functions processing arrays.
These functions manipulate arrays in various ways. Arrays are essential for storing, managing, and operating on sets of variables.
This is not a real extension : it is a documentation section, that helps classifying the functions.
<?php
function odd($var)
{
// returns whether the input integer is odd
return($var & 1);
}
function even($var)
{
// returns whether the input integer is even
return(!($var & 1));
}
$array1 = array('a'=>1, 'b'=>2, 'c'=>3, 'd'=>4, 'e'=>5);
$array2 = array(6, 7, 8, 9, 10, 11, 12);
echo 'Odd :'.PHP_EOL;
print_r(array_filter($array1, 'odd'));
echo 'Even:'.PHP_EOL;
print_r(array_filter($array2, 'even'));
?>
See also Arrays.
ext/info
[Since 0.8.4] - [ -P Extensions/Extinfo ] - [ Online docs ]
PHP Options and Information.
These functions enable you to get a lot of information about PHP itself, e.g. runtime configuration, loaded extensions, version and much more.
<?php
/*
Our php.ini contains the following settings:
display_errors = On
register_globals = Off
post_max_size = 8M
*/
echo 'display_errors = ' . ini_get('display_errors') . "\n";
echo 'register_globals = ' . ini_get('register_globals') . "\n";
echo 'post_max_size = ' . ini_get('post_max_size') . "\n";
echo 'post_max_size+1 = ' . (ini_get('post_max_size')+1) . "\n";
echo 'post_max_size in bytes = ' . return_bytes(ini_get('post_max_size'));
function return_bytes($val) {
$val = trim($val);
$last = strtolower($val[strlen($val)-1]);
switch($last) {
// The 'G' modifier is available since PHP 5.1.0
case 'g':
$val *= 1024;
case 'm':
$val *= 1024;
case 'k':
$val *= 1024;
}
return $val;
}
?>
See also PHP Options And Information.
ext/math
[Since 0.8.4] - [ -P Extensions/Extmath ] - [ Online docs ]
Core functions that provides math standard functions.
This is not a real extension : it is a documentation section, that helps sorting the functions.
<?php
echo decbin(12) . PHP_EOL;
echo decbin(26);
?>
See also Mathematical Functions.
$HTTP_RAW_POST_DATA Usage
[Since 0.8.4] - [ -P Php/RawPostDataUsage ] - [ Online docs ]
$HTTP_RAW_POST_DATA is deprecated, and should be replaced by php://input .
$HTTP_RAW_POST_DATA is deprecated since PHP 5.6.
It is possible to prepare code to this lack of feature by setting always_populate_raw_post_data to -1.
<?php
// PHP 5.5 and older
$postdata = $HTTP_RAW_POST_DATA;
// PHP 5.6 and more recent
$postdata = file_get_contents(php://input);
?>
Alternatives :
- Use php://input with fopen() instead.
Useless Instructions
[Since 0.8.4] - [ -P Structures/UselessInstruction ] - [ Online docs ]
Those instructions are useless, or contains useless parts.
For example, an addition whose result is not stored in a variable, or immediately used, does nothing : it is actually performed, and the result is lost. Just plain lost. In fact, PHP might detect it, and optimize it away.
Here the useless instructions that are spotted :
<?php
// Concatenating with an empty string is useless.
$string = 'This part '.$is.' useful but '.$not.'';
// This is a typo, that PHP turns into a constant, then a string, then nothing.
continue;
// Empty string in a concatenation
$a = 'abc' . '';
// Returning expression, whose result is not used (additions, comparisons, properties, closures, new without =, ...)
1 + 2;
// Returning post-incrementation
function foo($a) {
return $a++;
}
// array_replace() with only one argument
$replaced = array_replace($array);
// array_replace() is OK with ...
$replaced = array_replace(...$array);
// @ operator on source array, in foreach, or when assigning literals
$array = @array(1,2,3);
// Multiple comparisons in a for loop : only the last is actually used.
for($i = 0; $j = 0; $j < 10, $i < 20; ++$j, ++$i) {
print $i.' '.$j.PHP_EOL;
}
// Counting the keys and counting the array is the same.
$c = count(array_keys($array))
//array_keys already provides an array with only unique values, as they were keys in a previous array
$d = array_unique(array_keys($file['messages']))
// No need for assignation inside the ternary operator
$closeQuote = $openQuote[3] === "'" ? substr($openQuote, 4, -2) : $closeQuote = substr($openQuote, 3);
?>
Alternatives :
- Remove the extra semi-colon
- Remove the useless instruction
- Assign this expression to a variable and make use of it
Abstract Static Methods
[Since 0.8.4] - [ -P Classes/AbstractStatic ] - [ Online docs ]
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() ;
}
?>
Alternatives :
- Remove abstract keyword from the method
- Remove static keyword from the method
- Remove the method
Invalid Constant Name
[Since 0.8.4] - [ -P Constants/InvalidName ] - [ Online docs ]
There is a naming convention for PHP constants names.
According to PHP's manual, constant names, ' A valid constant name starts with a letter or underscore, followed by any number of letters, numbers, or underscores.'.
Constant, must follow this regex : /[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/ .
In particular when defined using define() function, no error is produced. When using const , on the other hand, the name must be valid at linting time.
<?php
define('+3', 1); // wrong constant name!
echo constant('+3'); // invalid constant access
// This won't compile, with a syntax error.
// const 3A = 3;
?>
Alternatives :
- Change constant name
Multiple Constant Definition
[Since 0.8.4] - [ -P Constants/MultipleConstantDefinition ] - [ Online docs ]
Some constants are defined several times in your code. This will lead to a fatal error, if they are defined during the same execution.
Multiple definitions may happens at bootstrap, when the application code is collecting information about the current environment. It may also happen at inclusion time, which one set of constant being loaded, while other definition are not, avoiding conflict. Both are false positive.
<?php
// OS is defined twice.
if (PHP_OS == 'Windows') {
define('OS', 'Win');
} else {
define('OS', 'Other');
}
?>
Alternatives :
- Move the constants to a class, and include the right class based on control flow.
- Give different names to the constants, and keep the condition close to utilisation.
- Move the constants to an external configuration file : it will be easier to identify that those constants may change.
Wrong Optional Parameter
[Since 0.8.4] - [ -P Functions/WrongOptionalParameter ] - [ Online docs ]
Wrong placement of optional parameters.
PHP parameters are optional when they defined with a default value, like this :
When a function have both compulsory and optional parameters, the compulsory ones should appear first, and the optional should appear last :
PHP solves this problem at runtime, assign values in the same other, but will miss some of the default values and emits warnings.
It is better to put all the optional parameters at the end of the method's signature.
Optional parameter wrongly placed are now a Notice in PHP 8.0. The only previous case that is allowed in PHP 8.0 and also in this analysis, is when the null value is used as default for typed arguments.
<?php
function x($arg = 1) {
// PHP code here
}
?>
Alternatives :
- Give default values to all but first parameters. Null is a good default value, as PHP will use it if not told otherwise.
- Remove default values to all but last parameters. That is probably a weak solution.
- Change the order of the values, so default-valued parameters are at the end. This will probably have impact on the rest of the code, as the API is changing.
Use === null
[Since 0.8.4] - [ -P Php/IsnullVsEqualNull ] - [ Online docs ]
It is faster to use === null than the function is_null().
This is a micro-optimisation. And being used frequently, and in loops, it may yield visible speed up.
<?php
// Operator === is faster
if ($a === null) {
}
// Function call is slow
if (is_null($a)) {
}
?>
Alternatives :
- Use === comparison instead of is_null
Assertions
[Since 0.8.4] - [ -P Php/AssertionUsage ] - [ Online docs ]
Usage of assertions, to add checks within PHP code.
Assertions should be used as a debugging feature only. You may use them for sanity-checks that test for conditions that should always be TRUE and that indicate some programming errors if not or to check for the presence of certain features like extension functions or certain system limits and features.
<?php
function foo($string) {
assert(!empty($string), 'An empty string was provided!');
echo '['.$string.']';
}
?>
See also assert.
$this Is Not An Array
[Since 0.8.4] - [ -P Classes/ThisIsNotAnArray ] - [ Online docs ]
$this variable represents the current object and it is not an array.
This is unless the class (or its parents) has the ArrayAccess interface, or extends ArrayObject or SimpleXMLElement .
<?php
// $this is an array
class Foo extends ArrayAccess {
function bar() {
++$this[3];
}
}
// $this is not an array
class Foo2 {
function bar() {
++$this[3];
}
}
?>
Alternatives :
- Extends ArrayObject , or a class that extends it, to use $this as an array too.
- Implements ArrayAccess to use $this as an array too.
- Use a property in the current class to store the data, instead of $this directly.
One Variable String
[Since 0.8.4] - [ -P Type/OneVariableStrings ] - [ Online docs ]
These strings only contains one variable or property or array.
When the goal is to convert a variable to a string, it is recommended to use the type casting (string) operator : it is then clearer to understand the conversion. It is also marginally faster, though very little.
<?php
$a = 0;
$b = "$a"; // This is a one-variable string
// Better way to write the above
$b = (string) $a;
// Alternatives :
$b2 = "$a[1]"; // This is a one-variable string
$b3 = "$a->b"; // This is a one-variable string
$c = "d";
$d = "D";
$b4 = "{$$c}";
$b5 = "{$a->foo()}";
?>
Alternatives :
- Drop the surrounding string, keep the variable (or property...)
- Include in the string any concatenation that comes unconditionally after or before
- Convert the variable to a string with the (type) operator
Cast Usage
[Since 0.8.4] - [ -P Php/CastingUsage ] - [ Online docs ]
List of all cast usage.
PHP does not require (or support) explicit type definition in variable declaration; a variable's type is determined by the context in which the variable is used.
Until PHP 7.2, a (unset) operator was available. It had the same role as unset()
<?php
if (is_int($_GET['x'])) {
$number = (int) $_GET['x'];
} else {
error_display('a wrong value was provided for "x"');
}
?>
See also Type Juggling and unset.
Function Subscripting
[Since 0.8.4] - [ -P Structures/FunctionSubscripting ] - [ Online docs ]
It is possible to use the result of a methodcall directly as an array, without storing the result in a temporary variable.
This works, given that the method actually returns an array.
This syntax was not possible until PHP 5.4. Until then, it was compulsory to store the result in a variable first. Although this is now superfluous, it has been a standard syntax in PHP, and is still being used.
Storing the result in a variable is still useful if the result is actually used more than once.
<?php
function foo() {
return array(1 => 'a', 'b', 'c');
}
echo foo()[1]; // displays 'a';
// Function subscripting, the old way
function foo() {
return array(1 => 'a', 'b', 'c');
}
$x = foo();
echo $x[1]; // displays 'a';
?>
See also Accessing array elements with square bracket syntax.
Nested Loops
[Since 0.8.4] - [ -P Structures/NestedLoops ] - [ Online docs ]
Nested loops happens when a loop (while, do..while, for, foreach), is used inside another loop.
Such structure tends to require a lot of processing, as the size of both loops have to be multiplied to estimate the actual payload. They should be avoided as much as possible. This may no be always possible, though.
Nested loops are worth a check for performances reasons, as they will process a lot of times the same instructions.
<?php
// Nested loops
foreach($array as $a) {
foreach ($letters as $b) {
// This is performed count($array) * count($letters) times.
doSomething();
}
}
?>
PHP Echo Tag Usage
[Since 0.8.4] - [ -P Php/EchoTagUsage ] - [ Online docs ]
Usage of the short echo tab, that echo's directly the following content.
Static Methods Can't Contain $this
[Since 0.8.4] - [ -P Classes/StaticContainsThis ] - [ Online docs ]
Static methods are also called class methods : they may be called even if the class has no instantiated object. Thus, the local variable $this won't exist, PHP will set it to NULL as usual.
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 .
<?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;
}
}
?>
Alternatives :
- Remove any $this usage
- Turn any $this usage into a static call : $this->foo() => self::foo()
While(List() = Each())
[Since 0.8.4] - [ -P Structures/WhileListEach ] - [ Online docs ]
This code structure is quite old : it should be replace by the more modern and efficient foreach.
This structure is deprecated since PHP 7.2. It may disappear in the future.
<?php
while(list($key, $value) = each($array)) {
doSomethingWith($key) and $value();
}
foreach($array as $key => $value) {
doSomethingWith($key) and $value();
}
?>
Alternatives :
- Change this loop with foreach
- Change this loop with an array_* functions with a callback
Several Instructions On The Same Line
[Since 0.8.4] - [ -P Structures/OneLineTwoInstructions ] - [ Online docs ]
Usually, instructions do not share their line : one instruction, one line.
This is good for readability, and help at understanding the code. This is especially important when fast-reading the code to find some special situation, where such double-meaning line way have an impact.
<?php
switch ($x) {
// Is it a fallthrough or not ?
case 1:
doSomething(); break;
// Easily spotted break.
case 1:
doSomethingElse();
break;
default :
doDefault();
break;
}
?>
Alternatives :
- Add new lines, so that one expression is on one line
Multiples Identical Case
[Since 0.8.4] - [ -P Structures/MultipleDefinedCase ] - [ Online docs ]
Some cases are defined multiple times, but only one will be processed. Check the list of cases, and remove the extra one.
Exakat finds the value of the cases as much as possible, and ignore any dynamic cases (using variables).
It is also possible to write a valid switch statement, with all identical cases, and yet, different meaning each time. This is considered an edge case, and shall be manually removed.
<?php
const A = 1;
case ($x) {
case 1 :
break;
case true: // This is a duplicate of the previous
break;
case 1 + 0: // This is a duplicate of the previous
break;
case 1.0 : // This is a duplicate of the previous
break;
case A : // The A constant is actually 1
break;
case $y : // This is not reported.
break;
default:
}
?>
Alternatives :
- Remove the double case
- Change the case to another and rightful value
Switch Without Default
[Since 0.8.4] - [ -P Structures/SwitchWithoutDefault ] - [ Online docs ]
Always use a default statement in switch() and match().
Switch statements hold a number of 'case' that cover all known situations, and a 'default' one which is executed when all other options are exhausted.
For Match statements, a missing default will lead to the UnhandledMatchError exception being raised. On the other hand, the switch statement will simply exit without action nor alert.
Most of the time, switch() do need a default case, so as to catch the odd situation where the 'value is not what it was expected'. This is a good place to catch unexpected values, to set a default behavior.
<?php
// Missing default
switch($format) {
case 'gif' :
processGif();
break 1;
case 'jpeg' :
processJpeg();
break 1;
case 'bmp' :
throw new UnsupportedFormat($format);
}
// In case $format is not known, then switch is ignored and no processing happens, leading to preparation errors
// switch with default
switch($format) {
case 'text' :
processText();
break 1;
case 'jpeg' :
processJpeg();
break 1;
case 'rtf' :
throw new UnsupportedFormat($format);
default :
throw new UnknownFileFormat($format);
}
// In case $format is not known, an exception is thrown for processing
?>
Alternatives :
- Add a default case
- Catch the UnhandledMatchError exception
$this Belongs To Classes Or Traits
[Since 0.8.4] - [ -P Classes/ThisIsForClasses ] - [ Online docs ]
The pseudo-variable $this must be used inside a class or trait, or bound closures.
$this variable represents the current object, inside a class or trait scope
It is a pseudo-variable, and should be used within class's or trait's methods and not outside. It should also not be used in static methods.
PHP 7.1 is stricter and check for $this at several situations.
<?php
// as an argument
function foo($this) {
// Using global
global $this;
// Using static (not a property)
static $this;
// Can't unset it
unset($this);
try {
// inside a foreach
foreach($a as $this) { }
foreach($a as $this => $b) { }
foreach($a as $b => $this) { }
} catch (Exception $this) {
// inside a catch
}
// with Variable Variable
$a = this;
$$a = 42;
}
class foo {
function bar() {
// Using references
$a =& $this;
$a = 42;
// Using extract(), parse_str() or similar functions
extract([this => 42]); // throw new Error(Cannot re-assign $this)
var_dump($this);
}
static function __call($name, $args) {
// Using __call
var_dump($this); // prints object(C)#1 (0) {}, php-7.0 printed NULL
$this->test(); // prints ops
}
}
?>
Alternatives :
- Do not use `$this` as a variable name, except for the current object, in a class, trait or closure.
Nested Ternary
[Since 0.8.4] - [ -P Structures/NestedTernary ] - [ Online docs ]
Ternary operators should not be nested too deep.
They are a convenient instruction to apply some condition, and avoid a if() structure. It works best when it is simple, like in a one liner.
However, ternary operators tends to make the syntax very difficult to read when they are nested. It is then recommended to use an if() structure, and make the whole code readable.
This is a separate analysis from PHP's preventing nested ternaries without parenthesis.
<?php
// Simple ternary expression
echo ($a == 1 ? $b : $c) ;
// Nested ternary expressions
echo ($a === 1 ? $d === 2 ? $b : $d : $d === 3 ? $e : $c) ;
echo ($a === 1 ? $d === 2 ? $f ===4 ? $g : $h : $d : $d === 3 ? $e : $i === 5 ? $j : $k) ;
//Previous expressions, written as a if / Then expression
if ($a === 1) {
if ($d === 2) {
echo $b;
} else {
echo $d;
}
} else {
if ($d === 3) {
echo $e;
} else {
echo $c;
}
}
if ($a === 1) {
if ($d === 2) {
if ($f === 4) {
echo $g;
} else {
echo $h;
}
} else {
echo $d;
}
} else {
if ($d === 3) {
echo $e;
} else {
if ($i === 5) {
echo $j;
} else {
echo $k;
}
}
}
?>
Alternatives :
- Replace ternaries by if/then structures.
- Replace ternaries by a functioncall : this provides more readability, offset the actual code, and gives room for making it different.
Non-constant Index In Array
[Since 0.8.4] - [ -P Arrays/NonConstantArray ] - [ Online docs ]
Undefined constants revert as strings in Arrays. They are also called barewords .
In $array[index] , PHP cannot find index as a constant, but, as a default behavior, turns it into the string index .
This default behavior raise concerns when a corresponding constant is defined, either using define() or the const keyword (outside a class). The definition of the index constant will modify the behavior of the index, as it will now use the constant definition, and not the 'index' string.
It is recommended to make index a real string (with ' or "), or to define the corresponding constant to avoid any future surprise.
Note that PHP 7.2 removes the support for this feature.
<?php
// 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;
?>
Alternatives :
- Declare the constant to give it an actual value
- Turn the constant name into a string
Undefined Constants
[Since 0.8.4] - [ -P Constants/UndefinedConstants ] - [ Online docs ]
Constants definition can't be located.
Those constants are not defined in the code, and will raise errors, or use the fallback mechanism of being treated like a string.
It is recommended to define them all, or to avoid using them.
<?php
const A = 1;
define('B', 2);
// here, C is not defined in the code and is reported
echo A.B.C;
?>
Alternatives :
- Define the constant
- Fix the name of the constant
- Fix the namespace of the constant (Fully Qualified Name or use)
- Remove the usage of the constant
Instantiating Abstract Class
[Since 0.8.4] - [ -P Classes/InstantiatingAbstractClass ] - [ Online docs ]
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();
?>
Alternatives :
- Make the class non abstract
- Extends that class with a new class that is not abstract. Instantiate that second class.
- Find an existing concrete class
Class, Interface, Enum Or Trait With Identical Names
[Since 0.8.4] - [ -P Classes/CitSameName ] - [ Online docs ]
The following names are used at the same time for classes, interfaces or traits. For example,
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.
<?php
class a { /* some definitions */ }
interface a { /* some definitions */ }
trait a { /* some definitions */ }
enum a { /* some definitions */ } // PHP 8.1
?>
Alternatives :
- 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.
Empty Try Catch
[Since 0.8.4] - [ -P Structures/EmptyTryCatch ] - [ Online docs ]
The code does try, then catch errors but do no act upon the error.
At worst, the error should be logged, so as to measure the actual usage of the catch expression.
catch( Exception $e) (PHP 5) or catch(Throwable $e) with empty catch block should be banned. They ignore any error and proceed as if nothing happened. At worst, the event should be logged for future analysis.
<?php
try {
doSomething();
} catch (Throwable $e) {
// ignore this
}
?>
Alternatives :
- Add some logging in the catch
- Add a comment to mention why the catch is empty
- Change the exception, chain it and throw again
ext/pcntl
[Since 0.8.4] - [ -P Extensions/Extpcntl ] - [ Online docs ]
Extension for process control.
Process Control support in PHP implements the Unix style of process creation, program execution, signal handling and process termination. Process Control should not be enabled within a web server environment and unexpected results may happen if any Process Control functions are used within a web server environment.
<?php
declare(ticks=1);
$pid = pcntl_fork();
if ($pid == -1) {
die('could not fork');
} else if ($pid) {
exit(); // we are the parent
} else {
// we are the child
}
// detatch from the controlling terminal
if (posix_setsid() == -1) {
die('could not detach from terminal');
}
// setup signal handlers
pcntl_signal(SIGTERM, 'sig_handler');
pcntl_signal(SIGHUP, 'sig_handler');
// loop forever performing tasks
while (1) {
// do something interesting here
}
function sig_handler($signo)
{
switch ($signo) {
case SIGTERM:
// handle shutdown tasks
exit;
break;
case SIGHUP:
// handle restart tasks
break;
default:
// handle all other signals
}
}
?>
See also Process Control.
Undefined Classes
[Since 0.8.4] - [ -P Classes/UndefinedClasses ] - [ Online docs ]
Those classes are used in the code, but there are no definition for them.
This may happens under normal conditions, if the application makes use of an unsupported extension, that defines extra classes;
or if some external libraries, such as PEAR, are not provided during the analysis.
This analysis also checks in attributes.
<?php
// FPDF is a classic PDF class, that is usually omitted by Exakat.
$o = new FPDF();
// Exakat reports undefined classes in instanceof
// PHP ignores them
if ($o instanceof SomeClass) {
// doSomething();
}
// Classes may be used in typehint too
function foo(TypeHintClass $x) {
// doSomething();
}
?>
Alternatives :
- Fix the typo in the class name
- Add a missing 'use' expression
- Create the missing class
- Added a missing component
ext/redis
[Since 0.8.4] - [ -P Extensions/Extredis ] - [ Online docs ]
Extension ext/redis.
The phpredis extension provides an API for communicating with the Redis key-value store.
<?php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_NONE); // don't serialize data
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_PHP); // use built-in serialize/unserialize
$redis->setOption(Redis::OPT_SERIALIZER, Redis::SERIALIZER_IGBINARY); // use igBinary serialize/unserialize
$redis->setOption(Redis::OPT_PREFIX, 'myAppName:'); // use custom prefix on all keys
/* Options for the SCAN family of commands, indicating whether to abstract
empty results from the user. If set to SCAN_NORETRY (the default), phpredis
will just issue one SCAN command at a time, sometimes returning an empty
array of results. If set to SCAN_RETRY, phpredis will retry the scan command
until keys come back OR Redis returns an iterator of zero
*/
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_NORETRY);
$redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
?>
See also A PHP extension for Redis and Redis.
Htmlentities Calls
[Since 0.8.4] - [ -P Structures/Htmlentitiescall ] - [ Online docs ]
htmlentities() and htmlspecialchars() are used to prevent injecting special characters in HTML code. As a bare minimum, they take a string and encode it for HTML.
The second argument of the functions is the type of protection. The protection may apply to quotes or not, to HTML 4 or 5, etc. It is highly recommended to set it explicitly.
The third argument of the functions is the encoding of the string. In PHP 5.3, it is ISO-8859-1 , in 5.4, was UTF-8 , and in 5.6, it is now default_charset, a php.ini configuration that has the default value of UTF-8 . It is highly recommended to set this argument too, to avoid distortions from the configuration.
Also, note that arguments 2 and 3 are constants and string, respectively, and should be issued from the list of values available in the manual. Other values than those will make PHP use the default values.
<?php
$str = 'A quote is <b>bold</b>';
// Outputs, without depending on the php.ini: A 'quote' is <b>bold</b>
echo htmlentities($str, ENT_QUOTES, 'UTF-8');
// Outputs, while depending on the php.ini: A quote is <b>bold</b>
echo htmlentities($str);
?>
Alternatives :
- Always use the third argument with htmlentities()
Undefined Class Constants
[Since 0.8.4] - [ -P Classes/UndefinedConstants ] - [ Online docs ]
Class constants that are used, but never defined. This yield a fatal error upon execution, but no feedback at compile level.
This analysis takes into account native PHP class constants, extensions and stubs. It also disambiguate enumeration cases.
Constants are searched in the typed class or interface, and their parent. They are not searched in the children, since the children are not necessarily available, unless the class is abstract. In particular, one of the children may not define the constant, and when such child is used, it will satisfy the type, but not the constant definition.
<?php
class foo {
const A = 1;
}
function foo(Foo $f) {
// here, C is not defined in the code and is reported
echo foo::A.foo::B.foo::C;
// This is also an undefined constant
echo $f::B;
}
?>
Alternatives :
- Fix the name of the constant
- Add the constant to the current class or one of its parent
- Update the constant's visibility
Used Once Variables (In Scope)
[Since 0.8.4] - [ -P Variables/VariableUsedOnceByContext ] - [ Online docs ]
This is the list of used once variables, scope by scope. Those variables are used once in a function, a method, a class or a namespace. In any case, this means the variable is read or written, while it should be used at least twice.
Static and global variables are omitted here : they may be used multiple times by having the method being called multiple times.
Blind variables, which are defined in a foreach() structure, are also omitted : the loop will use them multiple time, assigning different values each time.
Parameters that are inherited from parent classes' methods are also omitted : they are imposed by the structure, and cannot be avoided.
<?php
function foo() {
// The variables below never appear twice, inside foo()
$writtenOnce = 1;
foo($readOnce);
// They do appear again in other functions, or in global space.
}
function bar() {
$writtenOnce = 1;
foo($readOnce);
}
?>
Alternatives :
- Remove the variable
- Fix the name of variable
- Use the variable a second time in the current scope, at least
Undefined Functions
[Since 0.8.4] - [ -P Functions/UndefinedFunctions ] - [ Online docs ]
Those functions are called, though they are not defined in the code.
The functions are probably defined in a missing library, component, or in an extension. When this is not the case, PHP yield a Fatal error at execution.
<?php
// Undefined function
foo($a);
// valid function, as it belongs to the ext/yaml extension
$parsed = yaml_parse($yaml);
// This function is not defined in the a\b\c namespace, nor in the global namespace
a\b\c\foo();
?>
Alternatives :
- Fix the name of the function in the code
- Remove the functioncall in the code
- Define the function for the code to call it
- Include the correct library in the code source
Deprecated PHP Functions
[Since 0.8.4] - [ -P Php/Deprecated ] - [ Online docs ]
The following functions are deprecated. It is recommended to stop using them now and replace them with a durable equivalent.
Note that these functions may be still usable : they generate warning that help tracking their usage in the log. To eradicate their usage, watch the logs, and update any deprecated warning. This way, the code won't be stuck when the function is actually removed from PHP.
<?php
// This is the current function
list($day, $month, $year) = explode('/', '08/06/1995');
// This is deprecated
list($day, $month, $year) = split('/', '08/06/1995');
?>
Alternatives :
- Replace those deprecated with modern syntax
- Stop using deprecated syntax
Dangling Array References
[Since 0.8.4] - [ -P Structures/DanglingArrayReferences ] - [ Online docs ]
Always unset a referenced-variable used in a loop.
It is highly recommended to unset blind variables when they are set up as references after a loop.
When omitting this step, the next loop that will also require this variable will deal with garbage values, and produce unexpected results.
<?php
$array = array(1,2,3,4);
foreach($array as &$a) {
$a += 1;
}
// This only unset the reference, not the value
unset($a);
// Dangling array problem
foreach($array as &$a) {
$a += 1;
}
//$array === array(3,4,5,6);
// This does nothing (apparently)
// $a is already a reference, even if it doesn't show here.
foreach($array as $a) {}
//$array === array(3,4,5,5);
?>
Alternatives :
- Avoid using the reference altogether : sometimes, the reference is not needed.
- Add unset() right after the loop, to avoid reusing the reference.
ext/sqlsrv
[Since 0.8.4] - [ -P Extensions/Extsqlsrv ] - [ Online docs ]
Extension for Microsoft SQL Server Driver.
The SQLSRV extension allows you to access Microsoft SQL Server and SQL Azure databases when running PHP on Windows.
<?php
$serverName = 'serverName\sqlexpress';
$connectionInfo = array( 'Database'=>'dbName', 'UID'=>'username', 'PWD'=>'password' );
$conn = sqlsrv_connect( $serverName, $connectionInfo);
if( $conn === false ) {
die( print_r( sqlsrv_errors(), true));
}
$sql = 'INSERT INTO Table_1 (id, data) VALUES (?, ?)';
$params = array(1, 'some data');
$stmt = sqlsrv_query( $conn, $sql, $params);
if( $stmt === false ) {
die( print_r( sqlsrv_errors(), true));
}
?>
See also Microsoft SQL Server Driver and PHP Driver for SQL Server Support for LocalDB.
Queries In Loops
[Since 0.8.4] - [ -P Structures/QueriesInLoop ] - [ Online docs ]
Avoid querying databases in a loop.
Querying an external database in a loop usually leads to performances problems. This is also called the 'n + 1 problem'.
This problem applies also to prepared statement : when such statement are called in a loop, they are slower than one-time large queries.
It is recommended to reduce the number of queries by making one query, and dispatching the results afterwards. This is true with SQL databases, graph queries, LDAP queries, etc.
This optimisation is not always possible : for example, some SQL queries may not be prepared, like DROP TABLE or DESC . UPDATE commands often update one row at a time, and grouping such queries may be counter-productive or unsafe.
This analysis looks for query calls inside loops, and within one functioncall.
<?php
// Typical N = 1 problem : there will be as many queries as there are elements in $array
$ids = array(1,2,3,5,6,10);
$db = new SQLite3('mysqlitedb.db');
// all the IDS are merged into the query at once
$results = $db->query('SELECT bar FROM foo WHERE id in ('.implode(',', $id).')');
while ($row = $results->fetchArray()) {
var_dump($row);
}
// Typical N = 1 problem : there will be as many queries as there are elements in $array
$ids = array(1,2,3,5,6,10);
$db = new SQLite3('mysqlitedb.db');
foreach($ids as $id) {
$results = $db->query('SELECT bar FROM foo WHERE id = '.$id);
while ($row = $results->fetchArray()) {
var_dump($row);
}
}
?>
Alternatives :
- Batch calls by using WHERE clauses and applying the same operation to all similar data
- Use native commands to avoid double query : REPLACE instead of SELECT-(UPDATE/INSERT), or UPSERT, for example
Var Keyword
[Since 0.8.4] - [ -P Classes/OldStyleVar ] - [ Online docs ]
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;
}
?>
Alternatives :
- It is recommended to avoid using var, and explicitly use the new keywords : private, protected, public
Native Alias Functions Usage
[Since 0.8.4] - [ -P Functions/AliasesUsage ] - [ Online docs ]
PHP manual recommends to avoid function aliases.
Some PHP native functions have several names, and both may be used the same way. However, one of the names is the main name, and the others are aliases. Aliases may be removed or change or dropped in the future. Even if this is not forecast, it is good practice to use the main name, instead of the aliases.
Aliases are compiled in PHP, and do not provide any performances over the normal function.
Aliases are more likely to be removed later, but they have been around for a long time.
<?php
// official way to count an array
$n = count($array);
// official way to count an array
$n = sizeof($array);
?>
Alternatives :
- Always use PHP recommended functions
Uses Default Values
[Since 0.8.4] - [ -P Functions/UsesDefaultArguments ] - [ Online docs ]
Default values are provided to methods so as to make it convenient to use. However, with new versions, those values may change. For example, in PHP 5.4, htmlentities() switched from Latin1 to UTF-8 default encoding.
As much as possible, it is recommended to use explicit values in those methods, so as to prevent from being surprise at a future PHP evolution.
This analyzer tend to report a lot of false positives, including usage of count(). Count() indeed has a second argument for recursive counts, and a default value. This may be ignored safely.
<?php
$string = Eu não sou o pão;
echo htmlentities($string);
// PHP 5.3 : Eu não sou o pão
// PHP 5.4 : Eu não sou o pão
// Stable across versions
echo htmlentities($string, 'UTF8');
?>
Alternatives :
- Mention all arguments, as much as possible
Wrong Number Of Arguments
[Since 0.8.4] - [ -P Functions/WrongNumberOfArguments ] - [ Online docs ]
Those functioncalls are made with too many or too few arguments.
When the number arguments is wrong for native functions, PHP emits a warning.
When the number arguments is too small for custom functions, PHP raises an exception.
When the number arguments is too high for custom functions, PHP ignores the arguments. Such arguments should be handled with the variadic operator, or with func_get_args() family of functions.
It is recommended to check the signature of the methods, and fix the arguments.
<?php
echo strtoupper('This function is', 'ignoring arguments');
//Warning: strtoupper() expects exactly 1 parameter, 2 given in Command line code on line 1
echo strtoupper();
//Warning: strtoupper() expects exactly 1 parameter, 0 given in Command line code on line 1
function foo($argument) {}
echo foo();
//Fatal error: Uncaught ArgumentCountError: Too few arguments to function foo(), 0 passed in /Users/famille/Desktop/analyzeG3/test.php on line 10 and exactly 1 expected in /Users/famille/Desktop/analyzeG3/test.php:3
echo foo('This function is', 'ignoring arguments');
?>
Alternatives :
- Add more arguments to fill the list of compulsory arguments
- Remove arguments to fit the list of compulsory arguments
- Use another method or class
Hardcoded Passwords
[Since 0.8.4] - [ -P Functions/HardcodedPasswords ] - [ Online docs ]
Hardcoded passwords in the code.
Hardcoding passwords is a bad idea. Not only it make the code difficult to change, but it is an information leak. It is better to hide this kind of information out of the code.
<?php
$ftp_server = '300.1.2.3'; // yes, this doesn't exists, it's an example
$conn_id = ftp_connect($ftp_server);
// login with username and password
$login_result = ftp_login($conn_id, 'login', 'password');
?>
Alternatives :
- Remove all passwords from the code. Also, check for history if you are using a VCS.
Unresolved Classes
[Since 0.8.4] - [ -P Classes/UnresolvedClasses ] - [ Online docs ]
The following classes are instantiated in the code, but their definition couldn't be found in that same code. They might be defined in an extension or an external component.
<?php
class Foo extends Bar {
private function foobar() {
// here, parent is not resolved, as Bar is not defined in the code.
return parent::$prop;
}
}
?>
Alternatives :
- Check for namespaces and aliases and make sure they are correctly configured.
Ellipsis Usage
[Since 0.8.4] - [ -P Php/EllipsisUsage ] - [ Online docs ]
Usage of the ellipsis keyword. The keyword is three dots : ... . It is also named variadic or splat operator.
It may be in function definitions and function calls; it may be in arrays; it is also usable with parenthesis.
... allows for packing or unpacking arguments into an array.
<?php
$args = [1, 2, 3];
foo(...$args);
// Identical to foo(1,2,3);
function bar(...$a) {
// Identical to : $a = func_get_args();
}
?>
See also PHP RFC: Syntax for variadic functions, PHP 5.6 and the Splat Operator and Variable-length argument lists.
Useless Constructor
[Since 0.8.4] - [ -P Classes/UselessConstructor ] - [ Online docs ]
Class constructor that have empty bodies are useless. They may be removed, as they are not called.
One edge case is when the class has a parent, and the parent constructor must not be called.
Another edge case is promoted properties: the body of the constructor is still empty, but the parameters hold the definitions of properties. These might be better outside the constructor though.
<?php
class X {
function __construct() {
// Do nothing
}
}
class Y extends X {
// Useful constructor, as it prevents usage of the parent
function __construct() {
// Do nothing
}
}
?>
Alternatives :
- Remove the constructor
Implements Is For Interface
[Since 0.8.4] - [ -P Classes/ImplementIsForInterface ] - [ Online docs ]
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 {}
?>
Alternatives :
- Create an interface from the class, and use it with the implements keyword
Use const
[Since 0.8.4] - [ -P Constants/ConstRecommended ] - [ Online docs ]
The const keyword may be used to define constant, just like the define() function.
When defining a constant, it is recommended to use 'const' when the features of the constant are not dynamical (name or value are known at compile time).
This way, constant will be defined at compile time, and not at execution time.
define() function is useful when the constant is not known at compile time, or when case sensitivity is necessary.
<?php
//Do
const A = 1;
// Don't
define('A', 1);
?>
Alternatives :
- Use const instead of define()
Unresolved Use
[Since 0.8.4] - [ -P Namespaces/UnresolvedUse ] - [ Online docs ]
The following use instructions cannot be resolved to a known class, interface, trait, constant or function. They should be dropped or fixed.
A known class, interface, trait, constant or function is defined in PHP (standard), an extension, a stub or the current code.
Use expression are options for the current namespace.
<?php
namespace A {
// class B is defined
class B {}
// class C is not defined
}
namespace X/Y {
use A/B; // This use is valid
use A/C; // This use point to nothing.
new B();
new C();
}
?>
Alternatives :
- Remove the use expression
- Fix the use expression
Undefined Parent
[Since 0.8.4] - [ -P Classes/UndefinedParentMP ] - [ Online docs ]
List of properties and methods that are accessed using parent keyword but are not defined in the parent classes.
This may compile but, eventually yields a fatal error during execution.
Note that if the parent is defined using extends someClass but someClass is not available in the tested code, it will not be reported : it may be in composer, another dependency, or just missing.
<?php
class theParent {
// No bar() method
// private bar() method is not accessible to theChild
}
class theChild extends theParent {
function foo() {
// bar is defined in theChild, but not theParent
parent::bar();
}
function bar() {
}
}
?>
Alternatives :
- Remove the usage of the found method
- Add a definition for the method in the appropriate parent
- Fix the name of the method, and replace it with a valid definition
- Change 'parent' with 'self' if the method is eventually defined in the current class
- Change 'parent' with another object, if the method has been defined in another class
- Add the 'extends' keyword to the class, to actually have a parent class
Undefined static:: Or self::
[Since 0.8.4] - [ -P Classes/UndefinedStaticMP ] - [ Online docs ]
The identified property or method are undefined. self and static refer to the current class, or one of its parent or trait.
<?php
class x {
static public function definedStatic() {}
private definedStatic = 1;
public function method() {
self::definedStatic();
self::undefinedStatic();
static::definedStatic;
static::undefinedStatic;
}
}
?>
Alternatives :
- Define the missing method or property
- Remove usage of that undefined method or property
- Fix name to call an actual local structure
- Fix object to one of the local property
Accessing Private
[Since 0.8.4] - [ -P Classes/AccessPrivate ] - [ Online docs ]
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;
}
}
?>
Access Protected Structures
[Since 0.8.4] - [ -P Classes/AccessProtected ] - [ Online docs ]
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;
?>
Alternatives :
- 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
Parent, Static Or Self Outside Class
[Since 0.8.4] - [ -P Classes/PssWithoutClass ] - [ Online docs ]
Parent, static and self keywords must be used within a class, a trait, an interface or an enum. They make no sense outside a class or trait scope, as self and static refers to the current class and parent refers to one of parent above.
PHP 7.0 and later detect some of their usage at compile time, and emits a fatal error.
Static may be used in a function or a closure, but not globally.
<?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;
?>
Alternatives :
- Make sure the keyword is inside a class context
ext/0mq
[Since 0.8.4] - [ -P Extensions/Extzmq ] - [ Online docs ]
Extension ext/zmq for 0mq .
ØMQ is a software library that lets you quickly design and implement a fast message-based application.
<?php
// Example from https://github.com/kuying/ZeroMQ/blob/d80dcc3dc1c14a343ca90bbd656b98fd55366548/zguide/examples/PHP/msgqueue.php
/*
* Simple message queuing broker
* Same as request-reply broker but using QUEUE device
* @author Ian Barber <ian(dot)barber(at)gmail(dot)com>
*/
$context = new ZMQContext();
// Socket facing clients
$frontend = $context->getSocket(ZMQ::SOCKET_ROUTER);
$frontend->bind("tcp://*:5559");
// Socket facing services
$backend = $context->getSocket(ZMQ::SOCKET_DEALER);
$backend->bind("tcp://*:5560");
// Start built-in device
new ZMQDevice($frontend, $backend);
?>
See also ZeroMQ and ZMQ.
ext/memcache
[Since 0.8.4] - [ -P Extensions/Extmemcache ] - [ Online docs ]
Extension Memcache.
Memcache module provides handy procedural and object oriented interface to memcached, highly effective caching daemon, which was especially designed to decrease database load in dynamic web applications.
<?php
$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die ('Could not connect');
$version = $memcache->getVersion();
echo 'Server\'s version: '.$version.'<br/>';
$tmp_object = new stdClass;
$tmp_object->str_attr = 'test';
$tmp_object->int_attr = 123;
$memcache->set('key', $tmp_object, false, 10) or die ('Failed to save data at the server');
echo 'Store data in the cache (data will expire in 10 seconds)<br/>';
$get_result = $memcache->get('key');
echo 'Data from the cache:<br/>';
var_dump($get_result);
?>
See also Memcache on PHP and memcache on github.
ext/memcached
[Since 0.8.4] - [ -P Extensions/Extmemcached ] - [ Online docs ]
Extension ext-memcached.
This extension uses the libmemcached library to provide an API for communicating with memcached servers. It also provides a session handler (`memcached`).
<?php
$m = new Memcached();
$m->addServer('localhost', 11211);
$m->set('foo', 100);
var_dump($m->get('foo'));
?>
See also ext/memcached manual and memcached.
Dynamic Function Call
[Since 0.8.4] - [ -P Functions/Dynamiccall ] - [ Online docs ]
Mark a functioncall made with a variable name. This means the function is only known at execution time, since it depends on the content of the variable.
<?php
// function definition
function foo() {}
// function name is in a variable, as a string.
$var = 'foo';
// dynamic call of a function
$var();
call_user_func($var);
?>
Has Variable Arguments
[Since 0.8.4] - [ -P Functions/VariableArguments ] - [ Online docs ]
Indicates if this function or method accept an arbitrary number of arguments, based on func_get_args(), func_get_arg() and func_num_args() usage.
<?php
// Fixed number of arguments
function fixedNumberOfArguments($a, $b) {
if (func_num_args() > 2) {
$c = func_get_args();
array_shift($c); // $a
array_shift($c); // $b
}
// do something
}
// Fixed number of arguments
function fixedNumberOfArguments($a, $b, $c = 1) {}
?>
Multiple Catch With Try
[Since 0.8.4] - [ -P Structures/MultipleCatch ] - [ Online docs ]
This rule reports when a try structure have several catch statements.
<?php
// This try has several catch
try {
doSomething();
} catch (RuntimeException $e) {
processRuntimeException();
} catch (OtherException $e) {
processOtherException();
}
?>
Dynamically Called Classes
[Since 0.8.4] - [ -P Classes/VariableClasses ] - [ Online docs ]
This rule reports when a class is called dynamically. To call dynamically a class, one must use a variable at instantiation time, or with the objects syntaxes.
<?php
// This class is called dynamically
class X {
const CONSTANTE = 1;
}
$classe = 'X';
$x = new $classe();
echo $x::CONSTANTE;
?>
Conditioned Function
[Since 0.8.4] - [ -P Functions/ConditionedFunctions ] - [ Online docs ]
Indicates if a function is defined only if a condition is met.
<?php
// This is a conditioned function.
// it only exists if the PHP binary doesn't have it already.
if (!function_exists('join')) {
function join($glue, $array) {
return implode($glue, $array);
}
}
?>
Conditioned Constants
[Since 0.8.4] - [ -P Constants/ConditionedConstants ] - [ Online docs ]
This rule indicates when a constant is defined if a condition is met. Several definitions of a global constant are possible in the code: using conditions, it is possible to have only one defined during execution.
<?php
if (time() > 1519629617) {
define('MY_CONST', false);
} else {
define('MY_CONST', time() - 1519629617);
}
?>
Method Is A Generator
[Since 0.8.4] - [ -P Functions/IsGenerator ] - [ Online docs ]
This rule marks functions, methods, ... that are using yield and yield from keywords. The usage of that keyword makes them Generator, as is show by the compulsory return type of Generator .
<?php
function generator() {
yield from generator2();
return 3;
}
function generator2() {
yield 1;
yield 2;
}
?>
See also Generators overview.
Try With Finally
[Since 0.8.4] - [ -P Structures/TryFinally ] - [ Online docs ]
Indicates if a try use a finally statement.
<?php
try {
$a = doSomething();
} catch (Throwable $e) {
// Fix the problem
} finally {
// remove $a anyway
unset($a);
}
?>
See also Exceptions.
Dereferencing String And Arrays
[Since 0.8.4] - [ -P Structures/DereferencingAS ] - [ Online docs ]
PHP allows the direct dereferencing of strings and arrays, from array literals and returned array.
This was added in PHP 5.5. There is no need anymore for an intermediate variable between a string and array (or any expression generating such value) and accessing an index.
<?php
$x = array(4,5,6);
$y = $x[2] ; // is 6
//May be replaced by
$y = array(4,5,6)[2];
$y = [4,5,6][2];
?>
list() May Omit Variables
[Since 0.8.4] - [ -P Structures/ListOmissions ] - [ Online docs ]
Simply omit any unused variable in a list() call.
list() is the only PHP function that accepts to have omitted arguments. If the following code makes no usage of a listed variable, just omit it.
<?php
// No need for '2', so no assignation
list ($a, , $b) = array(1, 2, 3);
// works with PHP 7.1 short syntax
[$a, , $b] = array(1, 2, 3);
// No need for '2', so no assignation
list ($a, $c, $b) = array(1, 2, 3);
?>
Alternatives :
- Remove the unused variables from the list call
- When the ignored values are at the beginning or the end of the array, array_slice() may be used to shorten the array.
Or Die
[Since 0.8.4] - [ -P Structures/OrDie ] - [ Online docs ]
Classic old style failed error management.
Interrupting a script will leave the application with a blank page, will make your life miserable for testing. Just don't do that.
<?php
// In case the connexion fails, this kills the current script
mysql_connect('localhost', $user, $pass) or die();
?>
Alternatives :
- Throw an exception
- Trigger an error with trigger_error()
- Use your own error mechanism
Constant Scalar Expressions
[Since 0.8.4] - [ -P Structures/ConstantScalarExpression ] - [ Online docs ]
Define constant with the result of static expressions. This means that constants may be defined with the const keyword, with the help of various operators but without any functioncalls.
This feature was introduced in PHP 5.6. It also supports array(), and expressions in arrays.
Those expressions (using simple operators) may only manipulate other constants, and all values must be known at compile time.
<?php
// simple definition
const A = 1;
// constant scalar expression
const B = A * 3;
// constant scalar expression
const C = [A ** 3, '3' => B];
?>
See also Constant Scalar Expressions.
Written Only Variables
[Since 0.8.4] - [ -P Variables/WrittenOnlyVariable ] - [ Online docs ]
Those variables are being written, but never read. In this way, they are useless and should be removed, or be read at some point in the code.
When the variables are only written, it takes time to process them, while discarding their result without usage. Also, when those variables are built with a complex process, it makes it difficult to understand their point, and still create maintenance work.
<?php
// $a is used multiple times, but never read
$a = 'a';
$a .= 'b';
$b = 3;
//$b is actually read once
$a .= $b + 3;
?>
Alternatives :
- Check that variables are written AND read in each context
- Remove variables that are only read
- Use the variable that are only read
Must Return Methods
[Since 0.8.4] - [ -P Functions/MustReturn ] - [ Online docs ]
The following methods are expected to return a value that will be used later. Without return, they are useless.
Methods that must return are : __get(), __isset(), __sleep(), __toString(), __set_state(), __invoke(), __debugInfo().
Methods that may not return, but are often expected to : __call(), __callStatic().
<?php
class foo {
public function __isset($a) {
// returning something useful
return isset($this->$var[$a]);
}
public function __get($a) {
$this->$a++;
// not returning...
}
public function __call($name, $args) {
$this->$name(...$args);
// not returning anything, but that's OK
}
}
?>
Alternatives :
- Add a return expression, with a valid data type
- Remove the return typehint
Empty Instructions
[Since 0.8.4] - [ -P Structures/EmptyLines ] - [ Online docs ]
Empty instructions are part of the code that have no instructions.
This may be trailing semi-colon or empty blocks for if-then structures.
Comments that explains the reason of the situation are not taken into account.
<?php
$condition = 3;;;;
if ($condition) { }
?>
Alternatives :
- Remove the empty lines
- Fill the empty lines
ext/imagick
[Since 0.8.4] - [ -P Extensions/Extimagick ] - [ Online docs ]
Extension Imagick for PHP.
Imagick is a native php extension to create and modify images using the ImageMagick API.
<?php
header('Content-type: image/jpeg');
$image = new Imagick('image.jpg');
// If 0 is provided as a width or height parameter,
// aspect ratio is maintained
$image->thumbnailImage(100, 0);
echo $image;
?>
See also Imagick for PHP and Imagick.
ext/oci8
[Since 0.8.4] - [ -P Extensions/Extoci8 ] - [ Online docs ]
Extension ext/oci8.
OCI8 gives access Oracle Database 12c, 11g, 10g, 9i and 8i.
<?php
$conn = oci_connect('hr', 'welcome', 'localhost/XE');
if (!$conn) {
$e = oci_error();
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
// Prepare the statement
$stid = oci_parse($conn, 'SELECT * FROM departments');
if (!$stid) {
$e = oci_error($conn);
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
// Perform the logic of the query
$r = oci_execute($stid);
if (!$r) {
$e = oci_error($stid);
trigger_error(htmlentities($e['message'], ENT_QUOTES), E_USER_ERROR);
}
// Fetch the results of the query
print '<table border='1'>' . PHP_EOL;
while ($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {
print '<tr>' . PHP_EOL;
foreach ($row as $item) {
print ' <td>' . ($item !== null ? htmlentities($item, ENT_QUOTES) : ' ') . '</td>' . PHP_EOL;
}
print '</tr>' . PHP_EOL;
}
print '</table>' . PHP_EOL;
oci_free_statement($stid);
oci_close($conn);
?>
See also Oracle OCI8 and Oracle.
Overwritten Exceptions
[Since 0.8.4] - [ -P Exceptions/OverwriteException ] - [ Online docs ]
In catch blocks, it is good practice to avoid overwriting the incoming exception, as information about the exception will be lost.
<?php
try {
doSomething();
} catch (SomeException $e) {
// $e is overwritten
$e = new anotherException($e->getMessage());
throw $e;
} catch (SomeOtherException $e) {
// $e is chained with the next exception
$e = new Exception($e->getMessage(), 0, $e);
throw $e;
}
?>
Alternatives :
- Use another variable name to create new values inside the catch
- Use anonymous catch clause (no variable caught) in PHP 8.0, to make this explicit
Foreach Reference Is Not Modified
[Since 0.8.4] - [ -P Structures/ForeachReferenceIsNotModified ] - [ Online docs ]
Foreach statement may loop using a reference, especially when the loop has to change values of the array it is looping on.
In the spotted loop, reference are used but never modified. They may be removed.
<?php
$letters = range('a', 'z');
// $letter is not used here
foreach($letters as &$letter) {
$alphabet .= $letter;
}
// $letter is actually used here
foreach($letters as &$letter) {
$letter = strtoupper($letter);
}
?>
Alternatives :
- Remove the reference from the foreach
- Actually modify the content of the reference
ext/imap
[Since 0.8.4] - [ -P Extensions/Extimap ] - [ Online docs ]
Extension ext/imap.
This extension operate with the IMAP protocol, as well as the NNTP, POP3 and local mailbox access methods.
<?php
$mbox = imap_open('{imap.example.org}', 'username', 'password', OP_HALFOPEN)
or die('can't connect: ' . imap_last_error());
$list = imap_list($mbox, '{imap.example.org}', '*');
if (is_array($list)) {
foreach ($list as $val) {
echo imap_utf7_decode($val) . PHP_EOL;
}
} else {
echo 'imap_list failed: ' . imap_last_error() . PHP_EOL;
}
imap_close($mbox);
?>
See also IMAP.
Overwritten Class Constants
[Since 0.8.4] - [ -P Classes/OverwrittenConst ] - [ Online docs ]
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;
}
}
?>
Alternatives :
- Remove the constant in the interface
- Remove the constant in the class
- Rename one of the constants
Dynamic Class Constant
[Since 0.8.4] - [ -P Classes/DynamicConstantCall ] - [ Online docs ]
This is the list of dynamic calls to class constants.
Constant may be dynamically called with the constant() function. In PHP 8.3, they may also be called with a new dedicated syntax.
<?php
// Dynamic access to 'E_ALL'
echo constant('E_ALL');
interface i {
const MY_CONSTANT = 1;
}
// Dynamic access to 'E_ALL'
$constantName = 'MY_CONSTANT';
echo constant('i::'.$constantName);
// With PHP 8.3 :
echo i::{$constantName};
?>
Dynamic Methodcall
[Since 0.8.4] - [ -P Classes/DynamicMethodCall ] - [ Online docs ]
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();
?>
Dynamic New
[Since 0.8.4] - [ -P Classes/DynamicNew ] - [ Online docs ]
Dynamic instantiation of classes. It happens when the name of the class is an executable expression, and, as such, only known at execution time.
<?php
$classname = foo();
$object = new $classname();
$object = new (foo());
?>
Dynamic Property
[Since 0.8.4] - [ -P Classes/DynamicPropertyCall ] - [ Online docs ]
Dynamic access to class property. This is when the the name of the property is stored in a variable (or other container), rather than statically provided.
<?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;
?>
See also class.
Don't Change Incomings
[Since 0.8.4] - [ -P Structures/NoChangeIncomingVariables ] - [ Online docs ]
PHP hands over a lot of information using special variables like $_GET, $_POST, etc... Modifying those variables and those values inside variables means that the original content is lost, while it will still look like raw data, and, as such, will be untrustworthy.
It is recommended to put the modified values in another variable, and keep the original one intact.
<?php
// filtering and keeping the incoming value.
$_DATA'id'] = (int) $_GET['id'];
// filtering and changing the incoming value.
$_GET['id'] = strtolower($_GET['id']);
?>
Alternatives :
- Set the value to another variable and apply modifications to that variable
Dynamic Classes
[Since 0.8.4] - [ -P Classes/DynamicClass ] - [ Online docs ]
Dynamic calls of classes.
<?php
class x {
static function staticMethod() {}
}
$class = 'x';
$class::staticMethod();
?>
Compared Comparison
[Since 0.8.4] - [ -P Structures/ComparedComparison ] - [ Online docs ]
Usually, comparison are sufficient, and it is rare to have to compare the result of comparison. Check if this two-stage comparison is really needed.
<?php
if ($a === strpos($string, $needle) > 2) {}
// the expression above apply precedence :
// it is equivalent to :
if (($a === strpos($string, $needle)) > 2) {}
?>
See also Operators Precedence.
Useless Return
[Since 0.8.4] - [ -P Functions/UselessReturn ] - [ Online docs ]
The spotted functions or methods have a return statement, but this statement is useless. This is the case for constructor and destructors, whose return value are ignored or inaccessible.
When return is void, and the last element in a function, it is also useless.
<?php
class foo {
function __construct() {
// return is not used by PHP
return 2;
}
}
function bar(&$a) {
$a++;
// The last return, when empty, is useless
return;
}
?>
Alternatives :
- Remove the return expression. Keep any other calculation.
Multiple Classes In One File
[Since 0.8.4] - [ -P Classes/MultipleClassesInFile ] - [ Online docs ]
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.
One good reason to have multiple classes in one file is to reduce include time by providing everything into one nice include.
<?php
// three classes in the same file
class foo {}
class bar {}
class foobar{}
?>
Alternatives :
- Split the file into smaller files, one for each class
File Uploads
[Since 0.8.4] - [ -P Structures/FileUploadUsage ] - [ Online docs ]
This code makes usage of file upload features of PHP.
Upload file feature is detected through the usage of specific functions :
<?php
$uploaddir = '/var/www/uploads/';
$uploadfile = $uploaddir . basename($_FILES['userfile']['name']);
echo '<pre>';
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
echo 'File is valid, and was successfully uploaded.'.PHP_EOL;
} else {
echo 'Possible file upload attack!'.PHP_EOL;
}
echo 'Here is some more debugging info:';
print_r($_FILES);
print '</pre>';
?>
See also Handling file uploads.
Unused Classes
[Since 0.8.4] - [ -P Classes/UnusedClass ] - [ Online docs ]
The following classes are never explicitly used in the code.
Note that this may be valid in case the current code is a library or framework, since it defines classes that are used by other (unprovided) codes.
Also, this analyzer may find classes that are, in fact, dynamically loaded.
<?php
class unusedClasss {}
class usedClass {}
$y = new usedClass();
?>
Alternatives :
- Remove unused classes
- Make use of unused classes
- Fix class name
ext/intl
[Since 0.8.4] - [ -P Extensions/Extintl ] - [ Online docs ]
Extension international.
Internationalization extension (further is referred as Intl) is a wrapper for ICU library, enabling PHP programmers to perform various locale-aware operations including but not limited to formatting, transliteration, encoding conversion, calendar operations, UCA-conformant collation, locating text boundaries and working with locale identifiers, timezones and graphemes.
<?php
$coll = new Collator('en_US');
$al = $coll->getLocale(Locale::ACTUAL_LOCALE);
echo "Actual locale: $al\n";
$formatter = new NumberFormatter('en_US', NumberFormatter::DECIMAL);
echo $formatter->format(1234567);
?>
See also Internationalization Functions.
Dynamic Code
[Since 0.8.4] - [ -P Structures/DynamicCode ] - [ Online docs ]
List of instructions that were left during analysis, as they rely on dynamic data.
Any further analysis will need to start from here.
<?php
// Dynamic call to 'method';
$name = 'method';
$object->$name();
// Hard coded call to 'method';
$object->method();
?>
See also Variable functions.
Unpreprocessed Values
[Since 0.8.4] - [ -P Structures/Unpreprocessed ] - [ Online docs ]
Preprocessing values is the preparation of values before PHP executes the code.
There is no macro language in PHP, that prepares the code before compilation, bringing some comfort and short syntax. Most of the time, one uses PHP itself to preprocess data.
For example :
could be written
and avoid preprocessing the string into an array first.
Preprocessing could be done anytime the script includes all the needed values to process the expression.
This is a micro-optimisation, in particular when the expression is used once.
<?php
$days_en = 'monday,tuesday,wednesday,thursday,friday,saturday,sunday';
$days_zh = '星期-,星期二,星期三,星期四,星期五,星期六,星期日';
$days = explode(',', $lang === 'en' ? $days_en : $days_zh);
?>
Alternatives :
- Preprocess the values and hardcode them in PHP. Do not use PHP to calculate something at the last moment.
- Use already processed values, or cache to avoid calculating the value each hit.
- Create a class that export the data in the right format for every situation, including the developer's comfort.
ext/pspell
[Since 0.8.4] - [ -P Extensions/Extpspell ] - [ Online docs ]
Extension pspell.
These functions allow you to check the spelling of a word and offer suggestions.
<?php
$pspell_link = pspell_new('en');
if (pspell_check($pspell_link, 'testt')) {
echo 'This is a valid spelling';
} else {
echo 'Sorry, wrong spelling';
}
?>
See also Pspell and pspell.
No Direct Access
[Since 0.8.4] - [ -P Structures/NoDirectAccess ] - [ Online docs ]
This expression protects files against direct access. It will kill the process if it realizes this is not supposed to be directly accessed.
Those expressions are used in applications and framework, to prevent direct access to definition files.
<?php
// CONSTANT_EXEC is defined in the main file of the application
defined('CONSTANT_EXEC') or die('Access not allowed'); : Constant used!
?>
ext/opcache
[Since 0.8.4] - [ -P Extensions/Extopcache ] - [ Online docs ]
Extension opcache.
OPcache improves PHP performance by storing precompiled script bytecode in shared memory, thereby removing the need for PHP to load and parse scripts on each request.
<?php
echo opcache_compile_file('/var/www/index.php');
print_r(opcache_get_status());
?>
See also OPcache functions.
ext/expect
[Since 0.8.4] - [ -P Extensions/Extexpect ] - [ Online docs ]
Extension Expect.
This extension allows to interact with processes through PTY . You may consider using the expect:// wrapper with the filesystem functions which provide a simpler and more intuitive interface.
<?php
ini_set('expect.loguser', 'Off');
$stream = fopen('expect://ssh root@remotehost uptime', 'r');
$cases = array (
array (0 => 'password:', 1 => PASSWORD)
);
switch (expect_expectl ($stream, $cases)) {
case PASSWORD:
fwrite ($stream, 'password'.PHP_EOL);
break;
default:
die ('Error was occurred while connecting to the remote host!'.PHP_EOL);
}
while ($line = fgets($stream)) {
print $line;
}
fclose ($stream);
?>
See also expect.
Undefined Properties
[Since 0.8.4] - [ -P Classes/UndefinedProperty ] - [ Online docs ]
List of properties that are not explicitly defined in the class, its parents or traits.
It is possible to spot unidentified properties by using the PHP's magic methods __get and __set . Even if the class doesn't use magic methods, any call to an undefined property will be directed to those methods, and they can be used as a canary, warning that the code is missing a definition.
In PHP 8.2, undefined properties are reported as deprecated. They will become a Fatal Error in PHP 9.0.
<?php
class foo {
// property definition
private bar = 2;
function foofoo() {
// $this->bar is defined in the class
// $this->barbar is NOT defined in the class
return $this->bar + $this->barbar;
}
}
?>
Alternatives :
- Add an explicit property definition, and give it null as a default value : this way, it behaves the same as undefined.
- Rename the property to one that exists already.
ext/gettext
[Since 0.8.4] - [ -P Extensions/Extgettext ] - [ Online docs ]
Extension Gettext.
The gettext functions implement an NLS (Native Language Support) API which can be used to internationalize your PHP applications.
<?php
// Set language to German
putenv('LC_ALL=de_DE');
setlocale(LC_ALL, 'de_DE');
// Specify location of translation tables
bindtextdomain('myPHPApp', './locale');
// Choose domain
textdomain('myPHPApp');
// Translation is looking for in ./locale/de_DE/LC_MESSAGES/myPHPApp.mo now
// Print a test message
echo gettext('Welcome to My PHP Application');
// Or use the alias _() for gettext()
echo _('Have a nice day');
?>
See also Gettext and ext/gettext.
Short Open Tags
[Since 0.8.4] - [ -P Php/ShortOpenTagRequired ] - [ Online docs ]
Usage of short open tags is discouraged. The following files were found to be impacted by the short open tag directive at compilation time. They must be reviewed to ensure no <? tags are found in the code.
Strict Comparison With Booleans
[Since 0.8.4] - [ -P Structures/BooleanStrictComparison ] - [ Online docs ]
Strict comparisons prevent mistaking an error with a false.
Boolean values may be easily mistaken with other values, especially when the function may return integer or boolean as a normal course of action.
It is encouraged to use strict comparison === or !== when booleans are involved in a comparison.
switch() structures always uses `==` comparisons. Since PHP 8.0, it is possible to use match() to have strict comparisons. This is not reported by this analysis, as every switch should be refactored.
Native functions in_array(), array_keys() and array_search() have a third parameter to make it use strict comparisons.
<?php
// distinguish between : $b isn't in $a, and, $b is at the beginning of $a
if (strpos($a, $b) === 0) {
doSomething();
}
// DOES NOT distinguish between : $b isn't in $a, and, $b is at the beginning of $a
if (strpos($a, $b)) {
doSomething();
}
// will NOT mistake 1 and true
$a = array(0, 1, 2, true);
if (in_array($a, true, true)) {
doSomething();
}
// will mistake 1 and true
$a = array(0, 1, 2, true);
if (in_array($a, true)) {
doSomething();
}
?>
Alternatives :
- Use strict comparison whenever possible
Lone Blocks
[Since 0.8.4] - [ -P Structures/LoneBlock ] - [ Online docs ]
Grouped code without a commanding structure is useless and may be removed.
Blocks are compulsory when defining a structure, such as a class, a function or a switch. They are most often used with flow control instructions, like if then or foreach.
Blocks are also valid syntax that group several instructions together, though they have no effect at all. They are unusual enough to confuse the reader.
Most often, it is a ruin from a previous flow control instruction, whose condition was removed or commented. They should be removed.
<?php
// Lone block without artefact
{
$a = 3;
$c = 4;
}
// Lone block with commented out loop
//foreach($a as $b)
{
$b = 1;
}
?>
Alternatives :
- Remove the useless curly brackets
$this Is Not For Static Methods
[Since 0.8.4] - [ -P Classes/ThisIsNotForStatic ] - [ Online docs ]
Static methods shouldn't use $this variable.
$this variable represents an object, the current object. It is not compatible with a static method, which may operate without any object.
While executing a static method, $this is actually set to NULL.
<?php
class foo {
static $staticProperty = 1;
// Static methods should use static properties
static public function count() {
return self::$staticProperty++;
}
// Static methods can't use $this
static public function bar() {
return $this->a; // No $this usage in a static method
}
}
?>
Alternatives :
- Remove the static keyword on the method, and update all calls to this method to use $this
- Remove the usage of $this in the method, replacing it with static properties
- Make $this an argument (and change its name) : then, make the method a function
Super Global Usage
[Since 0.8.4] - [ -P Php/SuperGlobalUsage ] - [ Online docs ]
Spot usage of Super global variables, such as $_GET, $_POST or $_REQUEST.
<?php
echo htmlspecialchars($_GET['name'], UTF-8);
?>
See also Superglobals.
Global Usage
[Since 0.8.4] - [ -P Structures/GlobalUsage ] - [ Online docs ]
List usage of globals variables, with global keywords or direct access to $GLOBALS.
It is recommended to avoid using global variables, at it makes it very difficult to track changes in values across the whole application.
<?php
$a = 1; /* global scope */
function test()
{
echo $a; /* reference to local scope variable */
}
test();
?>
See also Variable scope.
Logical Should Use Symbolic Operators
[Since 0.8.4] - [ -P Php/LogicalInLetters ] - [ Online docs ]
Logical operators come in two flavors : and / &&, || / or, ^ / xor. However, they are not exchangeable, as && and and have different precedence.
It is recommended to use the symbol operators, rather than the letter ones.
<?php
// Avoid lettered operator, as they have lower priority than expected
$a = $b and $c;
// $a === 3 because equivalent to ($a = $b) and $c;
// safe way to write the above :
$a = ($b and $c);
$a = $b && $c;
// $a === 1
?>
Alternatives :
- Change the letter operators to the symbol one : and => &&, or => ||, xor => ^. Review the new expressions as processing order may have changed.
- Add parenthesis to make sure that the order is the expected one
Could Use self
[Since 0.8.4] - [ -P Classes/ShouldUseSelf ] - [ Online docs ]
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;
}
}
?>
Alternatives :
- Replace the explicit name with self
Catch Overwrite Variable
[Since 0.8.4] - [ -P Structures/CatchShadowsVariable ] - [ Online docs ]
The try/catch structure uses some variables that are also in use in this scope. In case of a caught exception, the exception will be put in the catch variable, and overwrite the current value, loosing some data.
It is recommended to use another name for these catch variables.
<?php
// variables and caught exceptions are distinct
$argument = 1;
try {
methodThatMayRaiseException($argument);
} (Exception $e) {
// here, $e has been changed to an exception.
}
// variables and caught exceptions are overlapping
$e = 1;
try {
methodThatMayRaiseException();
} (Exception $e) {
// here, $e has been changed to an exception.
}
?>
Alternatives :
- Use a standard : only use $e (or else) to catch exceptions. Avoid using them for anything else, parameter, property or local variable.
- Change the variable, and keep the caught exception
Namespaces
[Since 0.8.4] - [ -P Namespaces/NamespaceUsage ] - [ Online docs ]
Inventory of all namespaces.
<?php
namespace My/Personal/Name;
class Name {}
?>
Deep Definitions
[Since 0.8.4] - [ -P Functions/DeepDefinitions ] - [ Online docs ]
Structures, such as functions, classes, interfaces, traits, enum, etc. may be defined anywhere in the code, including inside functions. This is legit code for PHP.
Since the availability of autoload, with spl_register_autoload(), there is no need for that kind of code. Structures should be defined, and accessible to the autoloading. Inclusions and deep definitions should be avoided, as they compel code to load some definitions, while autoloading will only load them if needed.
Functions are excluded from autoload, but shall be gathered in libraries, and not hidden inside other code.
Constants definitions are tolerated inside functions : they may be used for avoiding repeat, or noting the usage of such function.
Definitions inside a if/then statement, that include PHP version check are accepted here.
<?php
class X {
function init() {
// myFunction is defined when and only if X::init() is called.
if (!function_exists('myFunction'){
function myFunction($a) {
return $a + 1;
}
})
}
}
?>
Alternatives :
- Move function definitions to the global space : outside structures, and method.
File Is Not Definitions Only
[Since 0.8.4] - [ -P Files/NotDefinitionsOnly ] - [ Online docs ]
An included file should only provide definitions and declarations, or executable code : not both.
With definitions only files, their inclusion provide new features, and keep the current execution untouched, and in control of the flow.
Within this context, globals, use, and namespaces instructions are not considered a warning.
<?php
// This whole script is a file
// It contains definitions and global code
class foo {
static public $foo = null;
}
//This can be a singleton creation
foo::$foo = new foo();
trait t {}
class bar {}
?>
Alternatives :
- Remove the executable code from the file
- Remove the definitions from the file
Repeated print()
[Since 0.8.4] - [ -P Structures/RepeatedPrint ] - [ Online docs ]
Merge several print or echo in one call, to speed up the processing.
It is recommended to use echo with multiple arguments, or a concatenation with print, instead of multiple calls to print echo, when outputting several blob of text.
<?php
//Write :
echo 'a', $b, 'c';
print 'a' . $b . 'c';
//Don't write :
print 'a';
print $b;
print 'c';
?>
Alternatives :
- Merge all prints into one echo call, separating arguments by commas.
- Collect all values in one variable, and do only one call to print or echo.
Avoid Parenthesis With Language Construct
[Since 0.8.4] - [ -P Structures/PrintWithoutParenthesis ] - [ Online docs ]
Avoid Parenthesis for language construct. Languages constructs are a few PHP native elements, that looks like functions but are not.
Among other distinction, those elements cannot be directly used as variable function call, and they may be used with or without parenthesis.
The usage of parenthesis actually give some feeling of comfort, it won't prevent PHP from combining those argument with any later operators, leading to unexpected results.
Even if most of the time, usage of parenthesis is legit, it is recommended to avoid them.
<?php
// normal usage of include
include 'file.php';
// This looks like a function and is not
include('file2.php');
?>
Alternatives :
- Remove the parenthesis
Objects Don't Need References
[Since 0.8.4] - [ -P Structures/ObjectReferences ] - [ Online docs ]
There is no need to add references to parameters for objects, as those are always passed by reference when used as arguments.
Reference operator is needed when the object are replaced inside the method with a new value (or a clone), as whole. Calls to methods or property modifications do not require extra reference.
Reference operator is also needed when one of the types is scalar : this include null, and the hidden null type : that is when the default value is null.
This rule applies to arguments in methods, and to foreach() blind variables.
<?php
$object = new stdClass();
$object->name = 'a';
foo($object);
print $object->name; // Name is 'b'
// No need to make $o a reference
function foo(&$o) {
$o->name = 'b';
}
// $o is assigned inside the function : the parameter must have a &, or the new object won't make it out of the foo3 scope
function foo3(&$o) {
$o = new stdClass;
}
$array = array($object);
foreach($array as &$o) { // No need to make this a reference
$o->name = 'c';
}
?>
Alternatives :
- Remove the reference
- Assign the argument with a new value
Lost References
[Since 0.8.4] - [ -P Variables/LostReferences ] - [ Online docs ]
Either avoid references, or propagate them correctly.
When assigning a referenced variable with another reference, the initial reference is lost, while the intend was to transfer the content.
Do not reassign a reference with another reference. Assign new content to the reference to change its value.
<?php
function foo(&$lostReference, &$keptReference)
{
$c = 'c';
// $lostReference was a reference to $bar, but now, it is a reference to $c
$lostReference =& $c;
// $keptReference was a reference to $bar : it is still now, though it contains the actual value of $c now
$keptReference = $c;
}
$bar = 'bar';
$bar2 = 'bar';
foo($bar, $bar2);
//displays bar c, instead of bar bar
print $bar. ' '.$bar2;
?>
Alternatives :
- Always assign new value to an referenced argument, and don't reassign a new reference
Constants Created Outside Its Namespace
[Since 0.8.4] - [ -P Constants/CreatedOutsideItsNamespace ] - [ Online docs ]
Constants Created Outside Its Namespace.
Using the define() function, it is possible to create constant outside their namespace, but using the fully qualified namespace.
However, this makes the code confusing and difficult to debug. It is recommended to move the constant definition to its namespace.
<?php
namespace A\B {
// define A\B\C as 1
define('C', 1);
}
namespace D\E {
// define A\B\C as 1, while outside the A\B namespace
define('A\B\C', 1);
}
?>
Alternatives :
- Declare the constant in its namespace
Fully Qualified Constants
[Since 0.8.4] - [ -P Namespaces/ConstantFullyQualified ] - [ Online docs ]
Constants defined with their namespace.
When defining constants with define() function, it is possible to include the actual namespace :
However, the name should be fully qualified without the initial \. Here, \a\b\c constant will never be accessible as a namespace constant, though it will be accessible via the constant() function.
Also, the namespace will be absolute, and not a relative namespace of the current one.
<?php
define('a\b\c', 1);
?>
Alternatives :
- Drop the initial \ when creating constants with define() : for example, use trim($x, '\'), which removes anti-slashes before and after.
Never Used Properties
[Since 0.8.4] - [ -P Classes/PropertyNeverUsed ] - [ Online docs ]
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;
?>
Alternatives :
- Drop unused properties
- Change the name of the unused properties
- Move the properties to children classes
- Find usage for unused properties
No Real Comparison
[Since 0.8.4] - [ -P Type/NoRealComparison ] - [ Online docs ]
Avoid comparing decimal numbers with ==, ===, !==, !=. Real numbers have an error margin which is random, and makes it very difficult to match even if the compared value is a literal.
PHP uses an internal representation in base 2 : any number difficult to represent with this base (like 0.1 or 0.7) will have a margin of error.
Use precision formulas with abs() to approximate values with a given precision, or avoid reals altogether.
<?php
$a = 1/7;
$b = 2.0;
// 7 * $a is a real, not an integer
var_dump( 7 * $a === 1);
// rounding error leads to wrong comparison
var_dump( (0.1 + 0.7) * 10 == 8);
// although
var_dump( (0.1 + 0.7) * 10);
// displays 8
// precision formula to use with reals. Adapt 0.0001 to your precision needs
var_dump( abs(((0.1 + 0.7) * 10) - 8) < 0.0001);
?>
Alternatives :
- Cast the values to integer before comparing
- Compute the difference, and keep it below a threshold
- Use the gmp or the bcmath extension to handle high precision numbers
- Change the 'precision' directive of PHP : ini_set('precision', 30) to make number larger
- Multiply by a power of ten, before casting to integer for the comparison
- Use floor(), ceil() or round() to compare the numbers, with a specific precision
Should Use Local Class
[Since 0.8.4] - [ -P Classes/ShouldUseThis ] - [ Online docs ]
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.
Note that a method using a class constant is not considered as using the local class, for this analyzer.
<?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;
}
}
?>
Alternatives :
- Make this method a function
- Actually use $this, or any related attributes of the class
Usage Of class_alias()
[Since 0.8.4] - [ -P Classes/ClassAliasUsage ] - [ Online docs ]
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.
ext/apache
[Since 0.8.4] - [ -P Extensions/Extapache ] - [ Online docs ]
Extension Apache.
These functions are only available when running PHP as an Apache module.
<?php
$ret = apache_getenv("SERVER_ADDR");
echo $ret;
?>
See also Extension Apache and Apache server.
ext/eaccelerator
[Since 0.8.4] - [ -P Extensions/Exteaccelerator ] - [ Online docs ]
Extension Eaccelerator. eAccelerator is a free open-source PHP accelerator & optimizer. DEPRECATED: This project is deprecated and does not work with anything newer than PHP 5.3.
ext/fpm
[Since 0.8.4] - [ -P Extensions/Extfpm ] - [ Online docs ]
Extension FPM, FastCGI Process Manager.
FPM (FastCGI Process Manager) is an alternative PHP FastCGI implementation with some additional features (mostly) useful for heavy-loaded sites.
<?php
echo $text;
fastcgi_finish_request( );
?>
See also FastCGI Process Manager.
No Direct Call To Magic Method
[Since 0.8.4] - [ -P Classes/DirectCallToMagicMethod ] - [ Online docs ]
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.
Accessing those methods in a static way is also discouraged.
<?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;
?>
See also Magical PHP: __call.
String May Hold A Variable
[Since 0.8.4] - [ -P Type/StringHoldAVariable ] - [ Online docs ]
Strings that contains a variable, yet are not interpolated.
Single quotes and Nowdoc syntax may include $ signs. They are treated as literals, and not replaced with a variable value.
However, there are some potential variables in those strings, making it possible for an error : the variable was forgotten and will be published as such. It is worth checking the content and make sure those strings are not variables.
<?php
$a = 2;
// Explicit variable, but literal effect is needed
echo '$a is '.$a;
// One of the variable has been forgotten
echo '$a is $a';
// $CAD is not a variable, rather a currency unit
$total = 12;
echo $total.' $CAD';
// $CAD is not a variable, rather a currency unit
$total = 12;
// Here, $total has been forgotten
echo <<<'TEXT'
$total $CAD
TEXT;
?>
Alternatives :
- Check if the variable is really a variable
- Turn the string into an interpolated string (double quote, heredoc, concatenation)
Echo With Concat
[Since 0.8.4] - [ -P Structures/EchoWithConcat ] - [ Online docs ]
Optimize your echo 's by avoiding concatenating at echo time, but serving all argument separated. This will save PHP a memory copy.
If values, literals and variables, are small enough, this won't have visible impact. Otherwise, this is less work and less memory waste.
instead of
It is a micro-optimisation.
<?php
echo $a, ' b ', $c;
?>
Alternatives :
- Turn the concatenation into a list of argument, by replacing the dots by commas.
Unused Global
[Since 0.8.4] - [ -P Structures/UnusedGlobal ] - [ Online docs ]
A global keyword is used in a method, yet the variable is not actually used. This makes PHP import values for nothing, or may create interference
<?php
function foo() {
global bar;
return 1;
}
?>
Alternatives :
- Remove the global declaration
- Remove the global variable altogether
Useless Global
[Since 0.8.4] - [ -P Structures/UselessGlobal ] - [ Online docs ]
Global are useless in two cases. First, on super-globals, which are always globals, like $_GET; secondly, on variables that are not used.
Also, PHP has superglobals, a special team of variables that are always available, whatever the context.
They are : $GLOBALS, $_SERVER, $_GET, $_POST, $_FILES, $_COOKIE, $_SESSION, $_REQUEST and $_ENV.
<?php
// $_POST is already a global : it is in fact a global everywhere
global $_POST;
// $unused is useless
function foo() {
global $used, $unused;
++$used;
}
?>
Alternatives :
- Drop the global expression
Preprocessable
[Since 0.8.4] - [ -P Structures/ShouldPreprocess ] - [ Online docs ]
The following expressions are made of literals or already known values : they may be fully calculated before running PHP.
By doing so, this will reduce the amount of work of PHP. This is a micro-optimisation, when this is used once, or the amount of work is small. It may be kept for readability.
<?php
// Building an array from a string
$name = 'PHP'.' '.'7.2';
// Building an array from a string
$list = explode(',', 'a,b,c,d,e,f');
// Calculating a power
$kbytes = $bytes / pow(2, 10);
// This will never change
$name = ucfirst(strtolower('PARIS'));
?>
Alternatives :
- Do the work yourself, instead of giving it to PHP
Useless Final
[Since 0.8.4] - [ -P Classes/UselessFinal ] - [ Online docs ]
When a class is declared final, all of its methods are also final by default.
There is no need to declare them individually final.
<?php
final class foo {
// Useless final, as the whole class is final
final function method() { }
}
class bar {
// Useful final, as the whole class is not final
final function method() { }
}
?>
See also Final Keyword and When to declare final.
Use Constant Instead Of Function
[Since 0.8.4] - [ -P Structures/UseConstant ] - [ Online docs ]
Some functioncalls have a constant equivalent, that is faster to execute than calling the function.
This applies to the following functions :
- pi() : replace with `M_PI`
- phpversion() : replace with `PHP_VERSION`
<?php
// recommended way
echo PHP_VERSION;
// slow version
echo php_version();
?>
Alternatives :
- Use the constant version, not the function.
Resources Usage
[Since 0.8.4] - [ -P Structures/ResourcesUsage ] - [ Online docs ]
List of situations that are creating resources.
<?php
// This functioncall creates a resource to use
$fp = fopen('/tmp/file.txt', 'r');
if (!is_resource($fp)){
thrown new RuntimeException('Could not open file.txt');
}
?>
Useless Unset
[Since 0.8.4] - [ -P Structures/UselessUnset ] - [ Online docs ]
There are situations where trying to remove a variable is actually useless.
PHP ignores any command that tries to unset a global variable, a static variable, or a blind variable from a foreach loop.
This is different from the garbage collector, which is run on its own schedule. It is also different from an explicit unset, aimed at freeing memory early : those are useful.
<?php
function foo($a) {
// unsetting arguments is useless
unset($a);
global $b;
// unsetting global variable has no effect
unset($b);
static $c;
// unsetting static variable has no effect
unset($c);
foreach($d as &$e){
// unsetting a blind variable is useless
(unset) $e;
}
// Unsetting a blind variable AFTER the loop is good.
unset($e);
}
?>
Alternatives :
- Remove the unset
- Set the variable to null : the effect is the same on memory, but the variable keeps its existence.
- Omit unsetting variables, and wait for the end of the scope. That way, PHP free memory en mass.
Buried Assignation
[Since 0.8.4] - [ -P Structures/BuriedAssignation ] - [ Online docs ]
Those assignations are buried in the code, and placed in unexpected situations.
They are difficult to spot, and may be confusing. It is advised to place them in a more visible place.
<?php
// $b may be assigned before processing $a
$a = $c && ($b = 2);
// Display property p immeiately, but also, keeps the object for later
echo ($o = new x)->p;
// legit syntax, but the double assignation is not obvious.
for($i = 2, $j = 3; $j < 10; $j++) {
}
?>
Alternatives :
- Extract the assignation and set it on its own line, prior to the current expression.
- Check if the local variable is necessary
No array_merge() In Loops
[Since 0.8.4] - [ -P Performances/ArrayMergeInLoops ] - [ Online docs ]
array_merge() is memory intensive : every call will duplicate the arguments in memory, before merging them.
To handle arrays that may be quite big, it is recommended to avoid using array_merge() in a loop. Instead, one should use array_merge() with as many arguments as possible, making the merge a on time call.
Note that array_merge_recursive() and file_put_contents() are affected and reported the same way.
<?php
// A large multidimensional array
$source = ['a' => ['a', 'b', /*...*/],
'b' => ['b', 'c', 'd', /*...*/],
/*...*/
];
// Faster way
$b = array();
foreach($source as $key => $values) {
//Collect in an array
$b[] = $values;
}
// One call to array_merge
$b = call_user_func_array('array_merge', $b);
// or with variadic
$b = call_user_func('array_merge', ..$b);
// Fastest way (with above example, without checking nor data pulling)
$b = call_user_func_array('array_merge', array_values($source))
// or
$b = call_user_func('array_merge', ...array_values($source))
// Slow way to merge it all
$b = array();
foreach($source as $key => $values) {
$b = array_merge($b, $values);
}
?>
Alternatives :
- Store all intermediate arrays in a temporary variable, and use array_merge() once, with ellipsis or call_user_func_array().
Useless Parenthesis
[Since 0.8.4] - [ -P Structures/UselessParenthesis ] - [ Online docs ]
Situations where parenthesis are not necessary, and may be removed.
Parenthesis group several elements together, and allows for a more readable expression. They are used with logical and mathematical expressions. They are necessary when the precedence of the operators are not the intended execution order : for example, when an addition must be performed before the multiplication.
Sometimes, the parenthesis provide the same execution order than the default order : they are deemed useless, from the PHP point of view. Yet, they may add readability to the code. In special circumstances, they may also protect the code from evolution in the precedence of operators : for example, 1 + 2 . '.' . 3 + 4; has different results between PHP 8 and PHP 7.
<?php
if ( ($condition) ) {}
while( ($condition) ) {}
do $a++; while ( ($condition) );
switch ( ($a) ) {}
$y = (1);
($y) == (1);
f(($x));
// = has precedence over ==
($a = $b) == $c;
($a++);
// No need for parenthesis in default values
function foo($c = ( 1 + 2) ) {}
?>
Alternatives :
- Remove useless parenthesis, unless they are important for readability.
Shell Usage
[Since 0.8.4] - [ -P Structures/ShellUsage ] - [ Online docs ]
List of shell calls to system.
<?php
// Using backtick operator
$a = `ls -hla`;
// Using one of PHP native or extension functions
$a = shell_exec('ls -hla');
$b = \pcntl_exec('/path/to/command');
?>
See also shell_exec and Execution Operators.
File Usage
[Since 0.8.4] - [ -P Structures/FileUsage ] - [ Online docs ]
The application makes usage of files on the system (read, write, delete, etc.).
Files usage is based on the usage of file functions.
<?php
$fp = fopen('/tmp/file.txt', 'w+');
// ....
?>
See also filesystem.
Mail Usage
[Since 0.8.4] - [ -P Structures/MailUsage ] - [ Online docs ]
Report usage of mail from PHP.
The analysis is based on mail() function and various classes used to send mail.
<?php
// The message
$message = "Line 1\r\nLine 2\r\nLine 3";
// In case any of our lines are larger than 70 characters, we should use wordwrap\(\)
$message = wordwrap($message, 70, "\r\n");
// Send
mail('caffeinated@example.com', 'My Subject', $message);
?>
See also mail.
Dynamic Calls
[Since 0.8.4] - [ -P Structures/DynamicCalls ] - [ Online docs ]
This rule lists all dynamic calls. This is convenient when one has to review them manually.
Only the dynamic call is listed : the sources are left in their context.
<?php
$a = 'b';
// Dynamic call of a constant
echo constant($a);
// Dynamic variables
$$a = 2;
echo $b;
// Dynamic call of a function
$a('b');
// Dynamic call of a method
$object->$a('b');
// Dynamic call of a static method
A::$a('b');
?>
Unresolved Instanceof
[Since 0.8.4] - [ -P Classes/UnresolvedInstanceof ] - [ Online docs ]
The instanceof operator doesn't confirm if the compared class exists.
It checks if an variable is of a specific class. However, if the referenced class doesn't exist, because of a bug, a missed inclusion or a typo, the operator always fails, without a warning.
Make sure the following classes are well defined.
<?php
namespace X {
class C {}
// This is OK, as C is defined in X
if ($o instanceof C) { }
// This is not OK, as C is not defined in global
// instanceof respects namespaces and use expressions
if ($o instanceof \C) { }
// This is not OK, as undefinedClass
if ($o instanceof undefinedClass) { }
// This is not OK, as $class is now a full namespace. It actually refers to \c, which doesn't exist
$class = 'C';
if ($o instanceof $class) { }
}
?>
Alternatives :
- Remove the call to instanceof and all its dependencies.
- Fix the class name and use a class existing in the project.
Use PHP Object API
[Since 0.8.4] - [ -P Php/UseObjectApi ] - [ Online docs ]
OOP API is the modern version of the PHP API.
When PHP offers the alternative between procedural and OOP api for the same features, it is recommended to use the OOP API.
Often, this least to more compact code, as methods are shorter, and there is no need to bring the resource around. Lots of new extensions are directly written in OOP form too.
OOP / procedural alternatives are available for mysqli, tidy, cairo, finfo, and some others.
<?php
/// OOP version
$mysqli = new mysqli("localhost", "my_user", "my_password", "world");
/* check connection */
if ($mysqli->connect_errno) {
printf("Connect failed: %s\n", $mysqli->connect_error);
exit();
}
/* Create table doesn't return a resultset */
if ($mysqli->query("CREATE TEMPORARY TABLE myCity LIKE City") === TRUE) {
printf("Table myCity successfully created.\n");
}
/* Select queries return a resultset */
if ($result = $mysqli->query("SELECT Name FROM City LIMIT 10")) {
printf("Select returned %d rows.\n", $result->num_rows);
/* free result set */
$result->close();
}
?>
Alternatives :
- Use the object API
Unthrown Exception
[Since 0.8.4] - [ -P Exceptions/Unthrown ] - [ Online docs ]
These exceptions are defined in the code but never thrown. They are probably dead code.
Unused exceptions are code bloat, as they increase the size of the code without any purpose. They are also misleading, as other developers might come to the impression that there is a mechanism to handle the situation that the exception describe, yet they are generating a fatal error.
<?php
//This exception is defined but never used in the code.
class myUnusedException extends \Exception {}
//This exception is defined and used in the code.
class myUsedException extends \Exception {}
throw new myUsedException('I was called');
?>
Alternatives :
- Remove the exception
- Find a place in the code to throw the exception
- Replace an existing \Exception with this more specific one
Old Style __autoload()
[Since 0.8.4] - [ -P Php/oldAutoloadUsage ] - [ Online docs ]
Avoid __autoload(), only use spl_register_autoload().
__autoload() is deprecated since PHP 7.2 and possibly removed in later versions. spl_register_autoload() was introduced in PHP 5.1.0.
__autoload() may only be declared once, and cannot be modified later. This creates potential conflicts between libraries that try to set up their own autoloading schema.
On the other hand, spl_register_autoload() allows registering and unregistering multiple autoloading functions or methods.
Do not use the old __autoload() function, but rather the new spl_register_autoload() function.
<?php
// Modern autoloading.
function myAutoload($class){}
spl_register_autoload('myAutoload');
// Old style autoloading.
function __autoload($class){}
?>
Alternatives :
- Move to spl_register_autoload()
- Remove usage of the old __autoload() function
- Modernize usage of old libraries
Altering Foreach Without Reference
[Since 0.8.4] - [ -P Structures/AlteringForeachWithoutReference ] - [ Online docs ]
Foreach() loop that could use a reference as value.
When using a foreach loop that modifies the original source, it is recommended to use referenced variables, rather than access the original value with $source[$index].
Using references is then must faster, and easier to read.
array_walk() and array_map() are also alternative to prevent the use of foreach(), when $key is not used.
<?php
// Using references in foreach
foreach($source as $key => &$value) {
$value = newValue($value, $key);
}
// Avoid foreach : use array_map
$source = array_walk($source, 'newValue');
// Here, $key MUST be the second argument or newValue
// Slow version to update the array
foreach($source as $key => &$value) {
$source[$key] = newValue($value, $key);
}
?>
Alternatives :
- Add the reference on the modified blind variable, and avoid accessing the source array
Test Class
[Since 0.8.4] - [ -P Classes/TestClass ] - [ Online docs ]
Those are test classes, based on popular UT frameworks. Currently, the following frameworks are detected, based on the classes that are mentionned :
- PHPUnit
- Atoum
- simpletest
- drupal tests
- symfony tests
- luya
Use Pathinfo
[Since 0.8.4] - [ -P Php/UsePathinfo ] - [ Online docs ]
Use pathinfo() function instead of string manipulations.
pathinfo() is more efficient and readable and string functions.
When the path contains UTF-8 characters, pathinfo() may strip them. There, string functions are necessary.
<?php
$filename = '/path/to/file.php';
// With pathinfo();
$details = pathinfo($filename);
print $details['extension']; // also capture php
// With string functions (other solutions possible)
$ext = substr($filename, - strpos(strreverse($filename), '.')); // Capture php
?>
Alternatives :
- Use pathinfo() and its second argument
Should Use Existing Constants
[Since 0.8.4] - [ -P Functions/ShouldUseConstants ] - [ Online docs ]
The following functions have related constants that should be used as arguments, instead of scalar literals, such as integers or strings.
<?php
// The file is read and new lines are ignored.
$lines = file('file.txt', FILE_IGNORE_NEW_LINES)
// What is this doing, with 2 ?
$lines = file('file.txt', 2);
?>
Alternatives :
- Use PHP native constants whenever possible, for better readability.
Hash Algorithms
[Since 0.8.4] - [ -P Php/HashAlgos ] - [ Online docs ]
There is a long but limited list of hashing algorithm available to PHP. The one found doesn't seem to be existing.
<?php
// This hash has existed in PHP. Check with hash_algos() if it is available on your system.
echo hash('ripmed160', 'The quick brown fox jumped over the lazy dog.');
// This hash doesn't exist
echo hash('ripemd160', 'The quick brown fox jumped over the lazy dog.');
?>
Alternatives :
- Use a hash algorithm that is available on several PHP versions
- Fix the name of the hash algorithm
ext/dio
[Since 0.8.4] - [ -P Extensions/Extdio ] - [ Online docs ]
Extension DIO : Direct Input Output.
PHP supports the direct io functions as described in the Posix Standard (Section 6) for performing I/O functions at a lower level than the C-Language stream I/O functions
<?php
$fd = dio_open('/dev/ttyS0', O_RDWR | O_NOCTTY | O_NONBLOCK);
dio_close($fd);
?>
See also DIO.
No Parenthesis For Language Construct
[Since 0.8.4] - [ -P Structures/NoParenthesisForLanguageConstruct ] - [ Online docs ]
Some PHP language constructs, such are include , require , include_once , require_once , print , echo don't need parenthesis. They accept parenthesis, but it is may lead to strange situations.
It it better to avoid using parenthesis with echo , print , return , throw , yield , yield from , include , require , include_once , require_once .
<?php
// This is an attempt to load 'foo.inc', or kill the script
include('foo.inc') or die();
// in fact, this is read by PHP as : include 1
// include 'foo.inc' or die();
?>
Alternatives :
- Remove parenthesis
No Hardcoded Path
[Since 0.8.4] - [ -P Structures/NoHardcodedPath ] - [ Online docs ]
It is not recommended to use hardcoded literals when designating files. Full paths are usually tied to one file system organization. As soon as the organisation changes or must be adapted to any external constraint, the path is not valid anymore.
Either use __FILE__ and __DIR__ to make the path relative to the current file; use a DOC_ROOT as a configuration constant that will allow the moving of the script to another folder; finally functions like sys_get_temp_dir() produce a viable temporary folder.
Relative paths are relative to the current execution directory, and not the current file. This means they may differ depending on the location of the start of the application, and are sensitive to chdir() and chroot() usage.
<?php
// This depends on the current executed script
file_get_contents('token.txt');
// Exotic protocols are ignored
file_get_contents('jackalope://file.txt');
// Some protocols are ignored : http, https, ftp, ssh2, php (with memory)
file_get_contents('http://www.php.net/');
file_get_contents('php://memory/');
// glob() with special chars * and ? are not reported
glob('./*/foo/bar?.txt');
// glob() without special chars * and ? are reported
glob('/foo/bar/');
?>
Alternatives :
- Add __DIR__ before the path to make it relative to the current file
- Add a configured prefix before the path to point to any file in the system
- Use sys_get_temp_dir() for temporary data
- Use include_path argument function, such as fie_get_contents(), to have the file located in configurable directories.
No Hardcoded Port
[Since 0.8.4] - [ -P Structures/NoHardcodedPort ] - [ Online docs ]
When connecting to a remove server, port is an important information. It is recommended to make this configurable (with constant or configuration), to as to be able to change this value without changing the code.
<?php
// Both configurable IP and hostname
$connection = ssh2_connect($_ENV['SSH_HOST'], $_ENV['SSH_PORT'], $methods, $callbacks);
// Both hardcoded IP and hostname
$connection = ssh2_connect('shell.example.com', 22, $methods, $callbacks);
if (!$connection) die('Connection failed');
?>
Alternatives :
- Move the port to a configuration file, an environment variable
ext/phalcon
[Since 0.8.4] - [ -P Extensions/Extphalcon ] - [ Online docs ]
Extension Phalcon : High Performance PHP Framework.
Phalcon's autoload examples from the docs : Tutorial 1: Let’s learn by example
<?php
use Phalcon\Loader;
// ...
$loader = new Loader();
$loader->registerDirs(
[
../app/controllers/,
../app/models/,
]
);
$loader->register();
?>
See also PhalconPHP.
Use Constant As Arguments
[Since 0.8.4] - [ -P Functions/UseConstantAsArguments ] - [ Online docs ]
Some methods and functions are defined to be used with constants as arguments. Those constants are made to be meaningful and readable, keeping the code maintenable. It is recommended to use such constants as soon as they are documented.
Here is the list of functions that use a unique PHP constant as argument :
- array_change_key_case()
- array_multisort()
- array_unique()
- count()
- dns_get_record()
- easter_days()
- extract()
- filter_input()
- filter_var()
- fseek()
- get_html_translation_table()
- gmp_div_q()
- gmp_div_qr()
- gmp_div_r()
- html_entity_decode()
- htmlspecialchars_decode()
- http_build_query()
- http_parse_cookie()
- http_parse_params()
- http_redirect()
- http_support()
- parse_ini_file()
- parse_ini_string()
- parse_url()
- pathinfo()
- pg_select()
- posix_access()
- round()
- scandir()
- socket_read()
- str_pad()
- trigger_error()
- arsort()
- asort()
- error_reporting()
- filter_input()
- filter_var()
- get_html_translation_table()
- htmlentities()
- htmlspecialchars()
- http_build_url()
- jdtojewish()
- krsort()
- ksort()
- pg_result_status()
- phpcredits()
- phpinfo()
- preg_grep()
- preg_match()
- preg_split()
- rsort()
- runkit_import()
- sort()
- stream_socket_client()
- stream_socket_server()
Here is the list of functions that use a combination of PHP native constants as argument.
<?php
// Turn off all error reporting
// 0 and -1 are accepted
error_reporting(0);
// Report simple running errors
error_reporting(E_ERROR | E_WARNING | E_PARSE);
// The first argument can be one of INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV.
$search_html = filter_input(INPUT_GET, 'search', FILTER_SANITIZE_SPECIAL_CHARS);
// sort accepts one of SORT_REGULAR, SORT_NUMERIC, SORT_STRING, SORT_LOCALE_STRING, SORT_NATURAL
// SORT_FLAG_CASE may be added, and combined with SORT_STRING or SORT_NATURAL
sort($fruits);
?>
Alternatives :
- Use PHP native constants, whenever possible, instead of nondescript literals.
Implied If
[Since 0.8.4] - [ -P Structures/ImpliedIf ] - [ Online docs ]
It is confusing to emulate if/then with boolean operators.
It is possible to emulate a if/then structure by using the operators 'and' and 'or'. Since optimizations will be applied to them :
when the left operand of 'and' is false, the right one is not executed, as its result is useless;
when the left operand of 'or' is true, the right one is not executed, as its result is useless;
However, such structures are confusing. It is easy to misread them as conditions, and ignore an important logic step.
It is recommended to use a real 'if then' structures, to make the condition readable.
<?php
// Either connect, or die
mysql_connect('localhost', $user, $pass) or die();
// Defines a constant if not found.
defined('SOME_CONSTANT') and define('SOME_CONSTANT', 1);
// Defines a default value if provided is empty-ish
// Warning : this is
$user = $_GET['user'] || 'anonymous';
?>
Alternatives :
- Replace this expression by an explicit if-then structure
Overwritten Literals
[Since 0.8.4] - [ -P Variables/OverwrittenLiterals ] - [ Online docs ]
The same variable is assigned a literal twice. It is possible that one of the assignation is too many.
This analysis doesn't take into account the distance between two assignations : it may report false positives when the variable is actually used for several purposes, and, as such, assigned twice with different values.
<?php
function foo() {
// Two assignations in a short sequence : one is too many.
$a = 1;
$a = 2;
for($i = 0; $i < 10; $i++) {
$a += $i;
}
$b = $a;
// New assignation. $a is now used as an array.
$a = array(0);
}
?>
Alternatives :
- Remove one of the assignation (the earliest)
Assign Default To Properties
[Since 0.8.4] - [ -P Classes/MakeDefault ] - [ Online docs ]
Properties may be assigned default values at declaration time. Such values may be later modified, if needed.
Default values will save some instructions in the constructor, and makes the value obvious in the code.
<?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();
}
}
?>
Alternatives :
- Add a default value whenever possible. This is easy for scalars, and array()
No Public Access
[Since 0.8.4] - [ -P Classes/NoPublicAccess ] - [ Online docs ]
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();
?>
Composer Usage
[Since 0.8.4] - [ -P Composer/UseComposer ] - [ Online docs ]
Mark the usage of composer, mostly by having a composer.json file.
Composer's autoload
[Since 0.8.4] - [ -P Composer/Autoload ] - [ Online docs ]
Report if this code is using the autoload from Composer.
Should Chain Exception
[Since 0.8.4] - [ -P Structures/ShouldChainException ] - [ Online docs ]
Chain exception to provide more context.
When catching an exception and rethrowing another one, it is recommended to chain the exception : this means providing the original exception, so that the final recipient has a chance to track the origin of the problem. This doesn't change the thrown message, but provides more information.
Note : Chaining requires PHP > 5.3.0.
<?php
try {
throw new Exception('Exception 1', 1);
} catch (\Exception $e) {
throw new Exception('Exception 2', 2, $e);
// Chaining here.
}
?>
Alternatives :
- Add the incoming exception to the newly thrown exception
Useless Interfaces
[Since 0.8.4] - [ -P Interfaces/UselessInterfaces ] - [ Online docs ]
The interfaces below are defined and are implemented by some classes.
However, they are never used to enforce an object's class in the code, using instanceof or in a type.
As they are currently used, those interfaces may be removed without change in behavior.
Interfaces should be used in type or with the instanceof operator.
<?php
// only defined interface but never enforced
interface i {};
class c implements i {}
?>
Alternatives :
- Use the interface with instanceof, or a type
- Drop the interface altogether : both definition and implements keyword
Undefined Interfaces
[Since 0.8.4] - [ -P Interfaces/UndefinedInterfaces ] - [ Online docs ]
Some typehints or instanceof that are relying on undefined interfaces or classes. They will always return false. Any condition based upon them are dead code.
<?php
class var implements undefinedInterface {
// If undefinedInterface is undefined, this code lints but doesn't run
}
if ($o instanceof undefinedInterface) {
// This is silent dead code
}
function foo(undefinedInterface $a) {
// This is dead code
// it will probably be discovered at execution
}
?>
Alternatives :
- Implement the missing interfaces
- Remove the code governed by the missing interface : the whole method if it is an typehint, the whole if/then if it is a condition.
ext/apcu
[Since 0.8.4] - [ -P Extensions/Extapcu ] - [ Online docs ]
Extension APCU .
APCu is APC stripped of opcode caching. The Alternative PHP Cache (APC) is a free and open opcode cache for PHP. Its goal is to provide a free, open, and robust framework for caching and optimizing PHP intermediate code.
<?php
$bar = 'BAR';
apcu_add('foo', $bar);
var_dump(apcu_fetch('foo'));
echo "\n";
$bar = 'NEVER GETS SET';
apcu_add('foo', $bar);
var_dump(apcu_fetch('foo'));
echo "\n";
?>
See also APCU, ext/apcu and krakjoe/apcu.
Double Instructions
[Since 0.8.4] - [ -P Structures/DoubleInstruction ] - [ Online docs ]
Twice the same call in a row. This might be a typo, and the second call is useless.
It may also be an non-idempotent method: that is, a method which has a different result when called with the same arguments. For example, rand()
<?php
// repetition of the same command, with the same effect each time.
$a = array_merge($b, $c);
$a = array_merge($b, $c);
// false positive : commands are identical, but the effect is compounded
$a = array_merge($a, $c);
$a = array_merge($a, $c);
?>
Alternatives :
- Remove double work
- Avoid repetition by using loops, variadic or quantifiers `(dirname($path, 2))`
Should Use Prepared Statement
[Since 0.8.4] - [ -P Security/ShouldUsePreparedStatement ] - [ Online docs ]
Modern databases provides support for prepared statement : it separates the query from the processed data and raise significantly the security.
Building queries with concatenations is not recommended, though not always avoidable. When possible, use prepared statements.
Same code, without preparation :
<?php
/* Execute a prepared statement by passing an array of values */
$sql = 'SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour';
$sth = $conn->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$sth->execute(array(':calories' => 150, ':colour' => 'red'));
$red = $sth->fetchAll();
?>
Alternatives :
- Use an ORM
- Use an Active Record library
- Change the query to hard code it and make it not injectable
Print And Die
[Since 0.8.4] - [ -P Structures/PrintAndDie ] - [ Online docs ]
Die() also prints.
When stopping a script with die(), it is possible to provide a message as first argument, that will be displayed at execution. There is no need to make a specific call to print or echo.
<?php
// die may do both print and die.
echo 'Error message';
die();
// exit may do both print and die.
print 'Error message';
exit;
// exit cannot print integers only : they will be used as status report to the system.
print 'Error message';
exit 1;
?>
Unchecked Resources
[Since 0.8.4] - [ -P Structures/UncheckedResources ] - [ Online docs ]
Resources are created, but never checked before being used. This is not safe.
Always check that resources are correctly created before using them.
<?php
// always check that the resource is created correctly
$fp = fopen($d,'r');
if ($fp === false) {
throw new Exception('File not found');
}
$firstLine = fread($fp);
// This directory is not checked : the path may not exist and return false
$uncheckedDir = opendir($pathToDir);
while(readdir($uncheckedDir)) {
// do something()
}
// This file is not checked : the path may not exist or be unreadable and return false
$fp = fopen($pathToFile);
while($line = freads($fp)) {
$text .= $line;
}
// unsafe one-liner : using bzclose on an unchecked resource
bzclose(bzopen('file'));
?>
Alternatives :
- Add a check between the resource acquisition and its usage
ext/trader
[Since 0.8.4] - [ -P Extensions/Exttrader ] - [ Online docs ]
Extension trader.
The trader extension is a free open source stock library based on TA-Lib. It's dedicated to trading software developers requiring to perform technical analysis of financial market data.
<?php
// get_data() reads the data from a source
var_dump(trader_avgprice(
get_data("open", $data0),
get_data("high", $data0),
get_data("low", $data0),
get_data("close", $data0)
));
?>
See also trader (PECL), 'TA-lib ext/mailparse
[Since 0.8.4] - [ -P Extensions/Extmailparse ] - [ Online docs ]
Extension mailparse.
Mailparse is an extension for parsing and working with email messages. It can deal with RFC 822 (MIME) and RFC 2045 (MIME) compliant messages.
<?php
$mail = mailparse_msg_create();
mailparse_msg_parse($mail, $mailInString);
$parts = mailparse_msg_get_structure($mail);
foreach($parts as $part) {
$section = mailparse_msg_get_part($mail, $part);
$info = mailparse_msg_get_part_data($section);
}
?>
See also Mailparse.
ext/mail
[Since 0.8.4] - [ -P Extensions/Extmail ] - [ Online docs ]
Extension for mail.
The mail() function allows you to send mail.
<?php
// The message
$message = "Line 1\r\nLine 2\r\nLine 3";
// In case any of our lines are larger than 70 characters, we should use wordwrap()
$message = wordwrap($message, 70, "\r\n");
// Send
mail('caffeinated@example.com', 'My Subject', $message);
?>
See also Mail related functions.
No Hardcoded Ip
[Since 0.8.4] - [ -P Structures/NoHardcodedIp ] - [ Online docs ]
Do not leave hard coded IP in your code.
It is recommended to move such configuration in external files or databases, for each update.
This may also come handy when testing.
127.0.0.1 , ::1 and ::0 are omitted, and not considered as a violation.
<?php
// This IPv4 is hardcoded.
$ip = '183.207.224.50';
// This IPv6 is hardcoded.
$ip = '2001:0db8:85a3:0000:0000:8a2e:0370:7334';
// This looks like an IP
$thisIsNotAnIP = '213.187.99.50';
$thisIsNotAnIP = '2133:1387:9393:5330';
?>
Alternatives :
- Move the hardcoded IP to an external source : environment variable, configuration file, database.
- Remove the hardcoded IP and ask for it at execution.
- Use a literal value for default messages in form.
Else If Versus Elseif
[Since 0.8.4] - [ -P Structures/ElseIfElseif ] - [ Online docs ]
Always use elseif instead of else and if.
The keyword elseif SHOULD be used instead of else if so that all control keywords look like single words". Quoted from the PHP-FIG documentation
<?php
// Using elseif
if ($a == 1) { doSomething(); }
elseif ($a == 2) { doSomethingElseIf(); }
else { doSomethingElse(); }
// Using else if
if ($a == 1) { doSomething(); }
else if ($a == 2) { doSomethingElseIf(); }
else { doSomethingElse(); }
// Using else if, no {}
if ($a == 1) doSomething();
else if ($a == 2) doSomethingElseIf();
else doSomethingElse();
?>
Alternatives :
- Merge else and if into elseif
- Turn the else expression into a block, and have more than the second if in this block
- Turn the if / else if / else into a switch structure
Unset In Foreach
[Since 0.8.4] - [ -P Structures/UnsetInForeach ] - [ Online docs ]
Unset applied to the variables of a foreach loop are useless. Those variables are copies and not the actual value. Even if the value is a reference, unsetting it has no effect on the original array : the only effect may be indirect, on elements inside an array, or on properties inside an object.
<?php
// When unset is useless
$array = [1, 2, 3];
foreach($array as $a) {
unset($a);
}
print_r($array); // still [1, 2, 3]
foreach($array as $b => &$a) {
unset($a);
}
print_r($array); // still [1, 2, 3]
// When unset is useful
$array = [ [ 'c' => 1] ]; // Array in array
foreach($array as &$a) {
unset(&$a['c']);
}
print_r($array); // now [ ['c' => null] ]
?>
Alternatives :
- Drop the unset
Could Be A Static Variable
[Since 0.8.4] - [ -P Structures/CouldBeStatic ] - [ Online docs ]
This global is only used in one function or method. It may be transformed into a 'static' variable, instead of global. This allows you to keep the value between call to the function, but will not be accessible outside this function.
<?php
function foo( ) {
static $variableIsReservedForX; // only accessible within foo( ), even between calls.
global $variableIsGlobal; // accessible everywhere in the application
}
?>
Multiple Class Declarations
[Since 0.8.4] - [ -P Classes/MultipleDeclarations ] - [ Online docs ]
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.
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.
<?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();
?>
Alternatives :
- 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
Empty Namespace
[Since 0.8.4] - [ -P Namespaces/EmptyNamespace ] - [ Online docs ]
Declaring a namespace in the code and not using it for structure declarations or global instructions is useless.
Using simple style :
Using bracket-style syntax :
<?php
namespace Y;
class foo {}
namespace X;
// This is useless
?>
Alternatives :
- Remove the namespace
- Fill the namespace with some definition
Could Use Short Assignation
[Since 0.8.4] - [ -P Structures/CouldUseShortAssignation ] - [ Online docs ]
Use short assignment operator, to speed up code, and keep syntax clear.
Some operators, like * or +, have a compact and fast 'do-and-assign' version. They looks like a compacted version for = and the operator. This syntax is good for readability, and saves some memory in the process.
Depending on the operator, not all permutations of arguments are possible. For example, $a = $a - 2 can use the -= short operator, but $a = 2 - $a doesn't.
Addition and short assignation of addition have a different set of features when applied to arrays. Do not exchange one another in that case.
Short operators are faster than the extended version, though it is a micro-optimization.
<?php
$a = 10 + $a;
$a += 10;
$b = $b - 1;
$b -= 1;
$c = $c * 2;
$c *= 2;
$d = $d / 3;
$d /= 3;
$e = $e % 4;
$e %= 4;
$f = $f | 5;
$f |= 5;
$g = $g & 6;
$g &= 6;
$h = $h ^ 7;
$h ^= 7;
$i = $i >> 8;
$i >>= 8;
$j = $j << 9;
$j <<= 9;
// PHP 7.4 and more recent
$l = $l ?? 'value';
$l ??= 'value';
?>
Alternatives :
- Change the expression to use the short assignation
Useless Abstract Class
[Since 0.8.4] - [ -P Classes/UselessAbstract ] - [ Online docs ]
Those classes are marked 'abstract' and they are never extended. This way, they won't be instantiated nor used.
Abstract classes that have only static methods are omitted here : one usage of such classes are Utilities classes, which only offer static methods.
<?php
// Never extended class : this is useless
abstract class foo {}
// Extended class
abstract class bar {
public function barbar() {}
}
class bar2 extends bar {}
// Utility class : omitted here
abstract class bar {
public static function barbar() {}
}
?>
Alternatives :
- Drop the abstract keyword
- Extends the abstract class, more than once
- If the class is extended, merge the class in the child
Scalar Typehint Usage
[Since 0.8.4] - [ -P Php/ScalarTypehintUsage ] - [ Online docs ]
Spot usage of scalar type hint : int , float , boolean and string .
Scalar typehint are PHP 7.0 and more recent. Some, like object , is 7.2.
Scalar typehint were not supported in PHP 5 and older. Then, the typehint is treated as a class name.
<?php
function withScalarTypehint(string $x) {}
function withoutScalarTypehint(someClass $x) {}
?>
See also PHP RFC: Scalar Type Hints and Type declarations.
Return Typehint Usage
[Since 0.8.4] - [ -P Php/ReturnTypehintUsage ] - [ Online docs ]
Spot usage of return typehint. It is a PHP 7.0 feature.
Return typehint were introduced in PHP 7.0, and are backward incompatible with PHP 5.x.
<?php
function foo($a) : stdClass {
return new \stdClass();
}
?>
See also RFC: Return Type Declarations and Return Type Declarations.
ext/ob
[Since 0.8.4] - [ -P Extensions/Extob ] - [ Online docs ]
Extension Output Buffering Control.
The Output Control functions allow you to control when output is sent from the script.
<?php
ob_start();
echo "Hello\n";
setcookie("cookiename", "cookiedata");
ob_end_flush();
?>
See also Output Buffering Control.
Static Loop
[Since 0.8.4] - [ -P Structures/StaticLoop ] - [ Online docs ]
Static loop may be preprocessed.
It looks like the following loops are static : the same code is executed each time, without taking into account loop variables.
It is possible to create loops that don't use any blind variables, though this is fairly rare. In particular, calling a method may update an internal pointer, like next() or SimpleXMLIterator::next() .
It is recommended to turn a static loop into an expression that avoid the loop. For example, replacing the sum of all integers by the function $n * ($n + 1) / 2 , or using array_sum().
This analysis doesn't detect usage of variables with compact .
<?php
// Static loop
$total = 0;
for($i = 0; $i < 10; $i++) {
$total += $i;
}
// The above loop may be replaced by (with some math help)
$total = 10 * (10 + 1) / 2;
// Non-Static loop (the loop depends on the size of the array)
$n = count($array);
for($i = 0; $i < $n; $i++) {
$total += $i;
}
?>
Alternatives :
- Precalculate the result of that loop and removes it altogether
- Check that the loop is not missing a blind variable usage
- Replace the usage of a loop with a native PHP call : for example, with str_repeat(). Although the loop is still here, it usually reflects better the intend.
Pre-increment
[Since 0.8.4] - [ -P Performances/PrePostIncrement ] - [ Online docs ]
When possible, use the pre-increment operator ( ++$i or --$i ) instead of the post-increment operator ( $i++ or $i-- ).
The latter needs an extra memory allocation that costs about 10% of performances.
This is a micro-optimisation. However, its usage is so widespread, including within loops, that it may eventually have an significant impact on execution time. As such, it is recommended to adopt this rule, and only consider changing legacy code as they are refactored for other reasons.
<?php
// ++$i should be preferred over $i++, as current value is not important
for($i = 0; $i <10; ++$i) {
// do Something
}
// ++$b and $b++ have different impact here, since $a will collect $b + 1 or $b, respectively.
$a = $b++;
?>
Alternatives :
- Use the pre increment when the new value is not reused.
Only Variable Returned By Reference
[Since 0.8.4] - [ -P Structures/OnlyVariableReturnedByReference ] - [ Online docs ]
Function can't return literals by reference.
When a function returns a reference, it is only possible to return variables, properties or static properties.
Anything else, like literals or static expressions, yield a warning at execution time.
<?php
// Can't return a literal number
function &foo() {
return 3 + rand();
}
// bar must return values that are stored in a
function &bar() {
$a = 3 + rand();
return $a;
}
?>
ext/geoip
[Since 0.8.4] - [ -P Extensions/Extgeoip ] - [ Online docs ]
Extension geoip for PHP.
The GeoIP extension allows the localisation of an IP address.
<?php
$org = geoip_org_by_name('www.example.com');
if ($org) {
echo 'This host IP is allocated to: ' . $org;
}
?>
See also GeoIP.
ext/event
[Since 0.8.4] - [ -P Extensions/Extevent ] - [ Online docs ]
Extension event.
This is an extension to efficiently schedule I/O, time and signal based events using the best I/O notification mechanism available for specific platform. This is a port of libevent to the PHP infrastructure.
<?php
// Read callback
function readcb($bev, $base) {
//$input = $bev->input; //$bev->getInput();
//$pos = $input->search('TTP');
$pos = $bev->input->search('TTP');
while (($n = $bev->input->remove($buf, 1024)) > 0) {
echo $buf;
}
}
// Event callback
function eventcb($bev, $events, $base) {
if ($events & EventBufferEvent::CONNECTED) {
echo 'Connected.';
} elseif ($events & (EventBufferEvent::ERROR | EventBufferEvent::EOF)) {
if ($events & EventBufferEvent::ERROR) {
echo 'DNS error: ', $bev->getDnsErrorString(), PHP_EOL;
}
echo 'Closing'.PHP_EOL;
$base->exit();
exit('Done'.PHP_EOL);
}
}
if ($argc != 3) {
echo <<<EOS
Trivial HTTP 0.x client
Syntax: php {$argv[0]} [hostname] [resource]
Example: php {$argv[0]} www.google.com /
EOS;
exit();
}
$base = new EventBase();
$dns_base = new EventDnsBase($base, TRUE); // We'll use async DNS resolving
if (!$dns_base) {
exit('Failed to init DNS Base'.PHP_EOL);
}
$bev = new EventBufferEvent($base, /* use internal socket */ NULL,
EventBufferEvent::OPT_CLOSE_ON_FREE | EventBufferEvent::OPT_DEFER_CALLBACKS,
'readcb', /* writecb */ NULL, 'eventcb'
);
if (!$bev) {
exit('Failed creating bufferevent socket'.PHP_EOL);
}
//$bev->setCallbacks('readcb', /* writecb */ NULL, 'eventcb', $base);
$bev->enable(Event::READ | Event::WRITE);
$output = $bev->output; //$bev->getOutput();
if (!$output->add(
'GET '.$argv[2].' HTTP/1.0'."\r\n".
'Host: '.$argv[1]."\r\n".
'Connection: Close'."\r\n\r\n"
)) {
exit('Failed adding request to output buffer\n');
}
if (!$bev->connectHost($dns_base, $argv[1], 80, EventUtil::AF_UNSPEC)) {
exit('Can\'t connect to host '.$argv[1].PHP_EOL);
}
$base->dispatch();
?>
See also Event and libevent.
ext/amqp
[Since 0.8.4] - [ -P Extensions/Extamqp ] - [ Online docs ]
Extension amqp .
PHP AMQP Binding Library`. This is an interface with the `RabbitMQ AMQP client library. It is a C language AMQP client library for use with version 2.0 and more recent of the RabbitMQ broker.
<?php
$cnn = new AMQPConnection();
$cnn->connect();
echo 'Used channels: ', $cnn->getUsedChannels(), PHP_EOL;
$ch = new AMQPChannel($cnn);
echo 'Used channels: ', $cnn->getUsedChannels(), PHP_EOL;
$ch = new AMQPChannel($cnn);
echo 'Used channels: ', $cnn->getUsedChannels(), PHP_EOL;
$ch = null;
echo 'Used channels: ', $cnn->getUsedChannels(), PHP_EOL;
?>
See also PHP AMQP Binding Library.
ext/gearman
[Since 0.8.4] - [ -P Extensions/Extgearman ] - [ Online docs ]
Extension Gearman.
Gearman is a generic application framework for farming out work to multiple machines or processes.
<?php
# Create our client object.
$gmclient= new GearmanClient();
# Add default server (localhost).
$gmclient->addServer();
echo 'Sending job'.PHP_EOL;
# Send reverse job
do
{
$result = $gmclient->doNormal('reverse', 'Hello!');
# Check for various return packets and errors.
switch($gmclient->returnCode())
{
case GEARMAN_WORK_DATA:
echo 'Data: '.$result . PHP_EOL;;
break;
case GEARMAN_WORK_STATUS:
list($numerator, $denominator)= $gmclient->doStatus();
echo 'Status: '.$numerator.'/'.$denominator.' complete'. PHP_EOL;
break;
case GEARMAN_WORK_FAIL:
echo 'Failed\n';
exit;
case GEARMAN_SUCCESS:
echo 'Success: $result\n';
break;
default:
echo 'RET: ' . $gmclient->returnCode() . PHP_EOL;
exit;
}
}
while($gmclient->returnCode() != GEARMAN_SUCCESS);
?>
See also Gearman on PHP and Gearman.
ext/com
[Since 0.8.4] - [ -P Extensions/Extcom ] - [ Online docs ]
Extension COM and .Net (Windows).
COM is an acronym for 'Component Object Model'; it is an object orientated layer (and associated services) on top of DCE RPC (an open standard) and defines a common calling convention that enables code written in any language to call and interoperate with code written in any other language (provided those languages are COM aware).
<?php
$domainObject = new COM("WinNT://Domain");
foreach ($domainObject as $obj) {
echo $obj->Name . "<br />";
}
?>
See also COM and .Net (Windows).
ext/gmagick
[Since 0.8.4] - [ -P Extensions/Extgmagick ] - [ Online docs ]
Extension gmagick.
Gmagick is a php extension to create, modify and obtain meta information of images using the GraphicsMagick API.
<?php
//Instantiate a new Gmagick object
$image = new Gmagick('example.jpg');
//Make thumbnail from image loaded. 0 for either axes preserves aspect ratio
$image->thumbnailImage(100, 0);
//Create a border around the image, then simulate how the image will look like as an oil painting
//Note the chaining of mutator methods which is supported in gmagick
$image->borderImage("yellow", 8, 8)->oilPaintImage(0.3);
//Write the current image at the current state to a file
$image->write('example_thumbnail.jpg');
?>
See also PHP gmagick and gmagick.
ext/ibase
[Since 0.8.4] - [ -P Extensions/Extibase ] - [ Online docs ]
Extensions Interbase and Firebird .
Firebird is a relational database offering many ISO SQL-2003 features that runs on Linux, Windows, and a variety of Unix platforms.
<?php
$host = 'localhost:/path/to/your.gdb';
$dbh = ibase_connect($host, $username, $password);
$stmt = 'SELECT * FROM tblname';
$sth = ibase_query($dbh, $stmt) or die(ibase_errmsg());
?>
See also Firebase / Interbase and Firebird.
ext/inotify
[Since 0.8.4] - [ -P Extensions/Extinotify ] - [ Online docs ]
Extension inotify.
The Inotify extension gives access to the Linux kernel subsystem that acts to extend filesystems to notice changes to the filesystem, and report those changes to applications.
<?php
// Open an inotify instance
$fd = inotify_init();
// Watch __FILE__ for metadata changes (e.g. mtime)
$watch_descriptor = inotify_add_watch($fd, __FILE__, IN_ATTRIB);
// generate an event
touch(__FILE__);
// Read events
$events = inotify_read($fd);
print_r($events);
// The following methods allows to use inotify functions without blocking on inotify_read():
// - Using stream_select() on $fd:
$read = array($fd);
$write = null;
$except = null;
stream_select($read,$write,$except,0);
// - Using stream_set_blocking() on $fd
stream_set_blocking($fd, 0);
inotify_read($fd); // Does no block, and return false if no events are pending
// - Using inotify_queue_len() to check if event queue is not empty
$queue_len = inotify_queue_len($fd); // If > 0, inotify_read() will not block
// Stop watching __FILE__ for metadata changes
inotify_rm_watch($fd, $watch_descriptor);
// Close the inotify instance
// This may have closed all watches if this was not already done
fclose($fd);
?>
See also ext/inotify manual and inotify.
ext/xdiff
[Since 0.8.4] - [ -P Extensions/Extxdiff ] - [ Online docs ]
Extension xdiff.
xdiff extension enables you to create and apply patch files containing differences between different revisions of files.
<?php
$old_version = 'my_script-1.0.php';
$patch = 'my_script.patch';
$errors = xdiff_file_patch($old_version, $patch, 'my_script-1.1.php');
if (is_string($errors)) {
echo 'Rejects:'.PHP_EOL;
echo $errors;
}
?>
See also libxdiff.
ext/ev
[Since 0.8.4] - [ -P Extensions/Extev ] - [ Online docs ]
Extension ev.
ext/ev is a high performance full-featured event loop written in C.
<?php
// Create and start timer firing after 2 seconds
$w1 = new EvTimer(2, 0, function () {
echo '2 seconds elapsed'.PHP_EOL;
});
// Create and launch timer firing after 2 seconds repeating each second
// until we manually stop it
$w2 = new EvTimer(2, 1, function ($w) {
echo 'is called every second, is launched after 2 seconds'.PHP_EOL;
echo 'iteration = ', Ev::iteration(), PHP_EOL;
// Stop the watcher after 5 iterations
Ev::iteration() == 5 and $w->stop();
// Stop the watcher if further calls cause more than 10 iterations
Ev::iteration() >= 10 and $w->stop();
});
// Create stopped timer. It will be inactive until we start it ourselves
$w_stopped = EvTimer::createStopped(10, 5, function($w) {
echo 'Callback of a timer created as stopped'.PHP_EOL;
// Stop the watcher after 2 iterations
Ev::iteration() >= 2 and $w->stop();
});
// Loop until Ev::stop() is called or all of watchers stop
Ev::run();
// Start and look if it works
$w_stopped->start();
echo 'Run single iteration'.PHP_EOL;
Ev::run(Ev::RUN_ONCE);
echo 'Restart the second watcher and try to handle the same events, but don\'t block'.PHP_EOL;
$w2->again();
Ev::run(Ev::RUN_NOWAIT);
$w = new EvTimer(10, 0, function() {});
echo 'Running a blocking loop'.PHP_EOL;
Ev::run();
echo 'END'.PHP_EOL;
?>
See also Ev and libev.
ext/php-ast
[Since 0.8.4] - [ -P Extensions/Extast ] - [ Online docs ]
PHP-AST extension (PHP 7.0 +).
<?php
$code = <<<'EOC'
<?php
$var = 42;
EOC;
var_dump(ast\parse_code($code, $version=50));
?>
See also ext/ast, Extension exposing PHP 7 abstract syntax tree and Introduction of PHP parse and its application in hyperf.
ext/xml
[Since 0.8.4] - [ -P Extensions/Extxml ] - [ Online docs ]
Extension xml (Parser).
This PHP extension implements support for James Clark's expat in PHP. This toolkit lets you parse, but not validate, XML documents.
<?php
$file = "data.xml";
$depth = array();
function startElement($parser, $name, $attrs)
{
global $depth;
if (!isset($depth[$parser])) {
$depth[$parser] = 0;
}
for ($i = 0; $i < $depth[$parser]; $i++) {
echo " ";
}
echo "$name\n";
$depth[$parser]++;
}
function endElement($parser, $name)
{
global $depth;
$depth[$parser]--;
}
$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
if (!($fp = fopen($file, "r"))) {
die("could not open XML input");
}
while ($data = fread($fp, 4096)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
}
xml_parser_free($xml_parser);
?>
See also XML Parser.
ext/xhprof
[Since 0.8.4] - [ -P Extensions/Extxhprof ] - [ Online docs ]
Extension xhprof.
XHProf is a light-weight hierarchical and instrumentation based profiler.
<?php
xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);
for ($i = 0; $i <= 1000; $i++) {
$a = $i * $i;
}
$xhprof_data = xhprof_disable();
$XHPROF_ROOT = '/tools/xhprof/';
include_once $XHPROF_ROOT . '/xhprof_lib/utils/xhprof_lib.php';
include_once $XHPROF_ROOT . '/xhprof_lib/utils/xhprof_runs.php';
$xhprof_runs = new XHProfRuns_Default();
$run_id = $xhprof_runs->save_run($xhprof_data, 'xhprof_testing');
echo 'http://localhost/xhprof/xhprof_html/index.php?run={$run_id}&source=xhprof_testing'.PHP_EOL;
?>
See also XHprof Documentation and ext/apcu.
Indices Are Int Or String
[Since 0.8.4] - [ -P Structures/IndicesAreIntOrString ] - [ Online docs ]
Indices in an array notation such as $array['indice'] may only be integers or string.
Boolean, Null or float will be converted to their integer or string equivalent.
Decimal numbers are rounded to the closest integer; Null is transtyped to '' (empty string); true is 1 and false is 0; Integers in strings are transtyped, while partial numbers or decimals are not analyzed in strings.
As a general rule of thumb, only use integers or strings that don\'t look like integers.
This analyzer may find constant definitions, when available.
Note also that PHP detects integer inside strings, and silently turn them into integers. Partial and octal numbers are not transformed.
<?php
$a = [true => 1,
1.0 => 2,
1.2 => 3,
1 => 4,
'1' => 5,
0.8 => 6,
0x1 => 7,
01 => 8,
null => 1,
'' => 2,
false => 1,
0 => 2,
'0.8' => 3,
'01' => 4,
'2a' => 5
];
print_r($a);
/*
The above displays
Array
(
[1] => 8
[0] => 2
[] => 2
[0.8] => 3
[01] => 4
[2a] => 5
)
*/
?>
Alternatives :
- Do not use any type but string or integer
- Force typecast the keys when building an array
Should Typecast
[Since 0.8.4] - [ -P Type/ShouldTypecast ] - [ Online docs ]
When typecasting, it is better to use the casting operator, such as (int) or (bool) .
Functions such as intval() or settype() are always slower.
This is a micro-optimisation, although such conversion may be use multiple times, leading to a larger performance increase.
Note that intval() may also be used to convert an integer to another base. Intval() called with 2 arguments are skipped by this rule.
<?php
// Fast version
$int = (int) $X;
// Slow version
$int = intval($X);
// Convert to base 8 : can't use (int) for that
$int = intval($X, 8);
?>
Alternatives :
- Use a typecast, instead of a functioncall.
No Self Referencing Constant
[Since 0.8.4] - [ -P Classes/NoSelfReferencingConstant ] - [ Online docs ]
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-referencing constant 'self::C2' . Unlike PHP which is self-referencing, self referencing variables can't have a value : just don't use that.
The code may access an already declared constant with self or with its class name.
This error is not detected by linting. It is only detected at instantiation time : if the class is not used, it won't appear.
<?php
class a {
const C1 = 1; // fully defined constant
const C2 = self::C2; // self referencing constant
const C3 = a::C3 + 2; // self referencing constant
}
?>
Alternatives :
- Give a literal value to this constant
- Give a constant value to this constant : other class constants or constant are allowed here.
No Direct Usage
[Since 0.8.4] - [ -P Structures/NoDirectUsage ] - [ Online docs ]
The results of the following functions shouldn't be used directly, but checked first.
For example, glob() returns an array, unless some error happens, in which case it returns a boolean (false). In such case, however rare it is, plugging glob() directly in a foreach() loops will yield errors.
<?php
// Used without check :
foreach(glob('.') as $file) { /* do Something */ }.
// Used without check :
$files = glob('.');
if (!is_array($files)) {
foreach($files as $file) { /* do Something */ }.
}
?>
Alternatives :
- Check the return of the function before using it, in particular for false, or array().
Break Outside Loop
[Since 0.8.4] - [ -P Structures/BreakOutsideLoop ] - [ Online docs ]
Starting with PHP 7, break or continue that are outside a loop (for, foreach(), do...while(), while()) or a switch() statement won't compile anymore.
It is not possible anymore to include a piece of code inside a loop that will then break.
<?php
// outside a loop : This won't compile
break 1;
foreach($array as $a) {
break 1; // Compile OK
break 2; // This won't compile, as this break is in one loop, and not 2
}
foreach($array as $a) {
foreach($array2 as $a2) {
break 2; // OK in PHP 5 and 7
}
}
?>
Else Usage
[Since 0.8.4] - [ -P Structures/ElseUsage ] - [ Online docs ]
Else can be avoided by various means.
For example, defaulting values before using a condition; returning early in the method, thus skipping long else blocks; using a ternary operator to assign values conditionnaly.
Else is the equivalent of the default clause in a switch statement. When the if/then structure can be replaced with a switch can (albeit, a 2-case switch seems strange), then else usage is a good solution.
<?php
// $a is always set
$a = 'default';
if ($condition) {
$a = foo($condition);
}
// Don't use else for default : set default before
if ($condition) {
$a = foo($condition);
} else {
$a = 'default';
}
// Use then to exit
if ( ! $condition) {
return;
}
$a = foo($condition);
// don't use else to return
if ($condition) {
$a = foo($condition);
} else {
return;
}
?>
See also Avoid Else, Return Early and Why does clean code forbid else expression.
Avoid Substr() One
[Since 0.8.4] - [ -P Structures/NoSubstrOne ] - [ Online docs ]
Use array notation $string[$position] to reach a single byte in a string.
There are two ways to access a byte in a string : substr() and $v[$pos] .
The second style is more readable. It may be up to four times faster, though it is a micro-optimization. It is recommended to use it.
PHP 7.1 also introduces the support of negative offsets as string index : negative offset are also reported.
Beware that substr() and $v[$pos] are similar, while mb_substr() is not. The first function works on bytes, while the latter works on characters.
<?php
$string = 'ab人cde';
echo substr($string, $pos, 1);
echo $string[$pos];
echo mb_substr($string, $pos, 1);
// when $pos = 1
// displays bbb
// when $pos = 2
// displays ??人
?>
Alternatives :
- Replace substr() with the array notations for strings.
- Replace substr() with a call to mb_substr().
Anonymous Classes
[Since 0.8.4] - [ -P Classes/Anonymous ] - [ Online docs ]
This rule lists the anonymous classes. Anonymous classes are defined without a name, and are immediately instanciated.
<?php
// Anonymous class, available since PHP 7.0
$object = new class { function __construct() { echo __METHOD__; } };
?>
See also Anonymous classes.
Coalesce
[Since 0.8.4] - [ -P Php/Coalesce ] - [ Online docs ]
Usage of coalesce operator.
Note that the coalesce operator is a special case of the ternary operator.
<?php
// Coalesce operator, since PHP 5.3
$a = $b ?: 'default value';
// Equivalent to $a = $b ? $b : 'default value';
?>
See also Ternary Operator.
Double Assignation
[Since 0.8.4] - [ -P Structures/DoubleAssignation ] - [ Online docs ]
This happens when a container (variable, property, array index) is assigned with values twice in a row. One of them is probably a debug instruction, that was forgotten.
<?php
// Normal assignation
$a = 1;
// Double assignation
$b = 2;
$b = 3;
?>
Empty List
[Since 0.8.4] - [ -P Php/EmptyList ] - [ Online docs ]
Empty list() are not allowed anymore in PHP 7. There must be at least one variable in the list call.
<?php
//Not accepted since PHP 7.0
list() = array(1,2,3);
//Still valid PHP code
list(,$x) = array(1,2,3);
?>
Alternatives :
- Remove empty list() calls
Directives Usage
[Since 0.8.4] - [ -P Php/DirectivesUsage ] - [ Online docs ]
This rule lists the directives mentioned in the code. When the directives are accessed in the code, it signals that they must be configured in PHP.ini first.
<?php
//accessing the configuration to change it
ini_set('timelimit', -1);
//accessing the configuration to check it
ini_get('safe_mode');
?>
See also `ini_set() Useless Brackets
[Since 0.8.4] - [ -P Structures/UselessBrackets ] - [ Online docs ]
Standalone brackets have no use. Brackets are used to delimit a block of code, and are used by control statements. They may also be used to protect variables in strings.
Standalone brackets may be a left over of an old instruction, or a misunderstanding of the alternative syntax.
<?php
// The following brackets are useless : they are a leftover from an older instruction
// if (DEBUG)
{
$a = 1;
}
// Here, the extra brackets are useless
for($a = 2; $a < 5; $a++) : {
$b++;
} endfor;
?>
Alternatives :
- Remove the brackets
- Restore the flow-control operation that was there and removed
- Move the block into a method or function, and call it
preg_replace With Option e
[Since 0.8.4] - [ -P Structures/pregOptionE ] - [ Online docs ]
preg_replace() supported the /e option until PHP 7.0. It allowed the use of eval()'ed expression as replacement. This has been dropped in PHP 7.0, for security reasons.
preg_replace() with /e option may be replaced with preg_replace_callback() and a closure, or preg_replace_callback_array() and an array of closures.
<?php
// preg_replace with /e
$string = 'abcde';
// PHP 5.6 and older usage of /e
$replaced = preg_replace('/c/e', 'strtoupper($0)', $string);
// PHP 7.0 and more recent
// With one replacement
$replaced = preg_replace_callback('/c/', function ($x) { return strtoupper($x[0]); }, $string);
// With several replacements, preventing multiple calls to preg_replace_callback
$replaced = preg_replace_callback_array(array('/c/' => function ($x) { return strtoupper($x[0]); },
'/[a-b]/' => function ($x) { return strtolower($x[0]); }), $string);
?>
Alternatives :
- Replace call to preg_replace() and /e with preg_replace_callback() or preg_replace_callback_array()
eval() Without Try
[Since 0.8.4] - [ -P Structures/EvalWithoutTry ] - [ Online docs ]
eval()
Note that it will catch situations where eval()
<?php
$code = 'This is no PHP code.';
//PHP 5 style
eval($code);
// Ends up with a Fatal error, at execution time
//PHP 7 style
try {
eval($code);
} catch (ParseError $e) {
cleanUpAfterEval();
}
?>
Alternatives :
- Always add a try/catch block around eval() call
Global In Global
[Since 0.8.4] - [ -P Structures/GlobalInGlobal ] - [ Online docs ]
List of global variables. There are the global variables, defined with the global keyword, and the implicit global variables, defined in the global scope.
<?php
global $explicitGlobal; // in global namespace
$implicitGlobal = 1; // in global namespace, variables are automatically global
function foo() {
global $explicitGlobalInFoo; // in functions, globals must be declared with global
}
?>
See also Variable Scope.
ext/fann
[Since 0.8.4] - [ -P Extensions/Extfann ] - [ Online docs ]
Extension FANN : Fast Artificial Neural Network.
PHP binding for FANN library which implements multi-layer artificial neural networks with support for both fully connected and sparsely connected networks.
<?php
$num_input = 2;
$num_output = 1;
$num_layers = 3;
$num_neurons_hidden = 3;
$desired_error = 0.001;
$max_epochs = 500000;
$epochs_between_reports = 1000;
$ann = fann_create_standard($num_layers, $num_input, $num_neurons_hidden, $num_output);
if ($ann) {
fann_set_activation_function_hidden($ann, FANN_SIGMOID_SYMMETRIC);
fann_set_activation_function_output($ann, FANN_SIGMOID_SYMMETRIC);
$filename = dirname(__FILE__) . '/xor.data';
if (fann_train_on_file($ann, $filename, $max_epochs, $epochs_between_reports, $desired_error))
fann_save($ann, dirname(__FILE__) . '/xor_float.net');
fann_destroy($ann);
}
?>
See also extension FANN, PHP-ML, Rubix ML and lib FANN.
func_get_arg() Modified
[Since 0.8.4] - [ -P Functions/funcGetArgModified ] - [ Online docs ]
func_get_arg() and func_get_args() used to report the calling value of the argument until PHP 7.
Since PHP 7, it is reporting the value of the argument at calling time, which may have been modified by a previous instruction.
This code will display 1 in PHP 7, and 0 in PHP 5.
<?php
function x($a) {
print func_get_arg(0); // 0
$a++;
print func_get_arg(0); // 1
}
x(0);
?>
Alternatives :
- Use func_get_arg() early in the function.
- Avoir mixing func_get_args() and direct access to the parameters.
- Avoir using func_get_args() and specifying parameters.
- Avoir modifying parameters.
Use Web
[Since 0.8.4] - [ -P Php/UseWeb ] - [ Online docs ]
The code is used in web environment.
The web usage is identified through the usage of the superglobals: $_GET , $_POST , etc.
<?php
// Accessing $_GET is possible when PHP is used in a web server.
$x = filter_validate($_GET['x'], FILTER_EMAIL);
?>
Use Cli
[Since 0.8.4] - [ -P Php/UseCli ] - [ Online docs ]
Signal the usage of code in CLI mode, through the usage of $argv and $argc variables, or the getopt() function.
<?php
// Characteristics of CLI usage
getopt("abcd");
?>
Avoid get_class()
[Since 0.8.4] - [ -P Structures/UseInstanceof ] - [ Online docs ]
get_class() should be replaced with the instanceof operator to check the class of an object.
get_class() only compares the full namespace name of the object's class, while instanceof actually resolves the name, using the local namespace and aliases.
<?php
use Stdclass as baseClass;
function foo($arg) {
// Slow and prone to namespace errors
if (get_class($arg) === 'Stdclass') {
// doSomething()
}
}
function bar($arg) {
// Faster, and uses aliases.
if ($arg instanceof baseClass) {
// doSomething()
}
}
?>
Alternatives :
- Replace get_class() with the instanceof operator
Silently Cast Integer
[Since 0.8.4] - [ -P Type/SilentlyCastInteger ] - [ Online docs ]
Those are integer literals that are cast to a float when running PHP. They are too big for the current PHP version, and PHP resorts to cast them into a float, which has a much larger capacity but a lower precision.
Compare your literals to PHP_MAX_INT (typically 9223372036854775807 ) and PHP_MIN_INT (typically -9223372036854775808 ).
This applies to binary ( 0b10101 ...), octal ( 0123123 ...) and hexadecimal ( 0xfffff ...) too.
<?php
echo 0b1010101101010110101011010101011010101011010101011010101011010111;
//6173123008118052203
echo 0b10101011010101101010110101010110101010110101010110101010110101111;
//1.2346246016236E+19
echo 0123123123123123123123;
//1498121094048818771
echo 01231231231231231231231;
//1.1984968752391E+19
echo 0x12309812311230;
//5119979279159856
echo 0x12309812311230fed;
//2.0971435127439E+19
echo 9223372036854775807; //PHP_MAX_INT
//9223372036854775807
echo 9223372036854775808;
9.2233720368548E+18
?>
Alternatives :
- Make sure hexadecimal numbers have the right number of digits : generally, it is 15, but it may depends on your PHP version.
Error Messages
[Since 0.8.4] - [ -P Structures/ErrorMessages ] - [ Online docs ]
Error message when an error is reported in the code. Those messages will be read by whoever is triggering the error, and it has to be helpful.
It is a good exercise to read the messages out of context, and try to understand what is about.
Error messages are spotted via die, exit, trigger_error() or throw.
<?php
// Not so helpful messages
die('Here be monsters');
exit('An error happened');
throw new Exception('Exception thrown at runtime');
?>
Timestamp Difference
[Since 0.8.4] - [ -P Structures/TimestampDifference ] - [ Online docs ]
Avoid adding or subtracting quantities of seconds to measure time.
time() , microtime()
Those functions are subject to variations, depending on system clock variations, such as daylight saving time difference (every spring and fall, one hour variation), or leap seconds, happening on June, 30th or December 31th , as announced by IERS.
When the difference may be rounded to a larger time unit (rounding the difference to days, or several hours), the variation may be ignored safely.
When the difference is very small, it requires a better way to measure time difference, such as `Ticks
`ext/hrtime
Alternatives :
<?php
// Calculating tomorow, same hour, the wrong way
// tomorrow is not always in 86400s, especially in countries with daylight saving
$tomorrow = time() + 86400;
// Good way to calculate tomorrow
$datetime = new DateTime('tomorrow');
?>
See also PHP DateTime difference – it’s a trap! and PHP Daylight savings bug?.
Php7 Relaxed Keyword
[Since 0.8.4] - [ -P Php/Php7RelaxedKeyword ] - [ Online docs ]
Most of the traditional PHP keywords may be used inside classes, enums, traits and interfaces: they can be used as constant or method name.
It is recommended to use this syntax cautiously, as it leads to a lot of surprises and confusion from unususpecting developpers.
This was not the case in PHP 5, and will yield parse errors.
<?php
// Compatible with PHP 7.0 +
class foo {
const array = [];
// as is a PHP 5 keyword
public function as() {
print_r(self::array);
}
}
?>
See also Loosening Reserved Word Restrictions.
ext/pecl_http
[Since 0.8.4] - [ -P Extensions/Exthttp ] - [ Online docs ]
Extension HTTP.
This HTTP extension aims to provide a convenient and powerful set of functionalities for one of PHP major applications.
It eases handling of HTTP URL, headers and messages, provides means for negotiation of a client's preferred content type, language and charset, as well as a convenient way to send any arbitrary data with caching and resuming capabilities.
It provides powerful request functionality with support for parallel requests.
<?php
$client = new http\Client;
$client->setSslOptions(array("verifypeer" => true));
$client->addSslOptions(array("verifyhost" => 2));
$client->enqueue($req = new http\Client\Request("GET", "https://twitter.com/"));
$client->send();
$ti = (array) $client->getTransferInfo($req);
var_dump($ti);
?>
See also ext-http and pecl_http.
Unused Parameter
[Since 0.8.4] - [ -P Functions/UnusedArguments ] - [ Online docs ]
Those parameters are not used inside the method or function.
Unused parameters should be removed in functions : they are dead code, and seem to offer features that they do not deliver.
Some parameters are unused, due to the signature compatibility: for example, if an interface or a parent class defines that parameter, but it is not useful in the current method. Then, it must stay.
This is a silent error: no error message is emitted when doing so.
<?php
// $unused is in the signature, but not used.
function foo($unused, $b, $c) {
return $b + $c;
}
interface i {
function goo($a);
}
class a implements i {
// goo signature comes from the interface
function goo($a) {
return 3;
}
}
?>
Alternatives :
- Drop the argument from the signature
- Actually use that argument in the body of the method
Uses Environment
[Since 0.8.4] - [ -P Php/UsesEnv ] - [ Online docs ]
This rule spots usage of $_ENV , getenv()
<?php
// Take some configuration from the environment
$secret_key = getenv('secret_key');
?>
Switch To Switch
[Since 0.8.4] - [ -P Structures/SwitchToSwitch ] - [ Online docs ]
The following structures are based on if / elseif / else. Since they have more than three conditions (not withstanding the final else), it is recommended to use the switch structure, so as to make this more readable.
On the other hand, switch() structures with less than 3 elements should be expressed as a if / else structure.
Note that if condition that uses strict typing (=== or !==) can't be converted to switch() as the latter only performs == or != comparisons.
Note that simple switch statement, which compare a variable to a literal are optimised in PHP 7.2 and more recent. This gives a nice performance boost, and keep code readable.
<?php
if ($a == 1) {
} elseif ($a == 2) {
} elseif ($a == 3) {
} elseif ($a == 4) {
} else {
}
// Better way to write long if/else lists
switch ($a) {
case 1 :
doSomething(1);
break 1;
case 2 :
doSomething(2);
break 1;
case 3 :
doSomething(3);
break 1;
case 4 :
doSomething(4);
break 1;
default :
doSomething();
break 1;
}
?>
Alternatives :
- Use a switch statement, rather than a long string of if/else
- Use a match() statement, rather than a long string of if/else (PHP 8.0 +)
Wrong Parameter Type
[Since 0.8.4] - [ -P Php/InternalParameterType ] - [ Online docs ]
The expected parameter is not of the correct type. Check PHP documentation to know which is the right format to be used.
<?php
// substr() shouldn't work on integers.
// the first argument is first converted to string, and it is 123456.
echo substr(123456, 0, 4); // display 1234
// substr() shouldn't work on boolean
// the first argument is first converted to string, and it is 1, and not t
echo substr(true, 0, 1); // displays 1
// substr() works correctly on strings.
echo substr(123456, 0, 4);
?>
Redefined Methods
[Since 0.8.4] - [ -P Classes/RedefinedMethods ] - [ Online docs ]
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.
Wrong fopen() Mode
[Since 0.8.4] - [ -P Php/FopenMode ] - [ Online docs ]
Wrong file opening for fopen().
fopen() has a few modes, as described in the documentation : 'r', 'r+', for reading; 'w', 'w+' for writing; 'a', 'a+' for appending; 'x', 'x+' for modifying; 'c', 'c+' for writing and locking, 't' for text files and windows only.
An optional 'b' may be used to make the fopen() call more portable and binary safe. Another optional 't' may be used to make the fopen() call process automatically text input : this one should be avoided.
Any other values are not understood by PHP.
<?php
// open the file for reading, in binary mode
$fp = fopen('/tmp/php.txt', 'rb');
// New option e in PHP 7.0.16 and 7.1.2 (beware of compatibility)
$fp = fopen('/tmp/php.txt', 'rbe');
// Unknown option x
$fp = fopen('/tmp/php.txt', 'rbx');
?>
Alternatives :
- Check the docs, choose the right opening mode.
Unknown Directive Name
[Since 0.8.4] - [ -P Php/DirectiveName ] - [ Online docs ]
Unknown directives names used in the code.
The following list has directive mentioned in the code, that are not known from PHP or any extension. If this is due to a mistake, the directive must be fixed to be actually useful.
<?php
// non-existing directive
$reporting_error = ini_get('reporting_error');
$error_reporting = ini_get('error_reproting'); // Note the inversion
if (ini_set('dump_globals')) {
// doSomething()
}
// Correct directives
$error_reporting = ini_get('reporting_error');
if (ini_set('xdebug.dump_globals')) {
// doSomething()
}
?>
Is CLI Script
[Since 0.8.4] - [ -P Files/IsCliScript ] - [ Online docs ]
Mark a file as a CLI script. This means that this code is used in command line. That detection is based on the usage of distinct CLI features, such as #! at the beginning of the file.
#!/usr/bin/php
<?php
echo PHP_VERSION;
?>
PHP Bugfixes
[Since 0.8.4] - [ -P Php/MiddleVersion ] - [ Online docs ]
This is the list of features, used in the code, that also received a bug fix in recent PHP versions.
Negative Power
[Since 0.8.4] - [ -P Structures/NegativePow ] - [ Online docs ]
The power operator ** has higher precedence than the sign operators + and -.
This means that -2 ** 2 == -4. It is in fact, -(2 ** 2).
When using negative power, it is clearer to add parenthesis or to use the pow() function, which has no such ambiguity :
<?php
// -2 to the power of 2 (a square)
pow(-2, 2) == 4;
// minus 2 to the power of 2 (a negative square)
-2 ** 2 == -(2 ** 2) == 4;
?>
Alternatives :
- Avoid negative number, as operands of **
- Use parenthesis with negative numbers and **
Already Parents Interface
[Since 0.8.4] - [ -P Interfaces/AlreadyParentsInterface ] - [ Online docs ]
The same interface is implemented by a class and one of its children.
That way, the child doesn't need to implement the interface, nor define its methods to be an instance of the interface.
This analysis may report classes which do not explicitly implements any interfaces : the issue is then coming from the parents.
<?php
interface i {
function i();
}
class A implements i {
function i() {
return __METHOD__;
}
}
// This implements is useless.
class AB extends A implements i {
// No definition for function i()
}
// Implements i is understated
class AB extends A {
// redefinition of the i method
function i() {
return __METHOD__.' ';
}
}
$x = new AB;
var_dump($x instanceof i);
// true
$x = new AC;
var_dump($x instanceof i);
// true
?>
Alternatives :
- Keep the implements call in the class that do implements the methods. Remove it from the children classes.
Use random_int()
[Since 0.8.4] - [ -P Php/BetterRand ] - [ Online docs ]
rand() and mt_rand() should be replaced with random_int().
At worse, rand() should be replaced with mt_rand(), which is a drop-in replacement and srand() by mt_srand().
random_int() replaces rand(), and has no seeding function like srand().
Other sources of entropy that should be replaced by random_int() : microtime(), uniqid(), time(). Those a often combined with hashing functions and mixed with other sources of entropy, such as a salt.
Since PHP 7, random_int() along with random_bytes(), provides cryptographically secure pseudo-random bytes, which are good to be used
when security is involved. openssl_random_pseudo_bytes() may be used when the OpenSSL extension is available.
<?php
// Avoid using this
$random = rand(0, 10);
// Drop-in replacement
$random = mt_rand(0, 10);
// Even better but different :
// valid with PHP 7.0+
try {
$random = random_int(0, 10);
} catch (\Exception $e) {
// process case of not enoug random values
}
// This is also a source of entropy, based on srand()
// random_int() is a drop-in replacement here
$a = sha256(uniqid());
?>
Alternatives :
- Use random_bytes() and randon_int(). At least, use them as a base for random data, and then add extra prefix and suffix, and a hash call on top.
Can't Extend Final
[Since 0.8.4] - [ -P Classes/CantExtendFinal ] - [ Online docs ]
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.
In a separate file :
<?php
// File Foo
final class foo {
public final function bar() {
// doSomething
}
}
?>
Alternatives :
- Remove the final keyword
- Remove the extending class
Ternary In Concat
[Since 0.8.4] - [ -P Structures/TernaryInConcat ] - [ Online docs ]
Ternary and coalesce operator have higher priority than dot '.' for concatenation. This means that :
prints actually 'E' , instead of the awaited 'B0CE' .
To be safe, always add parenthesis when using ternary operator with concatenation.
<?php
// print B0CE as expected
print 'B'.$b.'C'. ($b > 1 ? 'D') : 'E';
// print E, instead of B0CE
print 'B'.$b.'C'. $b > 1 ? 'D' : 'E';
print 'B'.$b.'C'. $b > 1 ? 'D' : 'E';
?>
Alternatives :
- Use parenthesis
- Avoid ternaries and coalesce operators inside a string
Using $this Outside A Class
[Since 0.8.4] - [ -P Classes/UsingThisOutsideAClass ] - [ Online docs ]
$this is a special variable, that should only be used in a class context.
Until PHP 7.1, $this may be used as an argument in a function or a method, a global, a static : while this is legit, it sounds confusing enough to avoid it.
Starting with PHP 7.1, the PHP engine check thoroughly that $this is used in an appropriate manner, and raise fatal errors in case it isn't.
Yet, it is possible to find $this outside a class : if the file is included inside a class, then $this will be recognized and validated. If the file is included outside a class context, it will yield a fatal error : Using $this when not in object context .
<?php
function foo($this) {
echo $this;
}
// A closure can be bound to an object at later time. It is valid usage.
$closure = function ($x) {
echo $this->foo($x);
}
?>
See also Closure::bind and The Basics.
ext/tokyotyrant
[Since 0.8.4] - [ -P Extensions/Exttokyotyrant ] - [ Online docs ]
Extension for Tokyo Tyrant.
tokyo_tyrant extension provides a wrapper for Tokyo Tyrant client libraries.
<?php
$tt = new TokyoTyrant("localhost");
$tt->put("key", "value");
echo $tt->get("key");
?>
See also tokyo_tyrant and Tokyo cabinet.
ext/v8js
[Since 0.8.4] - [ -P Extensions/Extv8js ] - [ Online docs ]
Extension v8js.
This extension embeds the V8 Javascript Engine into PHP.
<?php
$v8 = new V8Js();
/* basic.js */
$JS = <<< EOT
len = print('Hello' + ' ' + 'World!' + '\n');
len;
EOT;
try {
var_dump($v8->executeString($JS, 'basic.js'));
} catch (V8JsException $e) {
var_dump($e);
}
?>
See also V8 Javascript Engine Integration, V8 Javascript Engine for PHP and pecl v8js.
Yield Usage
[Since 0.8.4] - [ -P Php/YieldUsage ] - [ Online docs ]
Usage of generators, with yield keyword.
Yield was introduced in PHP 5.5, and is backward incompatible.
<?php
function prime() {
$primes = [2, 3, 5, 7, 11, 13, 17, 19];
foreach($primes as $prime) {
yield $prime;
}
}
?>
See also Generator Syntax, Deal with Memory Gently using "Yield" in PHP and Understanding PHP Generators.
Yield From Usage
[Since 0.8.4] - [ -P Php/YieldFromUsage ] - [ Online docs ]
Usage of generator delegation, with yield from keyword.
In PHP 7, generator delegation allows you to yield values from another Generator , Traversable object, or array by using the yield from .
Yield from was introduced in PHP 7.1, and is backward incompatible.
<?php
// Yield delegation
function foo() {
yield from bar();
}
function bar() {
yield 1;
}
?>
See also Generator Syntax and Understanding PHP Generators.
Pear Usage
[Since 0.8.4] - [ -P Php/PearUsage ] - [ Online docs ]
Pear Usage : list of Pear packages in use.
<?php
require_once('MDB2.php');
$dsn = 'mysql://user:pass@host';
$mdb2 = &MDB2::factory($dsn);
$mdb2->setFetchMode(MDB2_FETCHMODE_ASSOC);
?>
See also PEAR.
Undefined Trait
[Since 0.8.4] - [ -P Traits/UndefinedTrait ] - [ Online docs ]
Those are undefined, traits .
When the using class or trait is instantiated, PHP emits a a fatal error.
Trait which are referenced in a `use` expression are omitted: they are considered part of code that is probably outside the current code, either omitted or in external component.
<?php
use Composer/Component/someTrait as externalTrait;
trait t {
function foo() {}
}
// This class uses trait that are all known
class hasOnlyDefinedTrait {
use t, externalTrait;
}
// This class uses trait that are unknown
class hasUndefinedTrait {
use unknownTrait, t, externalTrait;
}
?>
Alternatives :
- Define the missing trait
- Remove usage of the missing trait
No Hardcoded Hash
[Since 0.8.4] - [ -P Structures/NoHardcodedHash ] - [ Online docs ]
Hash should never be hardcoded.
Hashes may be MD5, SHA1, SHA512, Bcrypt or any other. Such values must be easily changed, for security reasons, and the source code is not the safest place to hide it.
<?php
// Those strings may be sha512 hashes.
// it is recomemdned to check if they are static or should be put into configuration
$init512 = array( // initial values for SHA512
'6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
);
// strings which are obvious conversion are ignored
$decimal = intval('87878877', 12);
?>
Alternatives :
- Put any hardcoded hash in a configuration file, a database or a environment variable. An external source.
Identical Conditions
[Since 0.8.4] - [ -P Structures/IdenticalConditions ] - [ Online docs ]
These logical expressions contain members that are identical.
This means those expressions may be simplified.
<?php
// twice $a
if ($a || $b || $c || $a) { }
// Hiding in parenthesis is bad
if (($a) ^ ($a)) {}
// expressions may be large
if ($a === 1 && 1 === $a) {}
?>
Alternatives :
- Merge the two structures into one unique test
- Add extra expressions between the two structures
- Nest the structures, to show that different attempts are made
Unkown Regex Options
[Since 0.8.4] - [ -P Structures/UnknownPregOption ] - [ Online docs ]
Regex support in PHP accepts the following list of options : eimsuxADJSUX .
All other letter used as option are not supported : depending on the situation, they may be ignored or raise an error.
<?php
// all options are available
if (preg_match('/\d+/isA', $string, $results)) { }
// p and h are not regex options, p is double
if (preg_match('/\d+/php', $string, $results)) { }
?>
Alternatives :
- Remove the unknown options
- Replace the option with a valid one
- Fix any syntax typo in the regex
No Choice
[Since 0.8.4] - [ -P Structures/NoChoice ] - [ Online docs ]
A conditional structure is being used, and both alternatives are the same, leading to the illusion of choice.
Either the condition is useless, and may be removed, or the alternatives need to be distinguished.
<?php
if ($condition == 2) {
doSomething();
} else {
doSomething();
}
$condition == 2 ? doSomething() : doSomething();
?>
Alternatives :
- Remove the conditional, and call the expression directly
- Replace one of the alternative with a distinct call
- Remove the whole conditional : it may end up being useless
- Extract the common elements of the condition to make them obvious
Common Alternatives
[Since 0.8.4] - [ -P Structures/CommonAlternatives ] - [ Online docs ]
In the following conditional structures, expressions were found that are common to both 'then' and 'else'. It may be interesting, though not always possible, to put them both out of the conditional, and reduce line count.
may be rewritten in :
<?php
if ($c == 5) {
$b = strtolower($b[2]);
$a++;
} else {
$b = strtolower($b[2]);
$b++;
}
?>
Alternatives :
- Collect common expressions, and move them before of after the if/then expression.
- Move a prefix and suffixes to a third-party method
Logical Mistakes
[Since 0.8.4] - [ -P Structures/LogicalMistakes ] - [ Online docs ]
Avoid logical mistakes within long expressions.
Sometimes, the logic is not what it seems. It is important to check the actual impact of every part of the logical expression. Do not hesitate to make a table with all possible cases. If those cases are too numerous, it may be time to rethink the whole expression.
Inspired by an article from Andrey Karpov .
<?php
// Always true
if ($a != 1 || $a != 2) { }
// $a == 1 is useless
if ($a == 1 || $a != 2) {}
// Always false
if ($a == 1 && $a == 2) {}
// $a != 2 is useless
if ($a == 1 && $a != 2) {}
?>
Alternatives :
- Change the expressions for them to have a real meaning
ext/lua
[Since 0.8.4] - [ -P Extensions/Extlua ] - [ Online docs ]
Extension Lua.
'Lua is a powerful, fast, light-weight, embeddable scripting language.' This extension embeds the lua interpreter and offers an OO-API to lua variables and functions.
<?php
$lua = new Lua();
$lua->eval(<<<CODE
print(2);
CODE
);
?>
See also ext/lua manual and LUA.
Uncaught Exceptions
[Since 0.8.4] - [ -P Exceptions/UncaughtExceptions ] - [ Online docs ]
The following exceptions are thrown in the code, but are never caught.
Either they will lead to a Fatal Error, or they have to be caught by an including application. This is a valid behavior for libraries, but is not for a final application.
<?php
// This exception is throw, but not caught. It will lead to a fatal error.
if ($message = check_for_error()) {
throw new My\Exception($message);
}
// This exception is throw, and caught.
try {
if ($message = check_for_error()) {
throw new My\Exception($message);
}
} catch (\Exception $e) {
doSomething();
}
?>
Alternatives :
- Catch all the exceptions you throw
Same Conditions In Condition
[Since 0.8.4] - [ -P Structures/SameConditions ] - [ Online docs ]
At least two consecutive if/then structures use identical conditions. The latter will probably be ignored.
This analysis returns false positive when there are attempt to fix a situation, or to call an alternative solution.
Conditions that are shared between if structures, but inside a logical OR expression are also detected.
<?php
if ($a == 1) { doSomething(); }
elseif ($b == 1) { doSomething(); }
elseif ($c == 1) { doSomething(); }
elseif ($a == 1) { doSomething(); }
else {}
// Also works on if then else if chains
if ($a == 1) { doSomething(); }
else if ($b == 1) { doSomething(); }
else if ($c == 1) { doSomething(); }
else if ($a == 1) { doSomething(); }
else {}
// Also works on if then else if chains
// Here, $a is common and sufficient in both conditions
if ($a || $b) { doSomething(); }
elseif ($a || $c) { doSomethingElse(); }
// This sort of situation generate false postive.
$config = load_config_from_commandline();
if (empty($config)) {
$config = load_config_from_file();
if (empty($config)) {
$config = load_default_config();
}
}
?>
Alternatives :
- Merge the two conditions into one
- Make the two conditions different
Return True False
[Since 0.8.4] - [ -P Structures/ReturnTrueFalse ] - [ Online docs ]
These conditional expressions return true/false, depending on the condition. This may be simplified by dropping the control structure altogether.
This may be simplified with :
This may be applied to assignations and ternary operators too.
<?php
if (version_compare($a, $b) >= 0) {
return true;
} else {
return false;
}
?>
Alternatives :
- Return directly the comparison, without using the if/then structure
- Cast the value to (boolean) and use it instead of the ternary
Useless Switch
[Since 0.8.4] - [ -P Structures/UselessSwitch ] - [ Online docs ]
This switch has only one case. It may very well be replaced by a ifthen structure.
<?php
switch($a) {
case 1:
doSomething();
break;
}
// Same as
if ($a == 1) {
doSomething();
}
?>
Alternatives :
- Turn the switch into a if/then for better readability
- Add other cases to the switch, making it adapted to the situation
Could Use __DIR__
[Since 0.8.4] - [ -P Structures/CouldUseDir ] - [ Online docs ]
Use __DIR__ constant to access the current file's parent directory.
Avoid using dirname() on __FILE__.
__DIR__ has been introduced in PHP 5.3.0.
<?php
// Better way
$fp = fopen(__DIR__.'/myfile.txt', 'r');
// compatible, but slow way
$fp = fopen(dirname(__FILE__).'/myfile.txt', 'r');
// Since PHP 5.3
assert(dirname(__FILE__) == __DIR__);
?>
Alternatives :
- Use __DIR__ instead of dirname(__FILE__);
Should Use Coalesce
[Since 0.8.4] - [ -P Php/ShouldUseCoalesce ] - [ Online docs ]
PHP 7 introduced the ?? operator, that replaces longer structures to set default values when a variable is not set.
Sample extracted from PHP docs Isset Ternary.
<?php
// Fetches the request parameter user and results in 'nobody' if it doesn't exist
$username = $_GET['user'] ?? 'nobody';
// equivalent to: $username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
// Calls a hypothetical model-getting function, and uses the provided default if it fails
$model = Model::get($id) ?? $default_model;
// equivalent to: if (($model = Model::get($id)) === NULL) { $model = $default_model; }
?>
Alternatives :
- Replace the long syntax with the short one
Make Global A Property
[Since 0.8.4] - [ -P Classes/MakeGlobalAProperty ] - [ Online docs ]
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();
}
}
?>
Alternatives :
- Avoid using global variables, and use properties instead
- Remove the usage of these global variables
List With Keys
[Since 0.8.4] - [ -P Php/ListWithKeys ] - [ Online docs ]
Setting keys when using list() is a PHP 7.1 feature.
<?php
// PHP 7.1 and later only
list('a' => $a, 'b' => $b) = ['b' => 1, 'c' => 2, 'a' => 3];
?>
If With Same Conditions
[Since 0.8.4] - [ -P Structures/IfWithSameConditions ] - [ Online docs ]
Successive If / then structures that have the same condition may be either merged or have one of the condition changed.
Note that if the values used in the condition have been modified in the first if/then structure, the two distinct conditions may be needed.
<?php
if ($a == 1) {
doSomething();
}
if ($a == 1) {
doSomethingElse();
}
// May be replaced by
if ($a == 1) {
doSomething();
doSomethingElse();
}
?>
Alternatives :
- Merge the two conditions so the condition is used once.
- Change one of the condition, so they are different
- Make it obvious that the first condition is a try, preparing the normal conditions.
ext/suhosin
[Since 0.8.4] - [ -P Extensions/Extsuhosin ] - [ Online docs ]
Suhosin extension.
Suhosin (pronounced 'su-ho-shin') is an advanced protection system for PHP installations. It was designed to protect servers and users from known and unknown flaws in PHP applications and the PHP core.
Suhosin was a PHP 5 extension, and it has been ported to PHP 7 and 8, as a separate but eponymous project.
<?php
// sha256 is a ext/suhosin specific function
$sha256 = sha256($string);
?>
See also Suhosin.org and Suhosin snuffleupagus.
Throw Functioncall
[Since 0.8.4] - [ -P Exceptions/ThrowFunctioncall ] - [ Online docs ]
The throw keyword expects to use an exception. Calling a function to prepare that exception before throwing it is possible, but forgetting the new keyword is also possible.
When the new keyword is forgotten, then the class constructor is used as a function name, and now exception is emitted, but an Undefined function fatal error is emitted.
<?php
// Forgotten new
throw \RuntimeException('error!');
// Code is OK, function returns an exception
throw getException(ERROR_TYPE, 'error!');
function getException(ERROR_TYPE, $message) {
return new \RuntimeException($messsage);
}
?>
Alternatives :
- Add the new operator to the call
- Make sure the function is really a functioncall, not a class name
- Use return type for functions, so that Exception may be detected
Can't Disable Function
[Since 0.8.4] - [ -P Security/CantDisableFunction ] - [ Online docs ]
This is the list of potentially dangerous PHP functions being used in the code, such as exec() or fsockopen().
eval() is not reported here, as it is not a PHP function, but a language construct : it can't be disabled.
This analysis is the base for suggesting values for the disable_functions directive.
<?php
// This script uses ftp_connect(), therefore, this function shouldn't be disabled.
$ftp = ftp_connect($host, 21);
// This script doesn't use imap_open(), therefore, this function may be disabled.
?>
Functions Using Reference
[Since 0.8.4] - [ -P Functions/FunctionsUsingReference ] - [ Online docs ]
Functions and methods using references in their signature.
<?php
function usingReferences( &$a) {}
class foo {
public function methodUsingReferences($b, &$c = 1) {}
}
?>
Use Instanceof
[Since 0.8.4] - [ -P Classes/UseInstanceof ] - [ Online docs ]
The instanceof operator is a more precise alternative to is_object()
instanceof checks for an variable to be of a class or its parents or the interfaces it implements.
Once instanceof has been used, the actual attributes available (properties, constants, methods) are known, unlike with is_object()
Last, instanceof may be upgraded to Typehint, by moving it to the method signature.
instanceof and is_object()
The instanceof operator is also faster than the is_object()
<?php
class Foo {
// Don't use is_object
public function bar($o) {
if (!is_object($o)) { return false; } // Classic argument check
return $o->method();
}
// use instanceof
public function bar($o) {
if ($o instanceof myClass) { // Now, we know which methods are available
return $o->method();
}
return false; } // Default behavior
}
// use of typehinting
// in case $o is not of the right type, exception is raised automatically
public function bar(myClass $o) {
return $o->method();
}
}
?>
Alternatives :
- Use instanceof and remove is_object()
- Create a high level interface to check a whole family of classes, instead of testing them individually
- Use typehint when possible
- Avoid mixing scalar types and objects in the same variable
List Short Syntax
[Since 0.8.4] - [ -P Php/ListShortSyntax ] - [ Online docs ]
Usage of short syntax version of list().
<?php
// PHP 7.1 short list syntax
// PHP 7.1 may also use key => value structures with list
[$a, $b, $c] = ['2', 3, '4'];
// PHP 7.0 list syntax
list($a, $b, $c) = ['2', 3, '4'];
?>
Results May Be Missing
[Since 0.8.4] - [ -P Structures/ResultMayBeMissing ] - [ Online docs ]
preg_match() may return empty values, if the search fails. It is important to check for the existence of results before assigning them to another variable, or using it.
Since PHP 7.2, it is possible to use the PREG_UNMATCHED_AS_NULL constant in the flag parameter to avoid this.
<?php
preg_match('/PHP ([0-9\.]+) /', $res, $r);
$s = $r[1];
// $s may end up null if preg_match fails.
?>
Alternatives :
- Use a final always capturing parenthesis to avoid this
- Use the PREG_UNMATCHED_AS_NULL option (PHP 7.2)
Use Nullable Type
[Since 0.8.4] - [ -P Php/UseNullableType ] - [ Online docs ]
The code uses nullable type, available since PHP 7.1.
Nullable Types are an option to type hint : they allow the passing value to be null, or another type.
According to the authors of the feature : 'It is common in many programming languages including PHP to allow a variable to be of some type or null. This null often indicates an error or lack of something to return.'
<?php
function foo(?string $a = 'abc') : ?string {
return $a.b;
}
?>
See also Type declarations and PHP RFC: Nullable Types.
Always Positive Comparison
[Since 0.8.4] - [ -P Structures/NeverNegative ] - [ Online docs ]
Some PHP native functions, such as count(), strlen(), or abs() only returns positive or null values.
When comparing them to 0, the following expressions are always true and should be avoided.
<?php
$a = [1, 2, 3];
var_dump(count($a) >= 0);
var_dump(count($a) < 0);
?>
Alternatives :
Multiple Exceptions Catch()
[Since 0.8.4] - [ -P Exceptions/MultipleCatch ] - [ Online docs ]
It is possible to have several distinct exceptions class caught by the same catch, preventing code repetition.
This is a new feature since PHP 7.1.
This is a backward incompatible feature of PHP 7.1.
<?php
// PHP 7.1 and more recent
try {
throw new someException();
} catch (Single $s) {
doSomething();
} catch (oneType | anotherType $s) {
processIdentically();
} finally {
}
// PHP 7.0 and older
try {
throw new someException();
} catch (Single $s) {
doSomething();
} catch (oneType $s) {
processIdentically();
} catch (anotherType $s) {
processIdentically();
} finally {
}
?>
Empty Blocks
[Since 0.8.4] - [ -P Structures/EmptyBlocks ] - [ Online docs ]
Full empty block, part of a control structures.
It is recommended to remove those blocks, so as to reduce confusion in the code.
<?php
foreach($foo as $bar) ; // This block seems erroneous
$foobar++;
if ($a === $b) {
doSomething();
} else {
// Empty block. Remove this
}
// Blocks containing only empty expressions are also detected
for($i = 0; $i < 10; $i++) {
;
}
// Although namespaces are not control structures, they are reported here
namespace A;
namespace B;
?>
Alternatives :
- Fill the block with a command
- Fill the block with a comment that explain the situation
- Remove the block and its commanding operator
Throw In Destruct
[Since 0.8.4] - [ -P Classes/ThrowInDestruct ] - [ Online docs ]
According to the manual, Attempting to throw an exception from a destructor (called in the time of script termination) causes a fatal error.
The destructor may be called during the lifespan of the script, but it is not certain. If the exception is thrown later, the script may end up with a fatal error.
Thus, it is recommended to avoid throwing exceptions within the __destruct method of a class.
<?php
// No exception thrown
class Bar {
function __construct() {
throw new Exception('__construct');
}
function __destruct() {
$this->cleanObject();
}
}
// Potential crash
class Foo {
function __destruct() {
throw new Exception('__destruct');
}
}
?>
Alternatives :
- Remove any exception thrown from a destructor
Use System Tmp
[Since 0.8.4] - [ -P Structures/UseSystemTmp ] - [ Online docs ]
It is recommended to avoid hardcoding the temporary file. It is better to rely on the system's temporary folder, which is accessible with sys_get_temp_dir().
<?php
// Where the tmp is :
file_put_contents(sys_get_temp_dir().'/tempFile.txt', $content);
// Avoid hard-coding tmp folder :
// On Linux-like systems
file_put_contents('/tmp/tempFile.txt', $content);
// On Windows systems
file_put_contents('C:\WINDOWS\TEMP\tempFile.txt', $content);
?>
Alternatives :
- Do not hardcode the temporary file, use the system's
Dependant Trait
[Since 0.8.4] - [ -P Traits/DependantTrait ] - [ Online docs ]
Traits should be autonomous. It is recommended to avoid depending on methods or properties that should be in the using class.
The following traits make usage of methods and properties, static or not, that are not defined in the trait. This means the host class must provide those methods and properties, but there is no way to enforce this.
This may also lead to dead code : when the trait is removed, the host class have unused properties and methods.
<?php
// autonomous trait : all it needs is within the trait
trait t {
private $p = 0;
function foo() {
return ++$this->p;
}
}
// dependant trait : the host class needs to provide some properties or methods
trait t2 {
function foo() {
return ++$this->p;
}
}
class x {
use t2;
private $p = 0;
}
?>
Alternatives :
- Add local property definitions to make the trait independent
- Make the trait only use its own resources
- Split the trait in autonomous traits
[Since 0.8.4] - [ -P Namespaces/HiddenUse ] - [ Online docs ]
The use expression for namespaces should always be at the beginning of the namespace block.
It is where everyone expect them, and it is less confusing than having them at various levels.
<?php
// This is visible
use A;
class B {}
// This is hidden
use C as D;
class E extends D {
use traitT; // This is a use for a trait
function foo() {
// This is a use for a closure
return function ($a) use ($b) {}
}
}
?>
Alternatives :
- Group all uses together, at the beginning of the namespace or class
Should Make Alias
[Since 0.8.4] - [ -P Namespaces/ShouldMakeAlias ] - [ Online docs ]
Long names should be aliased.
Aliased names are easy to read at the beginning of the script; they may be changed at one point, and update the whole code at the same time.
Finally, short names makes the rest of the code readable.
<?php
namespace x\y\z;
use a\b\c\d\e\f\g as Object;
// long name, difficult to read, prone to change.
new a\b\c\d\e\f\g();
// long name, difficult to read, prone to silent dead code if namespace change.
if ($o instanceof a\b\c\d\e\f\g) {
}
// short names Easy to update all at once.
new Object();
if ($o instanceof Object) {
}
?>
Multiple Identical Trait Or Interface
[Since 0.8.4] - [ -P Classes/MultipleTraitOrInterface ] - [ Online docs ]
There is no need to use the same trait, or implements the same interface more than once in a class.
Up to PHP 7.4, this doesn't raise any warning. Traits are only imported once, and interfaces may be implemented as many times as wanted.
Since PHP 7.4, multiple implementations of the same interface in one class is reported at compilation time. It is possible to repeat the implementation in various levels of a class hierarchy (aka, same implements in a class and a parent).
This only applies in a single class: there are no checks in a class, or interface hierarchy.
<?php
class foo {
use aTrait, aTrait, aTrait;
use aTrait;
}
class bar implements anInterface, anInterface, anInterface {
}
?>
Alternatives :
- Remove the duplicate trait or interface
Multiple Alias Definitions
[Since 0.8.4] - [ -P Namespaces/MultipleAliasDefinitions ] - [ Online docs ]
Some aliases are representing different classes across the repository. This leads to potential confusion.
Across an application, it is recommended to use the same namespace for one alias. Failing to do this lead to the same keyword to represent different values in different files, with different behavior. Those are hard to find bugs.
<?php
namespace A {
use d\d; // aka D
}
// Those are usually in different files, rather than just different namespaces.
namespace B {
use b\c as D; // also D. This could be named something else
}
?>
Alternatives :
- Give more specific names to classes
- Use an alias 'use A\B ac BC' to give locally another name
Nested Ifthen
[Since 0.8.4] - [ -P Structures/NestedIfthen ] - [ Online docs ]
Three levels of ifthen is too much. The method should be split into smaller functions.
<?php
function foo($a, $b) {
if ($a == 1) {
// Second level, possibly too much already
if ($b == 2) {
}
}
}
function bar($a, $b, $c) {
if ($a == 1) {
// Second level.
if ($b == 2) {
// Third level level.
if ($c == 3) {
// Too much
}
}
}
}
?>
See also Structures/TooManyIf.
Cast To Boolean
[Since 0.8.4] - [ -P Structures/CastToBoolean ] - [ Online docs ]
This expression may be reduced to casting to a boolean. This makes the code more readable, and adapt the type of the value to its usage.
<?php
$variable = $condition == 'met' ? 1 : 0;
// Same as
$variable = (bool) $condition == 'met';
$variable = $condition == 'met' ? 0 : 1;
// Same as (Note the condition inversion)
$variable = (bool) $condition != 'met';
// also, with an indentical condition
$variable = !(bool) $condition == 'met';
// This also works with straight booleans expressions
$variable = $condition == 'met' ? true : false;
// Same as
$variable = $condition == 'met';
?>
Alternatives :
- Remove the old expression and use (bool) operator instead
- Change the target values from true/false, or 0/1 to non-binary values, like strings or integers beyond 0 and 1.
- Complete the current branches with other commands
Failed Substr() Comparison
[Since 0.8.4] - [ -P Structures/FailingSubstrComparison ] - [ Online docs ]
The extracted string must be of the size of the compared string.
This is also true for negative lengths.
This rule raise a false positive when the variable is already smaller than the expected substr() results.
This rule doesn't apply to mb_substr() and iconv_substr() : those functions use the character size, not the byte size.
<?php
// Possible comparison : strings and substr results are the same
if (substr($a, 0, 3) === 'abc') { }
if (substr($b, 4, 3) === 'abc') { }
// Always failing : substr will probably provide a longer string
if (substr($a, 0, 3) === 'ab') { }
if (substr($a, 3, -3) === 'ab') { }
// Omitted in this analysis
if (substr($a, 0, 3) !== 'ab') { }
?>
Alternatives :
- Fix the string
- Fix the length of the string
- Put the string in a constant, and use strlen() or mb_strlen()
- Put the string in a constant, and use strlen() or mb_strlen()
Should Use Ternary Operator
[Since 0.8.5] - [ -P Structures/ShouldMakeTernary ] - [ Online docs ]
Ternary operators are the best when assigning values to a variable.
They are less verbose, compatible with assignation and easier to read.
<?php
// verbose if then structure
if ($a == 3) {
$b = 2;
} else {
$b = 3;
}
// compact ternary call
$b = ($a == 3) ? 2 : 3;
// verbose if then structure
// Works with short assignations and simple expressions
if ($a != 3) {
$b += 2 - $a * 4;
} else {
$b += 3;
}
// compact ternary call
$b += ($a != 3) ? 2 - $a * 4 : 3;
?>
Alternatives :
- Use the ternary operator
Unused Returned Value
[Since 0.8.5] - [ -P Functions/UnusedReturnedValue ] - [ Online docs ]
The function called returns a value, which is ignored.
Usually, this is a sign of dead code, or a missed check on the results of the functioncall. At times, it may be a valid call if the function has voluntarily no return value.
It is recommended to add a check on the return value, or remove the call.
Note that this analysis ignores functions that return void (same meaning that PHP 7.1 : return ; or no return in the function body).
<?php
// simplest form
function foo() {
return 1;
}
foo();
// In case of multiple return, any one that returns something means that return value is meaningful
function bar() {
if (rand(0, 1)) {
return 1;
} else {
return ;
}
}
bar();
?>
Modernize Empty With Expression
[Since 0.8.6] - [ -P Structures/ModernEmpty ] - [ Online docs ]
empty() accepts expressions as argument. This feature was added in PHP 5.5.
There is no need to store the expression in a variable before testing, unless it is reused later.
<?php
// PHP 5.5+ empty() usage
if (empty(foo($b . $c))) {
doSomethingWithoutA();
}
// Compatible empty() usage
$a = foo($b . $c);
if (empty($a)) {
doSomethingWithoutA();
}
// $a2 is reused, storage is legit
$a2 = strtolower($b . $c);
if (empty($a2)) {
doSomething();
} else {
echo $a2;
}
?>
Alternatives :
- Avoid the temporary variable, and use directly empty()
Use Positive Condition
[Since 0.8.6] - [ -P Structures/UsePositiveCondition ] - [ Online docs ]
Whenever possible, use a positive condition.
Positive conditions are easier to understand, and lead to less understanding problems.
Negative conditions are not reported when else is not present.
<?php
// This is a positive condition
if ($a == 'b') {
doSomething();
} else {
doSomethingElse();
}
if (!empty($a)) {
doSomething();
} else {
doSomethingElse();
}
// This is a negative condition
if ($a == 'b') {
doSomethingElse();
} else {
doSomething();
}
// No need to force $a == 'b' with empty else
if ($a != 'b') {
doSomethingElse();
}
?>
Alternatives :
- Invert the code in the if branches, and the condition
Drop Else After Return
[Since 0.8.6] - [ -P Structures/DropElseAfterReturn ] - [ Online docs ]
Avoid else clause when the then clause returns, but not the else. And vice-versa.
This way, the else block disappears, and is now the main sequence of the function.
This is also true if else has a return, and then not. When doing so, don't forget to reverse the condition.
<?php
// drop the else
if ($a) {
return $a;
} else {
doSomething();
}
// drop the then
if ($b) {
doSomething();
} else {
return $a;
}
// return in else and then
if ($a3) {
return $a;
} else {
$b = doSomething();
return $b;
}
?>
Alternatives :
- Remove the else clause and move its code to the main part of the method
Use ::Class Operator
[Since 0.8.7] - [ -P Classes/UseClassOperator ] - [ Online docs ]
Use ::class to hardcode class names, instead of strings.
This is actually faster than strings, which are parsed at execution time, while ::class is compiled, making it faster to execute.
::class operator is also able to handle use expressions, including aliases and local namespace. The code is easier to maintain. For example, the target class's namespace may be renamed, without changing the ::class , while the string must be updated.
::class operator works with self and static keywords.
This is not possible when building the name of the class with concatenation.
This is a micro-optimization. This also helps static analysis, as it gives more information at compile time to analyse.
<?php
namespace foo\bar;
use foo\bar\X as B;
class X {}
$className = '\foo\bar\X';
$className = foo\bar\X::class;
$className = B\X;
$object = new $className;
?>
Alternatives :
- Replace strings by the ::class operator whenever possible
ext/rar
[Since 0.8.7] - [ -P Extensions/Extrar ] - [ Online docs ]
Extension RAR.
Rar is a powerful and effective archiver created by Eugene Roshal. This extension gives you possibility to read Rar archives but doesn't support writing Rar archives, because this is not supported by the UnRar library and is directly prohibited by its license.
<?php
$arch = RarArchive::open(example.rar);
if ($arch === FALSE)
die(Cannot open example.rar);
$entries = $arch->getEntries();
if ($entries === FALSE)
die(Cannot retrieve entries);
?>
See also Rar archiving and rarlabs.
Don't Echo Error
[Since 0.8.7] - [ -P Security/DontEchoError ] - [ Online docs ]
It is recommended to avoid displaying error messages directly to the browser.
PHP's uses the display_errors directive to control display of errors to the browser. This must be kept to off when in production.
Error messages should be logged, but not displayed.
<?php
// Inside a 'or' test
mysql_connect('localhost', $user, $pass) or die(mysql_error());
// Inside a if test
$result = pg_query( $db, $query );
if( !$result )
{
echo Erreur SQL: . pg_error();
exit;
}
// Changing PHP configuration
ini_set('display_errors', 1);
// This is also a security error : 'false' means actually true.
ini_set('display_errors', 'false');
?>
Alternatives :
- Remove any echo, print, printf() call built with error messages from an exception, or external source.
Useless Type Casting
[Since 0.8.7] - [ -P Structures/UselessCasting ] - [ Online docs ]
There is no need to cast values that are already well typed.
Typed values, such as properties, do not have to be cast again, as the engine always ensures their type.
Typed arguments are variables : after the initial check at method call time, they might change value and type. Those extra cast may then carry some value, although changing the type of an incoming value is not recommended.
<?php
// trim always returns a string : cast is useless
$a = (string) trim($b);
// strpos doesn't always returns an integer : cast is useful
$a = (boolean) strpos($b, $c);
// comparison don't need casting, nor parenthesis
$c = (bool) ($b > 2);
function foo(array $a) {
foreach((array) $a as $b) {
// doSomething
}
}
?>
Alternatives :
- Remove the type cast
No isset() With empty()
[Since 0.8.7] - [ -P Structures/NoIssetWithEmpty ] - [ Online docs ]
empty() actually does the job of isset() too.
From the manual : No warning is generated if the variable does not exist. That means empty() is essentially the concise equivalent to !isset($var) || $var == false. The main difference is that isset() only works with variables, while empty() works with other structures, such as constants.
<?php
// Enough validation
if (!empty($a)) {
doSomething();
}
// Too many tests
if (isset($a) && !empty($a)) {
doSomething();
}
?>
Alternatives :
- Only use isset(), just drop the empty()
- Only use empty(), just drop the empty()
- Use a null value, so the variable is always set
Useless Check
[Since 0.8.9] - [ -P Structures/UselessCheck ] - [ Online docs ]
There is no need to check the size of an array content before using foreach. Foreach() applies a test on the source, and skips the loop if no element is found.
This analysis checks for conditions with sizeof() and count(). Conditions with isset() and empty() are omitted : they also check for the variable existence, and thus, offer extra coverage.
<?php
// Checking for type is good.
if (is_array($array)) {
foreach($array as $a) {
doSomething($a);
}
}
// Foreach on empty arrays doesn't start. Checking is useless
if (!empty($array)) {
foreach($array as $a) {
doSomething($a);
}
}
?>
Alternatives :
- Drop the condition and the check
- Turn the condition into isset(), empty() and is_array()
Bail Out Early
[Since 0.8.9] - [ -P Structures/BailOutEarly ] - [ Online docs ]
When using conditions, it is recommended to quit in the current context, and avoid the else clause altogether.
The main benefit is to make clear the method applies a condition, and stop immediately when this condition is not satisfied.
The main sequence is then focused on the important code.
This analysis works with the break , continue , throw and goto keywords too, depending on situations.
<?php
// Bailing out early, low level of indentation
function foo1($a) {
if ($a > 0) {
return false;
}
$a++;
return $a;
}
// Works with continue too
foreach($array as $a => $b) {
if ($a > 0) {
continue false;
}
$a++;
return $a;
}
// No need for else
function foo2($a) {
if ($a > 0) {
return false;
} else {
$a++;
}
return $a;
}
// No need for else : return goes into then.
function foo3($a) {
if ($a < 0) {
$a++;
} else {
return false;
}
return $a;
}
// Make a return early, and make the condition visible.
function foo3($a) {
if ($a < 0) {
$a++;
methodcall();
functioncall();
}
}
?>
Alternatives :
- Detect errors, and then, return as soon as possible.
- When a if...then branches are unbalanced, test for the small branch, finish it with return. Then keep the other branch as the main code.
Don't Change The Blind Var
[Since 0.8.9] - [ -P Structures/DontChangeBlindKey ] - [ Online docs ]
When using a foreach(), the blind variables hold a copy of the original value. It is confusing to modify them, as it seems that the original value may be changed.
When actually changing the original value, use the reference in the foreach definition to make it obvious, and save the final reassignation.
When the value has to be prepared before usage, then save the filtered value in a separate variable. This makes the clean value obvious, and preserve the original value for a future usage.
<?php
// $bar is duplicated and kept
$foo = [1, 2, 3];
foreach($foo as $bar) {
// $bar is updated but its original value is kept
$nextBar = $bar + 1;
print $bar . ' => ' . ($nextBar) . PHP_EOL;
foobar($nextBar);
}
// $bar is updated and lost
$foo = [1, 2, 3];
foreach($foo as $bar) {
// $bar is updated but its final value is lost
print $bar . ' => ' . (++$bar) . PHP_EOL;
// Now that $bar is reused, it is easy to confuse its value
foobar($bar);
}
// $bar is updated and kept
$foo = [1, 2, 3];
foreach($foo as &$bar) {
// $bar is updated and keept
print $bar . ' => ' . (++$bar) . PHP_EOL;
foobar($bar);
}
?>
Avoid Using stdClass
[Since 0.9.1] - [ -P Php/UseStdclass ] - [ Online docs ]
stdClass is the default class for PHP. It is instantiated when PHP needs to return a object, but no class is specifically available.
It is recommended to avoid instantiating this class. Some PHP or frameworks functions, such as json_encode(), do return them : this is fine, although it is reported here.
If you need a stdClass object, it is faster to build it as an array, then cast it, than instantiate stdClass . This is a micro-optimisation.
<?php
$json = '{"a":1,"b":2,"c":3}';
$object = json_decode($json);
// $object is a stdClass, as returned by json_decode
// Fast building of $o
$a = [];
$a['a'] = 1;
$a['b'] = 2;
$a['c'] = 3;
json_encode( (object) $a);
// Slow building of $o
$o = new stdClass();
$o->a = 1;
$o->b = 2;
$o->c = 3;
json_encode($o);
?>
Alternatives :
- Create a custom class to handle the properties
ext/nsapi
[Since 0.9.2] - [ -P Extensions/Extnsapi ] - [ Online docs ]
NSAPI specific functions calls.
These functions are only available when running PHP as a NSAPI module in Netscape/iPlanet/Sun webservers.
<?php
// This scripts depends on ext/nsapi
if (ini_get('nsapi.read_timeout') < 60) {
doSomething();
}
?>
See also Sun, iPlanet and Netscape servers on Sun Solaris.
ext/newt
[Since 0.8.4] - [ -P Extensions/Extnewt ] - [ Online docs ]
Newt PHP CLI extension.
This is a PHP language extension for RedHat Newt library, a terminal-based window and widget library for writing applications with user friendly interface.
<?php
newt_init ();
newt_cls ();
newt_draw_root_text (0, 0, "Test Mode Setup Utility 1.12");
newt_push_help_line (null);
newt_get_screen_size ($rows, $cols);
newt_open_window ($rows/2-17, $cols/2-10, 34, 17, "Choose a Tool");
$form = newt_form ();
$list = newt_listbox (3, 2, 10);
foreach (array (
"Authentication configuration",
"Firewall configuration",
"Mouse configuration",
"Network configuration",
"Printer configuration",
"System services") as $l_item)
{
newt_listbox_add_entry ($list, $l_item, $l_item);
}
$b1 = newt_button (5, 12, "Run Tool");
$b2 = newt_button (21, 12, "Quit");
newt_form_add_component ($form, $list);
newt_form_add_components ($form, array($b1, $b2));
newt_refresh ();
newt_run_form ($form);
newt_pop_window ();
newt_pop_help_line ();
newt_finished ();
newt_form_destroy ($form);
?>
See also Newt.
ext/ncurses
[Since 0.8.4] - [ -P Extensions/Extncurses ] - [ Online docs ]
Extension ncurses (CLI).
ncurses (new curses) is a free software emulation of curses in System V Rel 4.0 (and above).
<?php
ncurses_init();
ncurses_start_color();
ncurses_init_pair(1, NCURSES_COLOR_GREEN, NCURSES_COLOR_BLACK);
ncurses_init_pair(2, NCURSES_COLOR_RED, NCURSES_COLOR_BLACK);
ncurses_init_pair(3, NCURSES_COLOR_WHITE, NCURSES_COLOR_BLACK);
ncurses_color_set(1);
ncurses_addstr('OK ');
ncurses_color_set(3);
ncurses_addstr('Success!'.PHP_EOL);
ncurses_color_set(2);
ncurses_addstr('FAIL ');
ncurses_color_set(3);
ncurses_addstr('Success!'.PHP_EOL);
?>
See also Ncurses Terminal Screen Control and Ncurses.
Use Composer Lock
[Since 0.9.2] - [ -P Composer/UseComposerLock ] - [ Online docs ]
This rule reports when the composer.lock is committed to the archive. composer.lock stores the actual versions of the components that were fetched by composer, based on the composer.json . This is useful to store and share among developers.
Too Many Local Variables
[Since 0.9.2] - [ -P Functions/TooManyLocalVariables ] - [ Online docs ]
Too many local variables were found in the methods. When over 15 variables are found in such a method, a violation is reported.
Local variables exclude globals (imported with global) and arguments. Local variable include static variables.
When too many variables are used in a function, it is a code smells. The function is trying to do too much and needs extra space for juggling.
Beyond 15 variables, it becomes difficult to keep track of their name and usage, leading to confusion, overwriting or hijacking.
<?php
// This function is OK : 3 vars are arguments, 3 others are globals.
function a20a3g3($a1, $a2, $a3) {
global $a4, $a5, $a6;
$a1 = 1;
$a2 = 2;
$a3 = 3 ;
$a4 = 4 ;
$a5 = 5 ;
$a6 = 6 ;
$a7 = 7 ;
$a8 = 8 ;
$a9 = 9 ;
$a10 = 10;
$a11 = 11;
$a12 = 12;
$a13 = 13 ;
$a14 = 14 ;
$a15 = 15 ;
$a16 = 16 ;
$a17 = 17 ;
$a18 = 18 ;
$a19 = 19 ;
$a20 = 20;
}
// This function has too many variables
function a20() {
$a1 = 1;
$a2 = 2;
$a3 = 3 ;
$a4 = 4 ;
$a5 = 5 ;
$a6 = 6 ;
$a7 = 7 ;
$a8 = 8 ;
$a9 = 9 ;
$a10 = 10;
$a11 = 11;
$a12 = 12;
$a13 = 13 ;
$a14 = 14 ;
$a15 = 15 ;
$a16 = 16 ;
$a17 = 17 ;
$a18 = 18 ;
$a19 = 19 ;
$a20 = 20;
}
?>
Alternatives :
- Remove some of the variables, and inline them
- Break the big function into smaller ones
- Find repeated code and make it a separate function
Illegal Name For Method
[Since 0.9.2] - [ -P Classes/WrongName ] - [ Online docs ]
PHP has reserved usage of methods starting with __ for magic methods. It is recommended to avoid using this prefix, to prevent confusions.
<?php
class foo{
// Constructor
function __construct() {}
// Constructor's typo
function __constructor() {}
// Illegal function name, even as private
private function __bar() {}
}
?>
Alternatives :
- Avoid method names starting with a double underscore : __
- Use method visibilities to ensure that methods are only available to the current class or its children
String
[Since 0.9.4] - [ -P Extensions/Extstring ] - [ Online docs ]
Strings in PHP. Strings are part of the core of PHP, and are not a separate extension.
<?php
$str = "Mary Had A Little Lamb and She LOVED It So";
$str = strtolower($str);
echo $str; // Prints mary had a little lamb and she loved it so
?>
See also String functions.
ext/mongodb
[Since 0.9.5] - [ -P Extensions/Extmongodb ] - [ Online docs ]
Extension MongoDb.
Do not mistake with extension Mongo, the previous version.
Mongodb driver supports both PHP and HHVM and is developed atop the libmongoc and libbson libraries.
<?php
require 'vendor/autoload.php'; // include Composer's autoloader
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->demo->beers;
$result = $collection->insertOne( [ 'name' => 'Hinterland', 'brewery' => 'BrewDog' ] );
echo "Inserted with Object ID '{$result->getInsertedId()}'";
?>
See also MongoDB driver and MongdDb.
Long Arguments
[Since 0.9.7] - [ -P Structures/LongArguments ] - [ Online docs ]
Long arguments should be put in variable, to preserve readability.
When literal arguments are too long, they break the hosting structure by moving the next argument too far on the right. Whenever possible, long arguments should be set in a local variable to keep the readability.
Literal strings and heredoc strings, including variables, that are over 50 chars longs are reported here.
<?php
// Now the call to foo() is easier to read.
$reallyBigNumber = <<<BIGNUMBER
123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
BIGNUMBER
foo($reallyBigNumber, 2, '12345678901234567890123456789012345678901234567890');
// where are the next arguments ?
foo('123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890', 2, '123456789012345678901234567890123456789012345678901234567890');
// This is still difficult to read
foo(<<<BIGNUMBER
123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
BIGNUMBER
, 2, '123456789012345678901234567890123456789012345678901234567890');
?>
Alternatives :
- Put the long arguments in a separate variable, and use the variable in the second expression, reducing its total length
Assigned Twice
[Since 0.9.8] - [ -P Variables/AssignedTwiceOrMore ] - [ Online docs ]
The same variable is assigned twice in the same function.
While this is possible and quite common, it is also a good practice to avoid changing a value from one literal to another. It is far better to assign the new value to
Incremental changes to a variables are not reported here.
<?php
function foo() {
// incremental changes of $a;
$a = 'a';
$a++;
$a = uppercase($a);
$b = 1;
$c = bar($b);
// B changed its purpose. Why not call it $d?
$b = array(1,2,3);
// This is some forgotten debug
$e = $config->getSomeList();
$e = array('OneElement');
}
?>
Alternatives :
- Remove the first assignation
- Remove the second assignation
- Change the name of the variable in one or both cases
Error_Log() Usage
[Since 0.10.0] - [ -P Php/ErrorLogUsage ] - [ Online docs ]
Usage of error_log() function. This leads to checking the configuration of error_log in the PHP configuration directives.
<?php
error_log("logging message\n");
?>
No Boolean As Default
[Since 0.10.0] - [ -P Functions/NoBooleanAsDefault ] - [ Online docs ]
Default values should always be set up with a constant name.
Class constants and constants improve readability when calling the methods or comparing values and statuses.
<?php
const CASE_INSENSITIVE = true;
const CASE_SENSITIVE = false;
function foo($case_insensitive = true) {
// doSomething()
}
// Readable code
foo(CASE_INSENSITIVE);
foo(CASE_SENSITIVE);
// unreadable code : is true case insensitive or case sensitive ?
foo(true);
foo(false);
?>
Alternatives :
- Use constants or class constants to give value to a boolean literal
- When constants have been defined, use them when calling the code
- Split the method into two methods, one for each case
SQL queries
[Since 0.10.1] - [ -P Type/Sql ] - [ Online docs ]
SQL queries, detected in literal strings.
SQL queries are detected with keywords, inside literals or concatenations.
<?php
// SQL in a string
$query = 'SELECT name FROM users WHERE id = 1';
// SQL in a concatenation
$query = 'SELECT name FROM '.$table_users.' WHERE id = 1';
// SQL in a Heredoc
$query = <<<SQL
SELECT name FROM $table_users WHERE id = 1
SQL;
?>
ext/libsodium
[Since 0.10.2] - [ -P Extensions/Extlibsodium ] - [ Online docs ]
Extension for libsodium : in PECL until PHP 7.2, and in core ever since.
The Sodium crypto library (libsodium) is a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more.
Sodium supports a variety of compilers and operating systems, including Windows (with MinGW or Visual Studio, x86 and x64), iOS and Android.
The design choices emphasize security, and "magic constants" have clear rationales.
<?php
// Example from the docs : https://paragonie.com/book/pecl-libsodium/read/06-hashing.md#crypto-generichash
// Fast, unkeyed hash function.
// Can be used as a secure replacement for MD5
$h = \Sodium\crypto_generichash('msg');
// Fast, keyed hash function.
// The key can be of any length between \Sodium\CRYPTO_GENERICHASH_KEYBYTES_MIN
// and \Sodium\CRYPTO_GENERICHASH_KEYBYTES_MAX, in bytes.
// \Sodium\CRYPTO_GENERICHASH_KEYBYTES is the recommended length.
$h = \Sodium\crypto_generichash('msg', $key);
// Fast, keyed hash function, with user-chosen output length, in bytes.
// Output length can be between \Sodium\CRYPTO_GENERICHASH_BYTES_MIN and
// \Sodium\CRYPTO_GENERICHASH_BYTES_MAX.
// \Sodium\CRYPTO_GENERICHASH_BYTES is the default length.
$h = \Sodium\crypto_generichash('msg', $key, 64);
?>
See also PHP extension for libsodium and Using Libsodium in PHP Projects.
Forgotten Thrown
[Since 0.10.2] - [ -P Exceptions/ForgottenThrown ] - [ Online docs ]
This rule reports when an exception is instantiated, but not thrown. Often, this is a case of forgotten throw.
<?php
class MyException extends \Exception { }
if ($error !== false) {
// This looks like 'throw' was omitted
new MyException();
}
?>
Alternatives :
- Remove the instantiation expression.
- Add the throw to the new expression.
Multiple Alias Definitions Per File
[Since 0.10.3] - [ -P Namespaces/MultipleAliasDefinitionPerFile ] - [ Online docs ]
Avoid aliasing the same name with different aliases. This leads to confusion.
<?php
// first occurrence
use name\space\ClasseName;
// when this happens, several other uses are mentioned
// name\space\ClasseName has now two names
use name\space\ClasseName as anotherName;
?>
See also `Namespaces/MultipleAliasDefinition`_.
__DIR__ Then Slash
[Since 0.10.3] - [ -P Structures/DirThenSlash ] - [ Online docs ]
__DIR__ must be concatenated with a string starting with /.
The magic constant __DIR__ holds the name of the current directory, without final /. When it is used to build path, then the following path fragment must start with /. Otherwise, two directories names will be merged together.
<?php
// __DIR__ = /a/b/c
// $filePath = /a/b/c/g.php
// /a/b/c/d/e/f.txt : correct path
echo __DIR__.'/d/e/f.txt';
echo dirname($filePath).'/d/e/f.txt';
// /a/b/cd/e/f.txt : most probably incorrect path
echo __DIR__.'d/e/f.txt';
echo dirname($filePath).'d/e/f.txt';
?>
Alternatives :
- Add a check on __DIR__, as it may be '/' when run at the root of the server
- Add a '/' at the beginning of the path after __DIR__.
- Add a call to realpath() or file_exists(), before accessing the file.
self, parent, static Outside Class
[Since 0.10.3] - [ -P Classes/NoPSSOutsideClass ] - [ Online docs ]
self, parent and static should be called inside a class or trait. PHP lint won't report those situations.
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.
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.
Closures and arrow functions are reported here, though they might be rebound with a valid context before execution.
<?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();
}
?>
Alternatives :
- Remove the call to static, parent or self
- Make sure the closure is correctly binded before usage
Used Once Property
[Since 0.10.3] - [ -P Classes/UsedOnceProperty ] - [ Online docs ]
Property used once in their defining class.
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.
Setting properties with default values is a good way to avoid littering the code with literal values, and provide a single point of update (by extension, or by hardcoding) for all those situations. A constant is definitely better suited for this task.
<?php
class foo {
private $defaultCols = '*';
cont DEFAULT_COLUMNS = '*';
// $this->defaultCols holds a default value. Should be a constant.
function bar($table, $cols) {
// This is necessary to activate usage of default values
if (empty($cols)) {
$cols = $this->defaultCols;
}
$res = $this->query('SELECT '.$cols.' FROM '.$table);
// ....
}
// Upgraded version of bar, with default values
function bar2($table, $cols = self::DEFAULT_COLUMNS) {
$res = $this->query('SELECT '.$cols.' FROM '.$table);
// .....
}
}
?>
Alternatives :
- Remove the property, as it is probably not unused
- Add another usage of the property where it is useful
Property Used In One Method Only
[Since 0.10.3] - [ -P Classes/PropertyUsedInOneMethodOnly ] - [ Online docs ]
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.
This analysis consider that using the current object with a cast or with the get_object_vars() function is also a usage, and skip those properties.
Note : properties used only once are not returned by this analysis. They are omitted, and are available in the analysis `Used Once Property`_.
<?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);
}
}
?>
Alternatives :
- Drop the property, and inline the value
- Drop the property, and make the property a local variable
- Use the property in another method
ext/ds
[Since 0.10.4] - [ -P Extensions/Extds ] - [ Online docs ]
Extension Data Structures : Data structures.
<?php
$vector = new \Ds\Vector();
$vector->push('a');
$vector->push('b', 'c');
$vector[] = 'd';
print_r($vector);
?>
See also Efficient data structures for PHP 7.
No Need For Else
[Since 0.10.4] - [ -P Structures/NoNeedForElse ] - [ Online docs ]
Else is not needed when the Then ends with a break. A break may be the following keywords : break, continue, return, goto. Any of these send the execution somewhere in the code. The else block is then executed as the main sequence, only if the condition fails.
<?php
function foo() {
// Else may be in the main sequence.
if ($a1) {
return $a1;
} else {
$a++;
}
// Same as above, but negate the condition : if (!$a2) { return $a2; }
if ($a2) {
$a++;
} else {
return $a2;
}
// This is OK
if ($a3) {
return;
}
// This has no break
if ($a4) {
$a++;
} else {
$b++;
}
// This has no else
if ($a5) {
$a++;
}
}
?>
Alternatives :
- Remove else block, but keep the code
Strange Name For Constants
[Since 0.10.5] - [ -P Constants/StrangeName ] - [ Online docs ]
Those constants looks like a typo from other names.
<?php
// This code looks OK : DIRECTORY_SEPARATOR is a native PHP constant
$path = $path . DIRECTORY_SEPARATOR . $file;
// Strange name DIRECOTRY_SEPARATOR
$path = $path . DIRECOTRY_SEPARATOR . $file;
?>
Alternatives :
- Fix any typo in the spelling of the constants
- Tell us about common misspelling so we can upgrade this analysis
Too Many Finds
[Since 0.10.5] - [ -P Classes/TooManyFinds ] - [ Online docs ]
Too many methods called 'find*' in this class. It is may be time to consider the Specification pattern.
<?php
// quite a fishy interface
interface UserInterface {
public function findByEmail($email);
public function findByUsername($username);
public function findByFirstName($firstname);
public function findByLastName($lastname);
public function findByName($name);
public function findById($id);
public function insert($user);
public function update($user);
}
?>
Alternatives :
- Split the class into smaller classes
- Remove some of the find* methods
Use Cookies
[Since 0.10.6] - [ -P Php/UseCookies ] - [ Online docs ]
This code source uses cookies.
Cookie usage is spotted with the usage of setcookie(), setrawcookie() and header() with the 'Set-Cookie' header.
<?php
header('Set-Cookie: '.$name.'='.$value.'; EXPIRES'.$date.';');
// From the PHP Manual :
setcookie('TestCookie3', $value, time()+3600, '/~rasmus/', 'example.com', 1);
?>
See also Cookies.
Should Use SetCookie()
[Since 0.10.6] - [ -P Php/UseSetCookie ] - [ Online docs ]
Use setcookie() or setrawcookie(). Avoid using header() to do so, as the PHP native functions are more convenient and easier to spot during a refactoring.
setcookie() applies some encoding internally, for the value of the cookie and the date of expiration. Rarely, this encoding has to be skipped : then, use setrawencoding().
Both functions help by giving a checklist of important attributes to be used with the cookie.
<?php
// same as below
setcookie("myCookie", 'chocolate', time()+3600, "/", "", true, true);
// same as above. Slots for path and domain are omitted, but should be used whenever possible
header('Set-Cookie: myCookie=chocolate; Expires='.date('r', (time()+3600)).'; Secure; HttpOnly');
?>
Alternatives :
- Use setcookie() function, instead of header()
Check All Types
[Since 0.10.6] - [ -P Structures/CheckAllTypes ] - [ Online docs ]
When checking for type, avoid using else. Mention explicitly all tested types, and raise an exception when all available options have been exhausted : after all, this is when the code doesn't know how to handle the datatype.
PHP has a short list of scalar types : null, boolean, integer, real, strings, object, resource and array. When a variable is not holding one the the type, then it may be of any other type.
Most of the time, when using a simple is_string() / else test, this is relying on the conception of the code. By construction, the arguments may be one of two types : array or string.
What happens often is that in case of failure in the code (database not working, another class not checking its results), a third type is pushed to the structure, and it ends up breaking the execution.
The safe way is to check the various types all the time, and use the default case (here, the else) to throw exception() or test an assertion and handle the special case.
Using is_callable(), is_iterable() with this structure is fine : when variable is callable or not, while a variable is an integer or else.
Using a type test without else is also accepted here. This is a special treatment for this test, and all others are ignored. This aspect may vary depending on situations and projects.
<?php
// hasty version
if (is_array($argument)) {
$out = $argument;
} else {
// Here, $argument is NOT an array. What if it is an object ? or a NULL ?
$out = array($argument);
}
// Safe type checking : do not assume that 'not an array' means that it is the other expected type.
if (is_array($argument)) {
$out = $argument;
} elseif (is_string($argument)) {
$out = array($argument);
} else {
assert(false, '$argument is not an array nor a string, as expected!');
}
?>
Alternatives :
- Include a default case to handle all unknown situations
- Include and process explicit types as much as possible
Missing Cases In Switch
[Since 0.10.7] - [ -P Structures/MissingCases ] - [ Online docs ]
It seems that some cases are missing in this switch structure.
When comparing two different switch() structures, it appears that some cases are missing in one of them. The set of cases are almost identical, but one of the values are missing.
Switch() structures using strings as literals are compared in this analysis. When the discrepancy between two lists is below 25%, both switches are reported.
In the example, one may argue that the 'c' case is actually handled by the 'default' case. Otherwise, business logic may request that omission.
<?php
// This switch operates on a, b, c, d and default
switch($a) {
case 'a': doSomethingA(); break 1;
case 'b': doSomethingB(); break 1;
case 'c': doSomethingC(); break 1;
case 'd': doSomethingD(); break 1;
default: doNothing();
}
// This switch operates on a, b, d and default
switch($o->p) {
case 'a': doSomethingA(); break 1;
case 'b': doSomethingB(); break 1;
case 'd': doSomethingD(); break 1;
default: doNothing();
}
?>
Alternatives :
- Add the missing cases
- Add comments to mention that missing cases are processed in the default case
Group Use Declaration
[Since 0.10.7] - [ -P Php/GroupUseDeclaration ] - [ Online docs ]
This rule reports when a group use declaration is used. This is PHP feature since version 7.0, yet it is seldom used.
<?php
// Adapted from the RFC documentation
// Pre PHP 7 code
use some\name_space\ClassA;
use some\name_space\ClassB;
use some\name_space\ClassC as C;
use function some\name_space\fn_a;
use function some\name_space\fn_b;
use function some\name_space\fn_c;
use const some\name_space\ConstA;
use const some\name_space\ConstB;
use const some\name_space\ConstC;
// PHP 7+ code
use some\name_space\{ClassA, ClassB, ClassC as C};
use function some\name_space\{fn_a, fn_b, fn_c};
use const some\name_space\{ConstA, ConstB, ConstC};
?>
See also Group Use Declaration RFC and Using namespaces: Aliasing/Importing.
Repeated Regex
[Since 0.10.9] - [ -P Structures/RepeatedRegex ] - [ Online docs ]
Repeated regex should be centralized.
When a regex is repeatedly used in the code, it is getting harder to update.
Regex that are repeated at least once (aka, used twice or more) are reported. Regex that are dynamically build are not reported.
<?php
// Regex used several times, at least twice.
preg_match('/^abc_|^square$/i', $_GET['x']);
//.......
preg_match('/^abc_|^square$/i', $row['name']);
// This regex is dynamically built, so it is not reported.
preg_match('/^circle|^'.$x.'$/i', $string);
// This regex is used once, so it is not reported.
preg_match('/^circle|^square$/i', $string);
?>
Alternatives :
- Create a central library of regex
- Use the regex inventory to spot other regex that are close, and should be identical.
No Class In Global
[Since 0.10.9] - [ -P Php/NoClassInGlobal ] - [ Online docs ]
Avoid defining structures in Global namespace. Always prefer using a namespace. This will come handy later, either when publishing the code, or when importing a library, or even if PHP reclaims that name.
<?php
// Code prepared for later
namespace Foo {
class Bar {}
}
// Code that may conflict with other names.
namespace {
class Bar {}
}
?>
Alternatives :
- Use a specific namespace for your classes
Crc32() Might Be Negative
[Since 0.11.0] - [ -P Php/Crc32MightBeNegative ] - [ Online docs ]
crc32() may return a negative number, on 32 bits platforms.
According to the manual : Because PHP\'s integer type is signed many CRC32 checksums will result in negative integers on 32 bits platforms. On 64 bits installations, all crc32() results will be positive integers though.
<?php
// display the checksum with %u, to make it unsigned
echo sprintf('%u', crc32($str));
// turn the checksum into an unsigned hexadecimal
echo dechex(crc32($str));
// avoid concatenating crc32 to a string, as it may be negative on 32bits platforms
echo 'prefix'.crc32($str);
?>
See also `crc32() Could Use str_repeat()
[Since 0.11.0] - [ -P Structures/CouldUseStrrepeat ] - [ Online docs ]
Use str_repeat() or str_pad() instead of making a loop.
Making a loop to repeat the same concatenation is actually much longer than using str_repeat(). As soon as the loop repeats more than twice, str_repeat() is much faster. With arrays of 30, the difference is significant, though the whole operation is short by itself.
<?php
// This adds 7 'e' to $x
$x .= str_repeat('e', 7);
// This is the same as above,
for($a = 3; $a < 10; ++$a) {
$x .= 'e';
}
// here, $default must contains 7 elements to be equivalent to the previous code
foreach($default as $c) {
$x .= 'e';
}
?>
Alternatives :
- Use strrepeat() whenever possible
Suspicious Comparison
[Since 0.11.0] - [ -P Structures/SuspiciousComparison ] - [ Online docs ]
The comparison seems to be misplaced.
A comparison happens in the last argument, while the actual function expect another type : this may be the case of a badly placed parenthesis.
Original idea by Vladimir Reznichenko.
<?php
// trim expect a string, a boolean is given.
if (trim($str === '')){
}
// Just move the first closing parenthesis to give back its actual meaning
if (trim($str) === ''){
}
?>
Alternatives :
- Remove the comparison altogether
- Move the comparison to its right place : that, or more the parenthesis.
- This may be what is intended : just leave it.
Strings With Strange Space
[Since 0.11.0] - [ -P Type/StringWithStrangeSpace ] - [ Online docs ]
An invisible space may be mistaken for a normal space.
However, PHP does straight comparisons, and may fail at recognizing. This analysis reports when it finds such strange spaces inside strings.
PHP doesn't mistake space and tables for whitespace when tokenizing the code.
This analysis doesn't report Unicode Codepoint Notation : those are visible in the code.
<?php
// PHP 7 notation,
$a = "\u{3000}";
$b = " ";
// Displays false
var_dump($a === $b);
?>
Alternatives :
- Replace the odd spaces with a normal space
- If unsecable spaces are important for presentation, add them at the templating level.
No Empty Regex
[Since 0.11.1] - [ -P Structures/NoEmptyRegex ] - [ Online docs ]
PHP regex don't accept empty regex, nor regex with alphanumeric delimiter.
Most of those errors happen at execution time, when the regex is build dynamically, but still may end empty. At compile time, such error are made when the code is not tested before commit.
<?php
// No empty regex
preg_match('', $string, $r);
// Delimiter must be non-alphanumerical
preg_replace('1abc1', $string, $r);
// Delimiter must be non-alphanumerical
preg_replace('1'.$regex.'1', $string, $r);
?>
Alternatives :
- Fix the regex by adding regex delimiters
Alternative Syntax Consistence
[Since 0.11.2] - [ -P Structures/AlternativeConsistenceByFile ] - [ Online docs ]
PHP allows for two syntax : the alternative syntax, and the classic syntax.
The classic syntax is almost always used. When used, the alternative syntax is used in templates.
This analysis reports files that are using both syntax at the same time. This is confusing.
<?php
// Mixing both syntax is confusing.
foreach($array as $item) :
if ($item > 1) {
print "$item elementsn";
} else {
print "$item elementn";
}
endforeach;
?>
Randomly Sorted Arrays
[Since 0.11.2] - [ -P Arrays/RandomlySortedLiterals ] - [ Online docs ]
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];
?>
Alternatives :
- 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.
ext/sphinx
[Since 0.11.3] - [ -P Extensions/Extsphinx ] - [ Online docs ]
Extension for the Sphinx search server.
This extension provides bindings for Sphinx search client library.
<?php
$s = new SphinxClient;
$s->setServer("localhost", 6712);
$s->setMatchMode(SPH_MATCH_ANY);
$s->setMaxQueryTime(3);
$result = $s->query("test");
var_dump($result);
?>
See also Sphinx Client and Sphinx Search.
Try With Multiple Catch
[Since 0.11.3] - [ -P Php/TryMultipleCatch ] - [ Online docs ]
Try may be used with multiple catch clauses.
<?php
try {
OneCatch();
} catch (FirstException $e) {
}
try {
TwoCatches();
} catch (FirstException $e) {
} catch (SecondException $e) {
}
?>
See also Exceptions.
ext/grpc
[Since 0.11.3] - [ -P Extensions/Extgrpc ] - [ Online docs ]
Extension for GRPC : A high performance, open-source universal RPC framework.
<?php
//https://github.com/grpc/grpc/blob/master/examples/php/greeter_client.php
require dirname(__FILE__).'/vendor/autoload.php';
// The following includes are needed when using protobuf 3.1.0
// and will suppress warnings when using protobuf 3.2.0+
@include_once dirname(__FILE__).'/helloworld.pb.php';
@include_once dirname(__FILE__).'/helloworld_grpc_pb.php';
function greet($name)
{
$client = new Helloworld\GreeterClient('localhost:50051', [
'credentials' => Grpc\ChannelCredentials::createInsecure(),
]);
$request = new Helloworld\HelloRequest();
$request->setName($name);
list($reply, $status) = $client->SayHello($request)->wait();
$message = $reply->getMessage();
return $message;
}
$name = !empty($argv[1]) ? $argv[1] : 'world';
echo greet($name)."\n";
?>
See also GRPC and GRPC on PECL.
Only Variable Passed By Reference
[Since 0.11.3] - [ -P Functions/OnlyVariablePassedByReference ] - [ Online docs ]
When an argument is expected by reference, it is compulsory to provide a container. A container may be a variable, an array, a property or a static property.
This may be linted by PHP, when the function definition is in the same file as the function usage. This is silently linted if definition and usage are separated, if the call is dynamical or made as a method.
This analysis currently covers functioncalls and static methodcalls, but omits methodcalls.
<?php
function foo(&$bar) { /**/ }
function &bar() { /**/ }
// This is not possible : strtolower() returns a value
foo(strtolower($string));
// This is valid : bar() returns a reference
foo(bar($string));
?>
Alternatives :
- Store the previous result in a variable, and then call the function.
No Return Used
[Since 0.11.3] - [ -P Functions/NoReturnUsed ] - [ Online docs ]
The return value of the following methods are never used. The return argument may be dropped from the code, as it is dead code.
This analysis supports functions and static methods, when a definition may be found. It doesn't support method calls.
<?php
function foo($a = 1) { return 1; }
foo();
foo();
foo();
foo();
foo();
foo();
// This function doesn't return anything.
function foo2() { }
// The following function are used in an expression, thus the return is important
function foo3() { return 1;}
function foo4() { return 1;}
function foo5() { return 1;}
foo3() + 1;
$a = foo4();
foo(foo5());
?>
Alternatives :
- Remove the return statement in the function
- Actually use the value returned by the method, for test or combination with other values
Use Browscap
[Since 0.11.4] - [ -P Php/UseBrowscap ] - [ Online docs ]
Browscap is a browser database, accessible via get_browser().
Browscap is the 'Browser Capabilities Project'.
<?php
echo $_SERVER['HTTP_USER_AGENT'] . "\n\n";
$browser = get_browser(null, true);
print_r($browser);
?>
See also browscap.
Use Debug
[Since 0.11.4] - [ -P Structures/UseDebug ] - [ Online docs ]
The code source includes calls to debug functions.
The following debug functions and libraries are reported :
- Aronduby Dump
- Cakephp Debug Toolbar
- Kint
- Krumo
- Nette tracy
- php-debugbar
- PHP native functions : print_r(), var_dump(), debug_backtrace(), debug_print_backtrace(), debug_zval_dump()
- Symfony debug
- Wordpress debug
- Xdebug
<?php
// Example with Zend Debug
Zend\Debug\Debug::dump($var, $label = null, $echo = true);
?>
No Reference On Left Side
[Since 0.11.5] - [ -P Structures/NoReferenceOnLeft ] - [ Online docs ]
Do not use references as the right element in an assignation.
This is the case for most situations : addition, multiplication, bitshift, logical, power, concatenation.
Note that PHP won't compile the code if the operator is a short operator (+=, .=, etc.), nor if the & is on the right side of the operator.
<?php
$b = 2;
$c = 3;
$a = &$b + $c;
// $a === 2 === $b;
$a = $b + $c;
// $a === 5
?>
See also References Explained and Operator Precedence.
Implemented Methods Must Be Public
[Since 0.11.5] - [ -P Classes/ImplementedMethodsArePublic ] - [ Online docs ]
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, and 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() {}
}
?>
Alternatives :
- Make the implemented method public
PSR-16 Usage
[Since 0.11.6] - [ -P Psr/Psr16Usage ] - [ Online docs ]
PSR-16 describes a simple yet extensible interface for a cache item and a cache driver. It is supported by an set of interfaces, that one may use in the code.
<?php
namespace My\SimpleCache;
// MyCache implements the PSR-16 Simple cache.
// MyCache is more of a black hole than a real cache.
class MyCache implements Psr\SimpleCache\CacheInterface {
public function get($key, $default = null) {}
public function set($key, $value, $ttl = null) {}
public function delete($key) {}
public function clear() {}
public function getMultiple($keys, $default = null) {}
public function setMultiple($values, $ttl = null) {}
public function deleteMultiple($keys) {}
public function has($key) {}
}
?>
See also PSR-16 : Common Interface for Caching Libraries.
PSR-7 Usage
[Since 0.11.6] - [ -P Psr/Psr7Usage ] - [ Online docs ]
PSR-7 describes common interfaces for representing HTTP messages as described in RFC 7230 and RFC 7231, and URI for use with HTTP messages as described in RFC 3986.
It is supported by an set of interfaces, that one may use in the code.
<?php
namespace MyNamespace;
// MyServerRequest implements the PSR-7 ServerRequestInterface.
// MyServerRequest is more of a black hole than a real Server.
class MyServerRequest extends \Psr\Http\Message\ServerRequestInterface {
public function getServerParams() {}
public function getCookieParams() {}
public function withCookieParams(array $cookies) {}
public function getQueryParams() {}
public function withQueryParams(array $query) {}
public function getUploadedFiles() {}
public function withUploadedFiles(array $uploadedFiles) {}
public function getParsedBody() {}
public function withParsedBody($data) {}
public function getAttributes() {}
public function getAttribute($name, $default = null) {}
public function withAttribute($name, $value) {}
public function withoutAttribute($name) {}
}
?>
See also PSR-7 : HTTP message interfaces.
PSR-6 Usage
[Since 0.11.6] - [ -P Psr/Psr6Usage ] - [ Online docs ]
PSR-6 is the cache standard for PHP.
The goal of PSR-6 is to allow developers to create cache-aware libraries that can be integrated into existing frameworks and systems without the need for custom development.
It is supported by an set of interfaces, that one may use in the code.
<?php
namespace MyNamespace;
// MyCacheItem implements the PSR-7 CacheItemInterface.
// This MyCacheItem is more of a black hole than a real CacheItem.
class MyCacheItem implements \Psr\Cache\CacheItemInterface {
public function getKey() {}
public function get() {}
public function isHit() {}
public function set($value) {}
public function expiresAt($expiration) {}
public function expiresAfter($time) {}
}
?>
See also PSR-6 : Caching.
PSR-3 Usage
[Since 0.11.6] - [ -P Psr/Psr3Usage ] - [ Online docs ]
PSR-3 describes a common interface for logging libraries.
It is supported by an set of interfaces, that one may use in the code.
<?php
namespace MyNamespace;
// MyLog implements the PSR-3 LoggerInterface.
// MyLog is more of a black hole than a real Log.
namespace ;
class MyLog implements \Psr\Log\LoggerInterface {
public function emergency($message, array $context = array()) {}
public function alert($message, array $context = array()) {}
public function critical($message, array $context = array()) {}
public function error($message, array $context = array()) {}
public function warning($message, array $context = array()) {}
public function notice($message, array $context = array()) {}
public function info($message, array $context = array()) {}
public function debug($message, array $context = array()) {}
public function log($level, $message, array $context = array()) {}
}
?>
See also PSR-3 : Logger Interface.
PSR-11 Usage
[Since 0.11.5] - [ -P Psr/Psr11Usage ] - [ Online docs ]
PSR-11 describes a common interface for dependency injection containers.
It is supported by an set of interfaces, that one may use in the code.
<?php
namespace MyNamespace;
// MyContainerInterface implements the PSR-7 ServerRequestInterface.
// MyContainerInterface is more of a black hole than a real Container.
class MyContainerInterface implements \Psr\Container\ContainerInterface {
public function get($id) {}
public function has($id) {}
}
?>
See also PSR-11 : Dependency injection container.
PSR-13 Usage
[Since 0.11.6] - [ -P Psr/Psr13Usage ] - [ Online docs ]
PSR-13 describes a common interface for dependency injection containers.
It is supported by an set of interfaces, that one may use in the code.
<?php
namespace MyNamespace;
// MyLink implements the PSR-13 LinkInterface.
// MyLink is more of a black hole than a real Container.
class MyLink implements LinkInterface {
public function getHref() {}
public function isTemplated() {}
public function getRels() {}
public function getAttributes() {}
}
?>
See also PSR-13 : Link definition interface.
Mixed Concat And Interpolation
[Since 0.11.5] - [ -P Structures/MixedConcatInterpolation ] - [ Online docs ]
Mixed usage of concatenation and string interpolation is error prone. It is harder to read, and leads to overlooking the concatenation or the interpolation.
Fixing this issue has no impact on the output. It makes code less error prone.
There are some situations where using concatenation are compulsory : when using a constant, calling a function, running a complex expression or make use of the escape sequence. You may also consider pushing the storing of such expression in a local variable.
<?php
// Concatenation string
$a = $b . 'c' . $d;
// Interpolation strings
$a = "{$b}c{$d}"; // regular form
$a = "{$b}c$d"; // irregular form
// Mixed Concatenation and Interpolation string
$a = "{$b}c" . $d;
$a = $b . "c$d";
$a = $b . "c{$d}";
// Mixed Concatenation and Interpolation string with constant
$a = "{$b}c" . CONSTANT;
?>
Alternatives :
- Only use one type of variable usage : either interpolation, or concatenation
ext/stats
[Since 0.11.5] - [ -P Extensions/Extstats ] - [ Online docs ]
Statistics extension.
This extension contains few dozens of functions useful for statistical computations. It is a wrapper around 2 scientific libraries, namely DCDFLIB (Library of C routines for Cumulative Distributions Functions, Inverses, and Other parameters) by B. Brown & J. Lavato and RANDLIB by Barry Brown, James Lavato & Kathy Russell.
<?php
$x = [ 15, 16, 8, 6, 15, 12, 12, 18, 12, 20, 12, 14, ];
$y = [ 17.24, 15, 14.91, 4.5, 18, 6.29, 19.23, 18.69, 7.21, 42.06, 7.5, 8,];
sprintf("%2.9f", stats_covariance($a_1, $a_2));
?>
See also Statistics and ext/stats.
Too Many Injections
[Since 0.11.6] - [ -P Classes/TooManyInjections ] - [ Online docs ]
When a class is constructed with more than four dependencies, it should be split into smaller classes.
<?php
// This class relies on 5 other instances.
// It is probably doing too much.
class Foo {
public function __construct(
A $a,
B $b,
C $c,
D $d
E $e ) {
$this->a = $a;
$this->b = $b;
$this->d = $d;
$this->d = $d;
$this->e = $e;
}
}
?>
Alternatives :
- Split the class into smaller classes. Try to do less in that class.
Dependency Injection
[Since 0.11.6] - [ -P Patterns/DependencyInjection ] - [ Online docs ]
A dependency injection is a typehinted argument, that is stored in a property by the constructor.
<?php
// Classic dependency injection
class foo {
private $bar;
public function __construct(Bar $bar) {
$this->bar = $bar;
}
public function doSomething($args) {
return $this->bar->barbar($args);
}
}
// Without typehint, this is not a dependency injection
class foo {
private $bar;
public function __construct($bar) {
$this->bar = $bar;
}
}
?>
See also Understanding Dependency Injection.
Courier Anti-Pattern
[Since 0.11.6] - [ -P Patterns/CourrierAntiPattern ] - [ Online docs ]
The courier anti-pattern is the storage of a dependency by a class, in order to create an instance that requires this dependency.
The class itself doesn't actually need this dependency, but has a dependency to a class that requires it.
The alternative here is to inject Foo instead of Bar.
<?php
// The foo class requires bar
class Foo {
public function __construct(Bar $b) {
}
}
// Class A doesn't depends on Bar, but depends on Foo
// Class A never uses Bar, but only uses Foo.
class A {
private $courier;
public function __construct(Bar $courier) {
$this->courier = $courier;
}
public function Afoo() {
$b = new Foo($this->courier);
}
}
?>
See also Courier Anti-pattern.
ext/gender
[Since 0.11.6] - [ -P Extensions/Extgender ] - [ Online docs ]
Gender extension.
The Gender PHP extension is a port of the gender.c program originally written by Joerg Michael. Its main purpose is to find out the gender of firstnames, based on a database of over 40000 firstnames from 54 countries.
<?php
namespace Gender;
$gender = new Gender;
$name = 'Milene';
$country = Gender::FRANCE;
$result = $gender->get($name, $country);
$data = $gender->country($country);
switch($result) {
case Gender::IS_FEMALE:
printf('The name %s is female in %s\n', $name, $data['country']);
break;
case Gender::IS_MOSTLY_FEMALE:
printf('The name %s is mostly female in %s\n', $name, $data['country']);
break;
case Gender::IS_MALE:
printf('The name %s is male in %s\n', $name, $data['country']);
break;
case Gender::IS_MOSTLY_MALE:
printf('The name %s is mostly male in %s\n', $name, $data['country']);
break;
case Gender::IS_UNISEX_NAME:
printf('The name %s is unisex in %s\n', $name, $data['country']);
break;
case Gender::IS_A_COUPLE:
printf('The name %s is both male and female in %s\n', $name, $data['country']);
break;
case Gender::NAME_NOT_FOUND:
printf('The name %s was not found for %s\n', $name, $data['country']);
break;
case Gender::ERROR_IN_NAME:
echo 'There is an error in the given name!'.PHP_EOL;
break;
default:
echo 'An error occurred!'.PHP_EOL;
break;
}
?>
See also ext/gender manual and genderReader.
ext/judy
[Since 0.11.6] - [ -P Extensions/Extjudy ] - [ Online docs ]
The Judy extension.
PHP Judy is a PECL extension for the Judy C library implementing dynamic sparse arrays.
<?php
$judy = new Judy(Judy::BITSET);
if ($judy->getType() === judy_type($judy) &&
$judy->getType() === Judy::BITSET) {
echo 'Judy BITSET type OK'.PHP_EOL;
} else {
echo 'Judy BITSET type check fail'.PHP_EOL;
}
unset($judy);
?>
See also php-judy.
Could Make A Function
[Since 0.11.6] - [ -P Functions/CouldCentralize ] - [ Online docs ]
When a function is called across the code with the same arguments often enough, it should be turned into a local API.
This approach is similar to turning literals into constants : it centralize the value, it helps refactoring by updating it. It also makes the code more readable. Moreover, it often highlight common grounds between remote code locations.
The analysis looks for functions calls, and checks the arguments. When the calls occurs more than 4 times, it is reported.
<?php
// str_replace is used to clean '&' from strings.
// It should be upgraded to a central function
function foo($arg ) {
$arg = str_replace('&', '', $arg);
// do something with $arg
}
class y {
function bar($database ) {
$value = $database->queryName();
$value = str_replace('&', '', $value);
// $value = removeAmpersand($value);
// do something with $arg2
}
}
// helper function
function removeAmpersand($string) {
return str_replace('&', '', $string);
}
?>
Alternatives :
- Create a constant for common pieces of data
- Create a function based on context-free repeated elements
- Create a class based on repeated elements with dependent values
Forgotten Interface
[Since 0.11.7] - [ -P Interfaces/CouldUseInterface ] - [ Online docs ]
The following classes have been found implementing an interface's methods, though it doesn't explicitly implements this interface. This may have been forgotten.
<?php
interface i {
function i();
}
// i is not implemented and declared
class foo {
function i() {}
function j() {}
}
// i is implemented and declared
class foo implements i {
function i() {}
function j() {}
}
?>
Alternatives :
- Mention interfaces explicitly whenever possible
Yii usage
[Since 0.11.8] - [ -P Vendors/Yii ] - [ Online docs ]
This analysis reports usage of the Yii 2 framework.
This analysis targets Yii 2, not Yii 1.
<?php
// A Yii controller
class SiteController extends \Yii\Web\Controller
{
public function actionIndex()
{
// ...
}
public function actionContact()
{
// ...
}
}
?>
See also Yii.
Codeigniter usage
[Since 0.11.8] - [ -P Vendors/Codeigniter ] - [ Online docs ]
This analysis reports usage of the Codeigniter 4 framework.
Note : Code igniter 3 and older are not reported.
<?php
// A code igniter controller
class Blog extends \App\Controllers\Home {
public function index()
{
echo 'Hello World!';
}
}
?>
See also Codeigniter.
Laravel usage
[Since 0.11.8] - [ -P Vendors/Laravel ] - [ Online docs ]
This analysis reports usage of the Laravel framework.
<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* @param int $id
* @return Response
*/
public function show($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}
?>
See also Laravel.
Symfony usage
[Since 0.11.8] - [ -P Vendors/Symfony ] - [ Online docs ]
This analysis reports usage of the Symfony framework.
<?php
// src/AppBundle/Controller/LuckyController.php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Response;
class LuckyController
{
/**
* @Route("/lucky/number")
*/
public function numberAction()
{
$number = mt_rand(0, 100);
return new Response(
'<html><body>Lucky number: '.$number.'</body></html>'
);
}
}
?>
See also Symfony.
Wordpress usage
[Since 0.11.8] - [ -P Vendors/Wordpress ] - [ Online docs ]
This analysis reports usage of the Wordpress platform.
The current supported version is Wordpress 5.8.
<?php
//Usage of the WP_http class from Wordpress
$rags = array(
'x' => '1',
'y' => '2'
);
$url = 'http://www.example.com/';
$request = new WP_Http();
$result = $request->request( $url, array( 'method' => 'POST', 'body' => $body) );
?>
See also Wordpress.
Joomla usage
[Since 0.11.8] - [ -P Vendors/Joomla ] - [ Online docs ]
This analysis reports usage of the Joomla CMS.
<?php
// no direct access
defined('_JEXEC') or die('Restricted access');
jimport('joomla.application.component.controller');
JLoader::import('KBIntegrator', JPATH_PLUGINS . DS . 'kbi');
class MyController extends JController {
function display($message) {
echo $message;
}
}
?>
See also Joomla.
Non Breakable Space In Names
[Since 0.12.0] - [ -P Structures/NonBreakableSpaceInNames ] - [ Online docs ]
PHP allows non-breakable spaces in structures names, such as class, interfaces, traits, and variables.
This may be a nice trick to make names more readable outside code context, like long-named methods for tests.
Original post by Matthieu Napoli .
.
<?php
class class with non breakable spaces {}
class ClassWithoutNonBreakableSpaces {}
?>
See also Using non-breakable spaces in test method names and PHP Variable Names.
Multiple Functions Declarations
[Since 0.12.0] - [ -P Functions/MultipleDeclarations ] - [ Online docs ]
Some functions are declared multiple times in the code.
PHP accepts multiple definitions for the same functions, as long as they are not in the same file (linting error), or not included simultaneously during the execution.
This creates to several situations in which the same functions are defined multiple times : the function may be compatible with various PHP version, but their implementation may not. Or the function is part of a larger library, and sometimes only need without the rest of the library.
It is recommended to avoid having several functions with the same name in one repository. Turn those functions into methods and load them when needed.
<?php
namespace a {
function foo() {}
}
// Other file
namespace a {
function foo() {}
function bar() {}
}
?>
Avoid Optional Properties
[Since 0.12.0] - [ -P Classes/AvoidOptionalProperties ] - [ Online docs ]
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.');
}
}
}
?>
Alternatives :
- Use a null object to fill any missing value
- Make sure the property is set at constructor time
Swoole
[Since 0.12.0] - [ -P Extensions/Extswoole ] - [ Online docs ]
Swoole : Production-Grade Async programming Framework for PHP.
Swoole is an event-driven asynchronous & concurrent networking communication framework with high performance written only in C for PHP.
<?php
for($i = 0; $i < 100; $i++) {
Swoole\Coroutine::create(function() use ($i) {
$redis = new Swoole\Coroutine\Redis();
$res = $redis->connect('127.0.0.1', 6379);
$ret = $redis->incr('coroutine');
$redis->close();
if ($i == 50) {
Swoole\Coroutine::create(function() use ($i) {
$redis = new Swoole\Coroutine\Redis();
$res = $redis->connect('127.0.0.1', 6379);
$ret = $redis->set('coroutine_i', 50);
$redis->close();
});
}
});
}
?>
See also Swoole and Swoole src.
Manipulates NaN
[Since 0.10.6] - [ -P Php/IsNAN ] - [ Online docs ]
This code handles Not-a-Number situations. Not-a-Number , also called NaN , happens when a calculation can't return an actual float.
<?php
// acos returns a float, unless it is not possible.
$a = acos(8);
var_dump(is_nan($a));
?>
Alternatives :
- Add the third argument, and set it to true
Manipulates INF
[Since 0.10.6] - [ -P Php/IsINF ] - [ Online docs ]
This code handles INF situations. INF represents the infinity, when used in a float context. It happens when a calculation returns a number that is much larger than the maximum allowed float (not integer), or a number that is not a Division by 0.
<?php
// pow returns INF, as it is equivalent to 1 / 0 ^ 2
$a = pow(0,-2); //
// exp returns an actual value, but won't be able to represent it as a float
$a = exp(PHP_INT_MAX);
// 0 ^ -1 is like 1 / 0 but returns INF.
$a = pow(0, -1);
var_dump(is_infinite($a));
// This yields a Division by zero exception
$a = 1 / 0;
?>
See also Math predefined constants.
Const Or Define
[Since 0.12.1] - [ -P Structures/ConstDefineFavorite ] - [ Online docs ]
const and define()
The analyzed code has less than 10% of one of them : for consistency reasons, it is recommended to make them all the same.
They are almost interchangeable, though not totally : define()
<?php
// be consistent
const A1 = 1 ;
const A2 = 2 ;
const A3 = 3 ;
const A4 = 4 ;
const A5 = 5 ;
const A6 = 6 ;
const A7 = 7 ;
const A8 = 8 ;
const A9 = 9 ;
const A10 = 10;
const A11 = 11;
define('A12', 12); // Be consistent, always use the same.
?>
See also define and const.
strict_types Preference
[Since 0.12.2] - [ -P Php/DeclareStrict ] - [ Online docs ]
strict_types is a PHP mode where typehint are enforced strictly or weakly. By default, it is weak typing, allowing backward compatibility with previous versions.
This analysis reports if strict_types are used systematically or not. strict_types affects the calling file, not the definition file.
<?php
// define strict_types
declare(strict_types = 1);
foo(1);
?>
Alternatives :
- Use strict_types as early as possible in the development, to make it easier to adopt
Declare strict_types Usage
[Since 0.12.1] - [ -P Php/DeclareStrictType ] - [ Online docs ]
Usage of strict_types . By default, PHP attempts to change the original type to match the type specified by the type-declaration. With an explicit strict_types declaration, PHP ensures that the incoming argument has the exact type.
strict_types were introduced in PHP 7.0.
<?php
// Setting strict_types;
declare(strict_types = 1);
function foo(int $i) {
echo $i;
}
// Always valid : displays 1
foo(1);
// with strict types, this emits an error
// without strict types, this displays 1
foo(1.7);
?>
See also declare.
Encoding Usage
[Since 0.12.1] - [ -P Php/DeclareEncoding ] - [ Online docs ]
Usage of declare(encoding = );.
<?php
// Setting encoding for the file;
declare(encoding = 'UTF-8');
?>
See also declare.
Ticks Usage
[Since 0.12.1] - [ -P Php/DeclareTicks ] - [ Online docs ]
Usage of declare() with ticks . When ticks are declared, a related handler must be registered with register_tick_function().
<?php
// Setting ticks value
declare(ticks = 2);
?>
See also declare.
Mismatched Ternary Alternatives
[Since 0.12.1] - [ -P Structures/MismatchedTernary ] - [ Online docs ]
A ternary operator should yield the same type on both branches.
Ternary operator applies a condition, and yield two different results. Those results will then be processed by code that expects the same types. It is recommended to match the types on both branches of the ternary operator.
<?php
// $object may end up in a very unstable state
$object = ($type == 'Type') ? new $type() : null;
//same result are provided by both alternative, though process is very different
$result = ($type == 'Addition') ? $a + $b : $a * $b;
//Currently, this is omitted
$a = 1;
$result = empty($condition) ? $a : 'default value';
$result = empty($condition) ? $a : getDefaultValue();
?>
Alternatives :
- Use compatible data type in both branch of the alternative
- Turn the ternary into a if/then, with different processing
Mismatched Default Arguments
[Since 0.12.3] - [ -P Functions/MismatchedDefaultArguments ] - [ Online docs ]
Arguments are relayed from one method to the other, and the arguments have different default values.
Although it is possible to have different default values, it is worth checking why this is actually the case.
This analysis reports the original arguments. Starting from it, follow the usage of the argument in its method, and find calls to other methods.
This analysis omits reporting argument when one of them does not have a default value.
<?php
function foo($a = null, $b = array() ) {
// foo method calls directly bar.
// When argument are provided, it's OK
// When argument are omited, the default value is not the same as the next method
bar($a, $b);
}
function bar($c = 1, $d = array() ) {
}
?>
Alternatives :
- Synchronize default values to avoid surprises
- Drop some of the default values
Mismatched Typehint
[Since 0.12.3] - [ -P Functions/MismatchedTypehint ] - [ Online docs ]
Relayed arguments don't have the same typehint.
Typehint acts as a filter method. When an object is checked with a first class, and then checked again with a second distinct class, the whole process is always false : $a can't be of two different classes at the same time.
Note : This analysis currently doesn't check generalisation of classes : for example, when B is a child of BB, it is still reported as a mismatch.
<?php
// Foo() calls bar()
function foo(A $a, B $b) {
bar($a, $b);
}
// $a is of A typehint in both methods, but
// $b is of B then BB typehing
function bar(A $a, BB $b) {
}
?>
Alternatives :
- Ensure that the default value match the expected typehint.
Scalar Or Object Property
[Since 0.12.3] - [ -P Classes/ScalarOrObjectProperty ] - [ Online docs ]
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;
}
}
?>
Alternatives :
- Only use one type of syntax with your properties.
Assign And Lettered Logical Operator Precedence
[Since 0.12.4] - [ -P Php/AssignAnd ] - [ Online docs ]
The lettered logical operators and , or and xor have lower precedence than assignation. It collects less information than expected.
When that precedence is taken into account, this is valid and useful code. Yet, as it is rare and surprising to many developers, it is recommended to avoid it.
It is recommended to use the &&, ^ and || operators, instead of and, or and xor, to prevent confusion.
<?php
// The expected behavior is
// The following are equivalent
$a = $b && $c;
$a = ($b && $c);
// The unexpected behavior is
// The following are equivalent
$a = $b and $c;
($a = $b) and $c;
// Here, the result is collected. That result would not make use of the result of the throw expression
$a = doSomething() or throw new Exception('Error happened');
?>
Alternatives :
- Use symbolic operators rather than letter ones
- To be safe, add parenthesis to enforce priorities
No Magic Method With Array
[Since 0.12.4] - [ -P Classes/NoMagicWithArray ] - [ Online docs ]
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".
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.
<?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();
?>
Alternatives :
- Use a distinct method to append a new value to that property
- Assign the whole array, and not just one of its elements
ext/xattr
[Since 0.12.4] - [ -P Extensions/Extxattr ] - [ Online docs ]
Extensions xattr.
The xattr extension allows for the manipulation of extended attributes on a filesystem.
<?php
$file = 'my_favourite_song.wav';
xattr_set($file, 'Artist', 'Someone');
xattr_set($file, 'My ranking', 'Good');
xattr_set($file, 'Listen count', '34');
/* ... other code ... */
printf('You\'ve played this song %d times', xattr_get($file, 'Listen count'));
?>
See also xattr and Extended attributres.
Logical To in_array
[Since 0.12.5] - [ -P Performances/LogicalToInArray ] - [ Online docs ]
Multiple exclusive comparisons with or may be replaced by faster alternative.
- isset() and an array which keys are the target comparisons
- array_key_exists() and an array which keys are the target comparisons
- strpos() call, with all the target values merged into a string
- str_contains() call, with all the target values merged into a string
- switch() call, with each case being an assignation
- match() call
- in_array() call, with each values in an array
While each alternative has its performance gain, they make the code more readable by bringing the alternative values into one simple list.
As little as three or comparisons are slower than using an alternative. The more calls, the slower is as string of or . Also, the further the target value is in the or list, the slower it is to find it. Although, it is not easy to control that value.
This analysis also reports in_array() calls with arrays of a single element : those should be turned into a or call, or have more values in the array, or have the array published as a constant.
This is a micro-optimisation : speed gain is low, and marginal. Code centralisation is a more significant advantage.
Thanks to Frederic Bouchery for extending the alternatives of that analysis.
<?php
$targetValues = array('a', 'b', 'c', 'd');
$needle = 'd'; // for example
// isset() & array_key_exists()
$targets = array_flip($targetValues); // This might be a slow operation
isset($targs[$a]);
array_key_exists($a, $targs);
// strpos() & str_contains
$targets = implode('', $targeValues);
strpos($targets, $needle) !== 0
str_contains($targets, $needle) !== 0
// switch()
switch($needle) {
case 'a': // Lots of typing to do
case 'b':
case 'c':
case 'd':
$result = true;
break;
default:
$result = false;
break;
}
// match()
// surprisingly, slitghly slower than switch()
$result = match($needle) {
'a', 'b', 'c', 'd' => true,
default => false
};
// in_array()
// Set the list of alternative in a variable, property or constant.
$result = in_array($a, $valid_values, true); // use third argument when you can
// slowest and hard to read
$result = $a == 'a' || $a == 'b' || $a == 'c' || $a == 'd');
?>
Alternatives :
- Replace the list of comparisons with a in_array() call on an array filled with the various values
- Replace the list of comparisons with a strpos() call on an string joined with the various values
- Replace the list of comparisons with a match() call on an string joined with the various values
- Replace the list of comparisons with a switch() call on an string joined with the various values
- Replace the list of comparisons with a isset() call on a hash whose keys are the various values
ext/rdkafka
[Since 0.12.8] - [ -P Extensions/Extrdkafka ] - [ Online docs ]
Extension for RDkafka.
PHP-rdkafka is a thin librdkafka binding providing a working PHP 5 / PHP 7 Kafka 0.8 / 0.9 / 0.10 client.
<?php
$rk = new RdKafka\Producer();
$rk->setLogLevel(LOG_DEBUG);
$rk->addBrokers("10.0.0.1,10.0.0.2");
?>
See also Kafka client for PHP and librdkafka.
ext/fam
[Since 0.12.8] - [ -P Extensions/Extfam ] - [ Online docs ]
File Alteration Monitor extension.
FAM monitors files and directories, notifying interested applications of changes.
ext/FAM is not available for Windows
<?php
$fam = fam_open('myApplication');
fam_monitor_directory($fam, '/tmp');
fam_close($fam);
?>
See also File Alteration Monitor.
Pathinfo() Returns May Vary
[Since 0.12.11] - [ -P Php/PathinfoReturns ] - [ Online docs ]
pathinfo() function returns an array whose content may vary. It is recommended to collect the values after check, rather than directly.
The same applies to parse_url(), which returns an array with various index.
<?php
$file = '/a/b/.c';
//$extension may be missing, leading to empty $filename and filename in $extension
list( $dirname, $basename, $extension, $filename ) = array_values( pathinfo($file) );
//Use PHP 7.1 list() syntax to assign correctly the values, and skip array_values()
//This emits a warning in case of missing index
['dirname' => $dirname,
'basename' => $basename,
'extension' => $extension,
'filename' => $filename ] = pathinfo($file);
//This works without warning
$details = pathinfo($file);
$dirname = $details['dirname'] ?? getpwd();
$basename = $details['basename'] ?? '';
$extension = $details['extension'] ?? '';
$filename = $details['filename'] ?? '';
?>
Alternatives :
- Add a check on the return value of pathinfo() before using it.
ext/parle
[Since 0.12.12] - [ -P Extensions/Extparle ] - [ Online docs ]
Extension Parser and Lexer.
The parle extension provides lexing and parsing facilities. The implementation is based on » Ben Hanson's libraries and requires a » C++14 capable compiler.
<?php
use Parle\{Token, Lexer, LexerException};
/* name => id */
$token = array(
'EOI' => 0,
'COMMA' => 1,
'CRLF' => 2,
'DECIMAL' => 3,
);
/* id => name */
$token_rev = array_flip($token);
$lex = new Lexer;
$lex->push("[\x2c]", $token['COMMA']);
$lex->push("[\r][\n]", $token['CRLF']);
$lex->push("[\d]+", $token['DECIMAL']);
$lex->build();
$in = "0,1,2\r\n3,42,5\r\n6,77,8\r\n";
$lex->consume($in);
do {
$lex->advance();
$tok = $lex->getToken();
if (Token::UNKNOWN == $tok->id) {
throw new LexerException('Unknown token "'.$tok->value.'" at offset '.$tok->offset.'.');
}
echo 'TOKEN: ', $token_rev[$tok->id], PHP_EOL;
} while (Token::EOI != $tok->id);
?>
See also Parsing and Lexing.
Regex Inventory
[Since 0.12.14] - [ -P Type/Regex ] - [ Online docs ]
All regular expressions used in the code. PHP relies on the PCRE extension to process them, with the functions preg_match(), preg_replace(), etc.
mbstring regular expressions are also collected. POSIX regex are not listed : they were deprecated in PHP 7.0.
<?php
// PCRE regex used with preg_match
preg_match('/[abc]+/', $string);
// Mbstring regex, in the arabic range
if(mb_ereg('[\x{0600}-\x{06FF}]', $text))
?>
See also preg_match(), `ext/mbstring Multiple Type Variable
[Since 0.12.15] - [ -P Structures/MultipleTypeVariable ] - [ Online docs ]
Avoid using the same variable with different types of data.
It is recommended to use different names for differently typed data, while processing them. This prevents errors where one believe the variable holds the former type, while it has already been cast to the later.
Incrementing variables, with math operations or concatenation, is OK : the content changes, but not the type. And casting the variable without storing it in itself is OK.
<?php
// $x is an array
$x = range('a', 'z');
// $x is now a string
$x = join('', $x);
$c = count($x); // $x is not an array anymore
// $letters is an array
$letters = range('a', 'z');
// $alphabet is a string
$alphabet = join('', $letters);
// Here, $letters is cast by PHP, but the variable is changed.
if ($letters) {
$count = count($letters); // $letters is still an array
}
?>
Alternatives :
- Use a class that accepts one type of argument, and exports another type of argument.
- Use different variable for each type of data format : $rows (for array), $list (for implode('', $rows))
- Pass the final result as argument to another method, avoiding the temporary variable
Is Actually Zero
[Since 0.12.15] - [ -P Structures/IsZero ] - [ Online docs ]
This addition actually may be simplified because one term is actually negated by another.
This kind of error happens when the expression is very large : the more terms are included, the more chances are that some auto-annihilation happens.
This error may also be a simple typo : for example, calculating the difference between two consecutive terms.
<?php
// This is quite obvious
$a = 2 - 2;
// This is obvious too. This may be a typo-ed difference between two consecutive terms.
// Could have been $c = $fx[3][4] - $fx[3][3] or $c = $fx[3][5] - $fx[3][4];
$c = $fx[3][4] - $fx[3][4];
// This is less obvious
$a = $b[3] - $c + $d->foo(1,2,3) + $c + $b[3];
?>
Alternatives :
- Clean the code and remove the null sum
- Fix one of the variable : this expression needs another variable here
- When adding differences, calculate the difference in a temporary variable first.
Unconditional Break In Loop
[Since 0.12.16] - [ -P Structures/UnconditionLoopBreak ] - [ Online docs ]
An unconditional break in a loop creates dead code. Since the break is directly in the body of the loop, it is always executed, creating a strange loop that can only run once.
Here, break may also be a return, a goto or a continue. They all branch out of the loop. Such statement are valid, but should be moderated with a condition.
<?php
// return in loop should be in
function summAll($array) {
$sum = 0;
foreach($array as $a) {
// Stop at the first error
if (is_string($a)) {
return $sum;
}
$sum += $a;
}
return $sum;
}
// foreach loop used to collect first element in array
function getFirst($array) {
foreach($array as $a) {
return $a;
}
}
?>
Alternatives :
- Remove the loop and call the content of the loop once.
Too Complex Expression
[Since 0.12.16] - [ -P Structures/ComplexExpression ] - [ Online docs ]
Long expressions should be broken in small chunks, to limit complexity.
Really long expressions tends to be error prone : either by typo, or by missing details. They are even harder to review, once the initially build of the expression is gone.
As a general rule, it is recommended to keep expressions short. The analysis include any expression that is more than 15 tokens large : variable and operators counts as one, properties, arrays count as two. Parenthesis are also counted.
PHP has no specific limit to expression size, so long expression are legal and valid. It is possible that the business logic requires a complex equation.
<?php
// Why not calculate wordwrap size separatedly ?
$a = explode("\n", wordwrap($this->message, floor($this->width / imagefontwidth($this->fontsize)), "\n"));
// Longer but easier to read
$width = floor($this->width / imagefontwidth($this->fontsize)), "\n");
$a = explode("\n", wordwrap($this->message, $width);
// Here, some string building, including error management with @, is making the data quite complex.
fwrite($fp, 'HEAD ' . @$url['path'] . @$url['query'] . ' HTTP/1.0' . "\r\n" . 'Host: ' . @$url['host'] . "\r\n\r\n")
// Better validation of data.
$http_header = 'HEAD ';
if (isset($url['path'])) {
$http_header .= $url['path'];
}
if (isset($url['query'])) {
$http_header .= $url['query'];
}
$http_header .= "\r\n";
if (isset($url['host'])) {
$http_header .= 'Host: ' . $url['host'] . "\r\n\r\n";
}
fwrite($fp, $http_header);
?>
Alternatives :
- Reduce complexity by breaking the expressions into smaller ones
Could Be Else
[Since 1.0.1] - [ -P Structures/CouldBeElse ] - [ Online docs ]
Merge opposite conditions into one if/then structure.
When two if/then structures follow each other, using a condition and its opposite, they may be merged into one.
<?php
// Short version
if ($a == 1) {
$b = 2;
} else {
$b = 1;
}
// Long version
if ($a == 1) {
$b = 2;
}
if ($a != 1) {
$b = 3;
}
?>
Alternatives :
- Merge the two conditions into one structure
- Check if the second condition is still applicable
Next Month Trap
[Since 1.0.1] - [ -P Structures/NextMonthTrap ] - [ Online docs ]
Avoid using +1 month with strtotime().
strtotime() calculates the next month by incrementing the month number. For day number that do not exist from one month to the next, strtotime() fixes them by setting them in the next-next month.
This happens to January, March, May, July, August and October. January is also vulnerable for 29 (not every year), 30 and 31.
To use '+1 month', rely on 'first day of next month' or 'last day of next month' to extract the next month's name. For longer interfaces, start from 'first day of next month'.
Note that Datetime and DatetimeImmutable are also subject to the same trap.
<?php
// Base date is October 31 => 10/31
// +1 month adds +1 to 10 => 11/31
// Since November 31rst doesn't exists, it is corrected to 12/01.
echo date('F', strtotime('+1 month',mktime(0,0,0,$i,31,2017))).PHP_EOL;
// Base date is October 31 => 10/31
echo date('F', strtotime('first day of next month',mktime(0,0,0,$i,31,2017))).PHP_EOL;
?>
Alternatives :
- Review strtotime() usage for month additions
- Base your calculations for the next/previous months on the first day of the month (or any day before the 28th)
- Avoid using '+n month' with Datetime() after the 28th of any month (sic)
Printf Number Of Arguments
[Since 1.0.1] - [ -P Structures/PrintfArguments ] - [ Online docs ]
The number of arguments provided to printf(), vprintf() and vsprintf() doesn't match the format string.
Extra arguments are ignored, and are dead code as such. Missing arguments are reported with a warning, and nothing is displayed.
Omitted arguments produce an error.
<?php
// not enough arguments
printf(' a %s ', $a1);
// OK
printf(' a %s ', $a1, $a2);
// too many arguments
printf(' a %s ', $a1, $a2, $a3);
// not enough arguments
sprintf(' a %s ', $a1);
// OK
\sprintf(' a %s ', $a1, $a2);
// too many arguments
sprintf(' a %s ', $a1, $a2, $a3);
?>
Alternatives :
- Sync the number of argument with the format command
Drupal Usage
[Since 1.0.3] - [ -P Vendors/Drupal ] - [ Online docs ]
This analysis reports usage of the Drupal CMS. The report is based on the usage of Drupal namespace.
<?php
namespace Drupal\example\Controller;
use Drupal\Core\Controller\ControllerBase;
/**
* An example controller.
*/
class ExampleController extends ControllerBase {
/**
* {@inheritdoc}
*/
public function content() {
$build = array(
'#type' => 'markup',
'#markup' => t('Hello World!'),
);
return $build;
}
}
?>
See also Drupal.
Ambiguous Static
[Since 1.0.3] - [ -P Classes/AmbiguousStatic ] - [ Online docs ]
Methods or properties with the same name, are defined static in one class, and not static in another. This is error prone, as it requires a good knowledge of the code to make it static or not.
Try to keep the methods simple and unique. Consider renaming the methods and properties to distinguish them easily. A method and a static method have probably different responsibilities.
<?php
class a {
function mixedStaticMethod() {}
}
class b {
static function mixedStaticMethod() {}
}
/... a lot more code later .../
$c->mixedStaticMethod();
// or
$c::mixedStaticMethod();
?>
Phalcon Usage
[Since 1.0.3] - [ -P Vendors/Phalcon ] - [ Online docs ]
This analysis reports usage of the Phalcon Framework. The report is based on the usage of Phalcon namespace, which may be provided by PHP code inclusion or the PHP extension.
<?php
use Phalcon\Mvc\Application;
// Register autoloaders
// Register services
// Handle the request
$application = new Application($di);
try {
$response = $application->handle();
$response->send();
} catch (\Exception $e) {
echo 'Exception: ', $e->getMessage();
}
?>
See also Phalcon.
Fuel PHP Usage
[Since 1.0.3] - [ -P Vendors/Fuel ] - [ Online docs ]
This analysis reports usage of the Fuel PHP Framework.
Do not confuse fuelPHP and fuelCMS
<?php
// file located in APPPATH/classes/presenter.php
class Presenter extends \Fuel\Core\Presenter
{
// namespace prefix
protected static $ns_prefix = 'Presenter\';
}
?>
See also FuelPHP.
Don't Send $this In Constructor
[Since 1.0.4] - [ -P Classes/DontSendThisInConstructor ] - [ Online docs ]
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();
}
}
?>
Alternatives :
- 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.
Argon2 Usage
[Since 1.0.4] - [ -P Php/Argon2Usage ] - [ Online docs ]
Argon2 is an optionally compiled password hashing API.
Argon2 has been added to the password hashing API in PHP 7.2.
It is not available in older version. It also requires PHP to be compiled with the --with-password-argon2 option.
<?php
// Hashing a password with argon2
$hash = password_hash('password', PASSWORD_ARGON2I, ['memory_cost' => 1<<17,
'time_cost' => PASSWORD_ARGON2_DEFAULT_TIME_COST,
'threads' => PASSWORD_ARGON2_DEFAULT_THREADS]);
?>
See also Argon2 Password Hash.
Crypto Usage
[Since 1.0.4] - [ -P Php/CryptoUsage ] - [ Online docs ]
Usage of cryptography and hashes functions.
The functions listed are the native PHP functions, and do not belong to a specific extension, like OpenSSL , mcrypt or mhash .
Cryptography and hashes are mainly used for storing sensitive data, such as passwords, or to verify authenticity of data. They may also be used for name-randomization with cache.
<?php
if (md5($_POST['password']) === $row['password_hash']) {
user_login($user);
} else {
error('Wrong password');
}
?>
See also Cryptography Extensions.
No get_class() With Null
[Since 1.0.4] - [ -P Structures/NoGetClassNull ] - [ Online docs ]
It is not possible to pass explicitly null to get_class() to get the current's class name. Since PHP 7.2, one must call get_class() without arguments to achieve that result.
<?php
class A {
public function f() {
// Gets the classname
$classname = get_class();
// Gets the classname and a warning
$classname = get_class(null);
}
}
$a = new A();
$a->f('get_class');
?>
Maybe Missing New
[Since 1.0.4] - [ -P Structures/MissingNew ] - [ Online docs ]
This functioncall looks like a class instantiation that is missing the new keyword.
Any function definition was found for that function, but a class with that name was. New is probably missing.
<?php
// Functioncall
$a = foo();
// Class definition
class foo {}
// Function definition
function foo {}
// Functioncall
$a = BAR;
// Function definition
class bar {}
// Constant definition
const BAR = 1;
?>
Alternatives :
- Add the new
- Rename the class to distinguish it from the function
- Rename the function to distinguish it from the class
Unknown Pcre2 Option
[Since 1.0.4] - [ -P Php/UnknownPcre2Option ] - [ Online docs ]
PCRE2 supports different options, compared to PCRE1 . PCRE2 was adopted with PHP 7.3.
The S modifier : it used to tell PCRE to spend more time studying the regex, so as to be faster at execution. This is now the default behavior, and may be dropped from the regex.
The X modifier : X is still existing with PCRE2 , though it is now the default for PCRE2 , and not for PHP as time of writing. In particular, Any backslash in a pattern that is followed by a letter that has no special meaning causes an error, thus reserving these combinations for future expansion. . It is recommended to avoid using useless sequence \s in regex to get ready for that change. All the following letters gijkmoqyFIJMOTY . Note that clLpPuU are valid PRCE sequences, and are probably failing for other reasons.
<?php
// \y has no meaning. With X option, this leads to a regex compilation error, and a failed test.
preg_match('/ye\y/', $string);
preg_match('/ye\y/X', $string);
?>
See also Pattern Modifiers and PHP RFC: PCRE2 migration.
Type Array Index
[Since 1.0.4] - [ -P Type/ArrayIndex ] - [ Online docs ]
All literal index used in the code.
<?php
// index is an index. it is read
$array['index'] = 1;
// another_index and second_level are read
$array[] = $array['another_index']['second_level'];
// variables index are not reported
$array[$variable] = 1;
?>
Incoming Variable Index Inventory
[Since 1.0.4] - [ -P Type/GPCIndex ] - [ Online docs ]
This collects all the index used in incoming variables : $_GET, $_POST, $_REQUEST, $_COOKIE.
<?php
// x is collected
echo $_GET['x'];
// y is collected, but no z.
echo $_POST['y']['z'];
// a is not collected
echo $_ENV['s'];
?>
ext/vips
[Since 1.0.4] - [ -P Extensions/Extvips ] - [ Online docs ]
Extension VIPS.
The VIPS image processing system is a very fast, multi-threaded image processing library with low memory needs.
<?php
dl('vips.' . PHP_SHLIB_SUFFIX);
$x = vips_image_new_from_file($argv[1])["out"];
vips_image_write_to_file($x, $argv[2]);
?>
See also php-vips-ext, libvips and libvips adapter for PHP Imagine.
Dl() Usage
[Since 1.0.4] - [ -P Php/DlUsage ] - [ Online docs ]
Dynamically load PHP extensions with dl().
<?php
// dynamically loading ext/vips
dl('vips.' . PHP_SHLIB_SUFFIX);
?>
See also dl.
Parent First
[Since 1.0.5] - [ -P Classes/ParentFirst ] - [ Online docs ]
When calling parent constructor, always put it first in the __construct method.
It ensures the parent is correctly build before the child start using values.
This analysis doesn't apply to Exceptions.
<?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();
}
}
?>
Alternatives :
- Use parent::__construct as the first call in the constructor.
Environment Variables
[Since 1.0.5] - [ -P Variables/UncommonEnvVar ] - [ Online docs ]
Environment variables are used to interact with the hosting system.
They often provides configuration parameter that are set by the host of the application to be used.
That way, information is not hardcoded in the application, and may be changed at production.
<?php
//ENVIRONMENT set the production context
if (getenv('ENVIRONMENT') === 'Production') {
$sshKey = getenv('HOST_KEY');
} elseif (getenv('ENVIRONMENT') === 'Developper') {
$sshKey = 'NO KEY';
} else {
header('No website here.');
die();
}
?>
See also $_ENV.
Invalid Regex
[Since 1.0.5] - [ -P Structures/InvalidRegex ] - [ Online docs ]
The PCRE regex doesn't compile. It isn't a valid regex.
Several reasons may lead to this situation : syntax error, Unknown modifier, missing parenthesis or reference.
Regex are check with the Exakat version of PHP.
Dynamic regex are only checked for simple values. Dynamic values may eventually generate a compilation error.
<?php
// valid regex
preg_match('/[abc]/', $string);
// invalid regex (missing terminating ] for character class
preg_match('/[abc/', $string);
?>
Alternatives :
- Fix the regex before running it
Use Named Boolean In Argument Definition
[Since 1.0.6] - [ -P Functions/AvoidBooleanArgument ] - [ Online docs ]
Boolean values in argument definition are confusing.
It is recommended to use explicit constant names or enumerations, instead. They are more readable. They also allow for easy replacement when the code evolve and has to replace those booleans by strings. This works even also with classes, and class constants.
<?php
function flipImage($im, $horizontal = NO_HORIZONTAL_FLIP, $vertical = NO_VERTICAL_FLIP) { }
// with constants
const HORIZONTAL_FLIP = true;
const NO_HORIZONTAL_FLIP = true;
const VERTICAL_FLIP = true;
const NO_VERTICAL_FLIP = true;
rotateImage($im, HORIZONTAL_FLIP, NO_VERTICAL_FLIP);
// without constants
function flipImage($im, $horizontal = false, $vertical = false) { }
rotateImage($im, true, false);
?>
Alternatives :
- Use available constants whenever possible
- Create a constant (global or class), and use it
- Use named parameters to clarify the target of the boolean
- Use a single-parameter method, so that the value of the boolean is obvious
- Use an enumeration
Same Variable Foreach
[Since 1.0.5] - [ -P Structures/AutoUnsetForeach ] - [ Online docs ]
A foreach which uses its own source as a blind variable is actually broken.
Actually, PHP makes a copy of the source before it starts the loop. As such, the same variable may be used for both source and blind value.
Of course, this is very confusing, to see the same variables used in very different ways.
The source will also be destroyed immediately after the blind variable has been turned into a reference.
<?php
$array = range(0, 10);
foreach($array as $array) {
print $array.PHP_EOL;
}
print_r($array); // display number from 0 to 10.
$array = range(0, 10);
foreach($array as &$array) {
print $array.PHP_EOL;
}
print_r($array); // display 10
?>
Alternatives :
- Name the source and variable names distinctly
Never Called Parameter
[Since 1.0.6] - [ -P Functions/NeverUsedParameter ] - [ Online docs ]
This analysis reports when a parameter is never used at calltime.
Such parameter has a default value, and always falls back to it. As such, it may be turned into a local variable.
A never called parameter is often planned for future use, though, so far, the code base doesn't make use of it. It also happens that the code use it, but is not part of the analyzed code base, such as a plugin system.
This issue is silent: it doesn't yield any error. It is also difficult to identify, as it requires checking all the usage of the method.
This analysis checks for actual usage of the parameter, from the outside of the method. This is different from checking if the parameter is used inside the method.
<?php
// $b may be turned into a local var, it is unused
function foo($a, $b = 1) {
return $a + $b;
}
// whenever foo is called, the 2nd arg is not mentioned
foo($a);
foo(3);
foo('a');
foo($c);
?>
Alternatives :
- Drop the unused argument in the method definition
- Actually use the argument when calling the method
- Drop the default value, and check warnings that mention usage of this parameter
ext/igbinary
[Since 1.0.6] - [ -P Extensions/Extigbinary ] - [ Online docs ]
Extension igbinary.
igbinary is a drop in replacement for the standard php serializer. Instead of time and space consuming textual representation, igbinary stores php data structures in compact binary form.
<?php
$serialized = igbinary_serialize($variable);
$unserialized = igbinary_unserialize($serialized);
?>
See also igbinary.
Identical On Both Sides
[Since 1.0.8] - [ -P Structures/IdenticalOnBothSides ] - [ Online docs ]
Operands should be different when comparing or making a logical combination. Of course, the value each operand holds may be identical. When the same operand appears on both sides of the expression, the result is know before execution.
<?php
// Trying to confirm consistency
if ($login == $login) {
doSomething();
}
// Works with every operators
if ($object->login( ) !== $object->login()) {
doSomething();
}
if ($sum >= $sum) {
doSomething();
}
//
if ($mask && $mask) {
doSomething();
}
if ($mask || $mask) {
doSomething();
}
?>
Alternatives :
- Remove one of the alternative, and remove the logical link
- Modify one of the alternative, and make it different from the other
Identical Consecutive Expression
[Since 1.0.8] - [ -P Structures/IdenticalConsecutive ] - [ Online docs ]
Identical consecutive expressions might be double code. They are worth being checked.
They may be a copy/paste with unmodified content. When the content has to be duplicated, it is recommended to avoid executing the expression again, and just access the cached result.
<?php
$current = $array[$i];
$next = $array[$i + 1];
$nextnext = $array[$i + 1]; // OOps, nextnext is wrong.
// Initialization
$previous = foo($array[1]); // previous is initialized with the first value on purpose
$next = foo($array[1]); // the second call to foo() with the same arguments should be avoided
// the above can be rewritten as :
$next = $previous; // save the processing.
for($i = 1; $i < 200; ++$i) {
$next = doSomething();
}
?>
Alternatives :
- Check if the expression needs to be used twice.
No Reference For Ternary
[Since 1.0.8] - [ -P Php/NoReferenceForTernary ] - [ Online docs ]
The ternary operator and the null coalescing operator are both expressions that only return values, and not a reference.
This means that any provided reference will be turned into its value. While this is usually invisible, it will raise a warning when a reference is expected. This is the case with methods returning a reference.
A PHP notice is generated when using a ternary operator or the null coalesce operator : Only variable references should be returned by reference . The notice is also emitted when returning objects.
This applies to methods, functions and closures.
<?php
// This works
function &foo($a, $b) {
if ($a === 1) {
return $b;
} else {
return $a;
}
}
// This raises a warning, as the operator returns a value
function &foo($a, $b) { return $a === 1 ? $b : $a; }
?>
Alternatives :
- Drop the reference at assignation time
- Drop the reference in the argument definition
- Drop the reference in the function return definition
Unused Inherited Variable In Closure
[Since 1.0.11] - [ -P Functions/UnusedInheritedVariable ] - [ Online docs ]
Some closures forgot to make usage of inherited variables.
Closure have two separate set of incoming variables : the arguments (between parenthesis) and the inherited variables, in the 'use' clause. Inherited variables are extracted from the local environment at creation time, and keep their value until execution.
The reported closures are requesting some local variables, but do not make any usage of them. They may be considered as dead code.
<?php
// In this closure, $y is forgotten, but $u is used.
$a = function ($y) use ($u) { return $u; };
// In this closure, $u is forgotten
$a = function ($y, $z) use ($u) { return $u; };
?>
Alternatives :
- Remove the unused inherited variable
- Make us of the unused inherited variable
Inclusion Wrong Case
[Since 1.1.1] - [ -P Files/InclusionWrongCase ] - [ Online docs ]
Inclusion should follow exactly the case of included files and path. This prevents the infamous case-sensitive filesystem bug, where files are correctly included in a case-insensitive system, and failed to be when moved to production.
<?php
// There must exist a path called "path/to" and a file "library.php" with this case
include "path/to/library.php";
// Error on the case, while the file does exist
include "path/to/LIBRARY.php";
// Error on the case, on the PATH
include "path/TO/library.php";
?>
Alternatives :
- Make the inclusion string identical to the file name.
- Change the name of the file to reflect the actual inclusion. This is the best way when a naming convention has been set up for the project, and the file doesn't adhere to it. Remember to change all other inclusion.
Missing Include
[Since 1.1.2] - [ -P Files/MissingInclude ] - [ Online docs ]
The included files doesn't exists in the repository. The inclusions target a files that doesn't exist.
The analysis works with every type of inclusion : include(), require(), include_once() and require_once(). It also works with parenthesis when used as parameter delimiter.
The analysis doesn't take into account include_path . This may yield false positives.
Missing included files may lead to a fatal error, a warning or other error later in the execution.
<?php
include 'non_existent.php';
// variables are not resolved. This won't be reported.
require ($path.'non_existent.php');
?>
Useless Referenced Argument
[Since 1.1.3] - [ -P Functions/UselessReferenceArgument ] - [ Online docs ]
The argument has a reference, and is only used for reading.
This is probably a development artefact that was forgotten. It is better to remove it.
This analysis also applies to foreach() loops, that declare the blind variable as reference, then use the variable as an object, accessing properties and methods. When a variable contains an object, there is no need to declare a reference : it is a reference automatically.
<?php
function foo($a, &$b, &$c) {
// $c is passed by reference, but only read. The reference is useless.
$b = $c + $a;
// The reference is useful for $b
}
foreach ($array as &$element) {
$element->method();
}
?>
Alternatives :
- Remove the useless & from the argument
- Make an actual use of the argument before the end of the method
Fallback Function
[Since 1.1.4] - [ -P Functions/FallbackFunction ] - [ Online docs ]
A function that is called with its name alone, and whose definition is in the global scope.
<?php
namespace {
// global definition
function foo() {}
}
namespace Bar {
// local definition
function foo2() {}
foo(); // definition is in the global namespace
foo2(); // definition is in the Bar namespace
}
?>
See also Using namespaces: fallback to global function/constant.
Possible Infinite Loop
[Since 1.1.5] - [ -P Structures/PossibleInfiniteLoop ] - [ Online docs ]
Loops on files that can't be open results in infinite loop.
fgets(), and functions like fgetss(), fgetcsv(), fread(), return false when they finish reading, or can't access the file.
In case the file is not accessible, comparing the result of the reading to something that is falsy, leads to a permanent valid condition. The execution will only finish when the max_execution_time is reached.
It is recommended to check the file resources when they are opened, and always use === or !== to compare readings. feof() is also a reliable function here.
<?php
$file = fopen('/path/to/file.txt', 'r');
// when fopen() fails, the next loops is infinite
// fgets() will always return false, and while will always be true.
while($line = fgets($file) != 'a') {
doSomething();
}
?>
ext/hrtime
[Since 1.1.5] - [ -P Extensions/Exthrtime ] - [ Online docs ]
High resolution timing Extension.
The HRTime extension implements a high resolution `StopWatch` class. It uses the best possible API on different platforms which brings resolution up to nanoseconds. It also makes possible to implement a custom stopwatch using low level ticks delivered by the underlaying system.
<?php
$c = new HRTime\StopWatch;
$c->start();
/* measure this code block execution */
for ($i = 0; $i < 1024*1024; $i++);
$c->stop();
$elapsed0 = $c->getLastElapsedTime(HRTime\Unit::NANOSECOND);
/* measurement is not running here*/
for ($i = 0; $i < 1024*1024; $i++);
$c->start();
/* measure this code block execution */
for ($i = 0; $i < 1024*1024; $i++);
$c->stop();
$elapsed1 = $c->getLastElapsedTime(HRTime\Unit::NANOSECOND);
$elapsed_total = $c->getElapsedTime(HRTime\Unit::NANOSECOND);
?>
See also ext/hrtime manual.
Test Then Cast
[Since 1.1.6] - [ -P Structures/TestThenCast ] - [ Online docs ]
A test is run on a value without a cast, and later the cast value is later used.
The cast may introduce a distortion to the value, and still lead to the unwanted situation. For example, comparing to 0, then later casting to an int. The comparison to 0 is done without casting, and as such, 0.1 is different from 0. Yet, (int) 0.1 is actually 0, leading to a Division by 0 error.
<?php
// Here. $x may be different from 0, but (int) $x may be 0
$x = 0.1;
if ($x != 0) {
$y = 4 / (int) $x;
}
// Safe solution : check the cast value.
if ( (int) $x != 0) {
$y = 4 / (int) $x;
}
?>
Alternatives :
- Test with the cast value
Foreach On Object
[Since 1.1.6] - [ -P Php/ForeachObject ] - [ Online docs ]
Foreach on object looks like a typo. This is particularly true when both object and member are variables.
Foreach on an object member is a legit PHP syntax, though it is very rare : blind variables rarely have to be securing in an object to be processed.
<?php
// This is the real thing
foreach($array as $o => $b) {
doSomething();
}
// Looks suspicious
foreach($array as $o -> $b) {
doSomething();
}
?>
ext/xxtea
[Since 1.1.7] - [ -P Extensions/Extxxtea ] - [ Online docs ]
Extension xxtea : XXTEA encryption algorithm extension for PHP.
XXTEA is a fast and secure encryption algorithm. This is a XXTEA extension for PHP.
It is different from the original XXTEA encryption algorithm. It encrypts and decrypts string instead of uint32 array, and the key is also string.
<?php
// Example is extracted from the xxtea repository on github : tests/xxtea.phpt
$str = 'Hello World! 你好,中国🇨🇳!';
$key = '1234567890';
$base64 = 'D4t0rVXUDl3bnWdERhqJmFIanfn/6zAxAY9jD6n9MSMQNoD8TOS4rHHcGuE=';
$encrypt_data = xxtea_encrypt($str, $key);
$decrypt_data = xxtea_decrypt($encrypt_data, $key);
if ($str == $decrypt_data && base64_encode($encrypt_data) == $base64) {
echo 'success!';
} else {
echo base64_encode($encrypt_data);
echo 'fail!';
}
?>
See also PECL ext/xxtea and ext/xxtea on Github.
ext/uopz
[Since 1.1.7] - [ -P Extensions/Extuopz ] - [ Online docs ]
Extension UOPZ : User Operations for Zend.
The uopz extension is focused on providing utilities to aid with unit testing PHP code.
It supports the following activities: Intercepting function execution, Intercepting object creation, Hooking into function execution, Manipulation of function statics, Manipulation of function flags, Redefinition of constants, Deletion of constants, Runtime creation of functions and methods,
<?php
// The example is extracted from the UOPZ extension test suite : tests/001.phpt
class Foo {
public function bar(int $arg) : int {
return $arg;
}
}
var_dump(uopz_set_return(Foo::class, 'bar', true));
$foo = new Foo();
var_dump($foo->bar(1));
uopz_set_return(Foo::class, 'bar', function(int $arg) : int {
return $arg * 2;
}, true);
var_dump($foo->bar(2));
try {
uopz_set_return(Foo::class, 'nope', 1);
} catch(Throwable $t) {
var_dump($t->getMessage());
}
class Bar extends Foo {}
try {
uopz_set_return(Bar::class, 'bar', null);
} catch (Throwable $t) {
var_dump($t->getMessage());
}
uopz_set_something(Bar::class, 'bar', null);
?>
See also ext/uopz and User Operations for Zend.
ext/varnish
[Since 1.1.7] - [ -P Extensions/Extvarnish ] - [ Online docs ]
Extension PHP for varnish.
Varnish Cache is an open source, state of the art web application accelerator. The extension makes it possible to interact with a running varnish instance through TCP socket or shared memory.
<?php
$args = array(
VARNISH_CONFIG_HOST => '::1',
VARNISH_CONFIG_PORT => 6082,
VARNISH_CONFIG_SECRET => '5174826b-8595-4958-aa7a-0609632ad7ca',
VARNISH_CONFIG_TIMEOUT => 300,
);
$va = new VarnishAdmin($args);
?>
See also ext/varnish and pecl/Varnish.
ext/opencensus
[Since 1.1.7] - [ -P Extensions/Extopencensus ] - [ Online docs ]
Extension PHP for OpenCensus.
A stats collection and distributed tracing framework.
<?php
opencensus_trace_begin('root', ['spanId' => '1234']);
opencensus_trace_add_annotation('foo');
opencensus_trace_begin('inner', []);
opencensus_trace_add_annotation('asdf', ['spanId' => '1234']);
opencensus_trace_add_annotation('abc');
opencensus_trace_finish();
opencensus_trace_finish();
$traces = opencensus_trace_list();
echo "Number of traces: " . count($traces) . "\n";
$span = $traces[0];
print_r($span->timeEvents());
$span2 = $traces[1];
print_r($span2->timeEvents());
?>
See also opencensus.
ext/leveldb
[Since 1.1.7] - [ -P Extensions/Extleveldb ] - [ Online docs ]
PHP Binding for LevelDB.
LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.
<?php
$db = new LevelDB($leveldb_path);
$batch = new LevelDBWriteBatch();
$batch->set('batch_foo', 'batch_bar');
$batch->put('batch_foo2', 'batch_bar2');
$batch->delete('batch_foo');
$db->write($batch);
$batch->clear();
$batch->delete('batch_foo2');
$batch->set('batch_foo', 'batch again');
?>
See also ext/leveldb on Github and Leveldb.
Property Could Be Local
[Since 1.1.7] - [ -P Classes/PropertyCouldBeLocal ] - [ Online docs ]
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() {}
}
?>
Alternatives :
- Remove the property and make it an argument in the method
- Use that property elsewhere
ext/db2
[Since 1.1.8] - [ -P Extensions/Extdb2 ] - [ Online docs ]
Extension for IBM DB2, Cloudscape and Apache Derby.
This extension gives access to IBM DB2 Universal Database, IBM Cloudscape, and Apache Derby databases using the DB2 Call Level Interface (DB2 CLI).
<?php
$conn = db2_connect($database, $user, $password);
if ($conn) {
$stmt = db2_exec($conn, 'SELECT count(*) FROM animals');
$res = db2_fetch_array( $stmt );
echo $res[0] . PHP_EOL;
// Turn AUTOCOMMIT off
db2_autocommit($conn, DB2_AUTOCOMMIT_OFF);
// Delete all rows from ANIMALS
db2_exec($conn, 'DELETE FROM animals');
$stmt = db2_exec($conn, 'SELECT count(*) FROM animals');
$res = db2_fetch_array( $stmt );
echo $res[0] . PHP_EOL;
// Roll back the DELETE statement
db2_rollback( $conn );
$stmt = db2_exec( $conn, 'SELECT count(*) FROM animals' );
$res = db2_fetch_array( $stmt );
echo $res[0] . PHP_EOL;
db2_close($conn);
}
?>
See also IBM Db2.
Too Many Native Calls
[Since 1.1.10] - [ -P Php/TooManyNativeCalls ] - [ Online docs ]
Avoid stuffing too many PHP native call inside another functioncall.
For readability reasons, or, more often, for edge case handling, it is recommended to avoid nesting too many PHP native calls.
This analysis reports any situation where more than 3 PHP native calls are nested.
<?php
// Too many nested functions
$cleanArray = array_unique(array_keys(array_count_values(array_column($source, 'x'))));
// Avoid warning when source is empty
$extract = array_column($source, 'x');
if (empty($extract)) {
$cleanArray = array();
} else {
$cleanArray = array_unique(array_keys(array_count_values($extract)));
}
// This is not readable, although it is short.
// It may easily get out of hand.
echo chr(80), chr(72), chr(80), chr(32), ' is great!';
?>
Alternatives :
- Reduce the number of native calls
- Split the method into smaller methods
Don't Unset Properties
[Since 1.2.3] - [ -P Classes/DontUnsetProperties ] - [ Online docs ]
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.
This analysis works on properties and static properties. It also reports magic properties being unset.
Thanks for Benoit Burnichon for the original idea.
<?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);
?>
Alternatives :
- Set the property to null or its default value
- Make the property an array, and set/unset its index
Strtr Arguments
[Since 1.2.3] - [ -P Php/StrtrArguments ] - [ Online docs ]
Strtr() replaces characters by others in a string. When using strings, strtr() replaces characters as long as they have a replacement. All others are ignored.
In particular, strtr() works on strings of the same size, and cannot be used to remove chars.
<?php
$string = 'abcde';
echo strtr($string, 'abc', 'AB');
echo strtr($string, 'ab', 'ABC');
// displays ABcde
// c is ignored each time
// strtr can't remove a char
echo strtr($string, 'a', '');
// displays a
?>
Alternatives :
- Check the call to strtr() and make sure the arguments are of the same size
- Replace strtr() with str_replace(), which works with strings and array, not chars
- Replace strtr() with preg_match(), which works with patterns and not chars
Missing Parenthesis
[Since 1.2.6] - [ -P Structures/MissingParenthesis ] - [ Online docs ]
Adding parenthesis to addition expressions make them more readable and to prevent bugs.
In the expressions below, the code is legit, although it is prone to misunderstanding.
<?php
// Missing some parenthesis!!
if (!$a instanceof Stdclass) {
print "Not\n";
} else {
print "Is\n";
}
// Could this addition be actually,
$c = -$a + $b;
// this one ?
$c = -($a + $b);
// or this one ?
$c = $b - $a;
?>
Alternatives :
- Use parenthesis to show intent in the addition expression
Callback Function Needs Return
[Since 1.2.6] - [ -P Functions/CallbackNeedsReturn ] - [ Online docs ]
When used with array_map() functions, the callback must return something. This return may be in the form of a return statement, a global variable or a parameter with a reference. All those solutions extract information from the callback.
The following functions are omitted, as they don't require the return :
- forward_static_call_array()
- forward_static_call()
- register_shutdown_function()
- register_tick_function()
<?php
// This filters each element
$filtered = array_filter($array, function ($x) {return $x == 2; });
// This return void for every element
$filtered = array_filter($array, function ($x) {return ; });
// costly array_sum()
$sum = 0;
$filtered = array_filter($array, function ($x) use (&$sum) {$sum += $x; });
// costly array_sum()
global $sum = 0;
$filtered = array_filter($array, function () {global $sum; $sum += $x; });
// register_shutown_function() doesn't require any return
register_shutown_function("my_shutdown");
?>
Alternatives :
- Add an explicit return to the callback
- Use `null` to unset elements in an array without destroying the index
Wrong Range Check
[Since 1.2.5] - [ -P Structures/WrongRange ] - [ Online docs ]
The interval check should use && and not ||.
<?php
//interval correctly checked a is between 2 and 999
if ($a > 1 && $a < 1000) {}
//interval incorrectly checked : a is 2 or more ($a < 1000 is never checked)
if ($a > 1 || $a < 1000) {}
?>
Alternatives :
- Make the interval easy to read and understand
- Check the truth table for the logical operation
ext/zookeeper
[Since 1.2.5] - [ -P Extensions/Extzookeeper ] - [ Online docs ]
Extension for Apache Zookeeper.
ZooKeeper is an Apache project that enables centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.
<?php
$zookeeper = new Zookeeper('locahost:2181');
$path = '/path/to/node';
$value = 'nodevalue';
$zookeeper->set($path, $value);
$r = $zookeeper->get($path);
if ($r)
echo $r;
else
echo 'ERR';
?>
See also ext/zookeeper, Install Zookeeper PHP Extension and Zookeeper.
ext/cmark
[Since 1.2.7] - [ -P Extensions/Extcmark ] - [ Online docs ]
Extension Cmark, for Common Mark.
cmark provides access to the reference implementation of CommonMark, a rationalized version of Markdown syntax with a specification.
<?php
$text = new CommonMark\Node\Text;
$text->literal = 'Hello World';
$document = new CommonMark\Node\Document;
$document->appendChild(
(new CommonMark\Node\Paragraph)
->appendChild($text));
echo CommonMark\Render\HTML($document);
?>
See also Cmark and ext/cmark.
Can't Instantiate Class
[Since 1.2.8] - [ -P Classes/CantInstantiateClass ] - [ Online docs ]
When constructor is not public, it is not possible to instantiate such a class. Either this is a conception choice, or there are factories to handle that. Either way, it is not possible to call new on such class.
<?php
//This is the way to go
$x = X::factory();
//This is not possible
$x = new X();
class X {
//This is also the case with proctected __construct
private function __construct() {}
static public function factory() {
return new X();
}
}
?>
Alternatives :
- Make the constructor public
- Create a factory, as a static method, in that class, to create objects
- Remove the new call
strpos() Too Much
[Since 1.2.8] - [ -P Performances/StrposTooMuch ] - [ Online docs ]
strpos() covers the whole string before reporting 0. If the expected string is expected be at the beginning, or a fixed place, it is more stable to use substr() for comparison.
The longer the haystack (the searched string), the more efficient is that trick. The string has to be 10k or more to have impact, unless it is in a loop.
This applies to stripos() too.
<?php
// This always reads the same amount of string
if (substr($html, 0, 6) === '<html>') {
}
// When searching for a single character, checking with a known position ($string[$position]) is even faster
if ($html[0] === '<') {
}
// With strpos(), the best way is to search for something that exist, and use absence as worst case scenario
if (strpos($html, '<html>') > 0) {
} else {
//
}
// When the search fails, the whole string has been read
if (strpos($html, '<html>') === 0) {
}
?>
Alternatives :
- Check for presence, and not for absence
- Use substr() and compare the extracted string
- For single chars, try using the position in the string
Class-typed References
[Since 1.2.8] - [ -P Functions/TypehintedReferences ] - [ Online docs ]
Class-typee arguments have no need for references. Since they are representing an object, they are already a reference.
In fact, adding the & on the argument definition may lead to error like Only variables should be passed by reference .
This applies to the object type hint, but not the the others, such as int or bool .
<?php
// a class
class X {
public $a = 3;
}
// typehinted reference
//function foo(object &$x) works too
function foo(X &$x) {
$x->a = 1;
return $x;
}
// Send an object
$y = foo(new X);
// This prints 1;
print $y->a;
?>
Alternatives :
- Remove reference for typehinted arguments, unless the typehint is a scalar typehint.
Weak Typing
[Since 1.2.8] - [ -P Classes/WeakType ] - [ Online docs ]
The variable's validation is not enough to allow for a sophisticated usage. For example, the variable is checked for null, then used as an object or an array.
<?php
if ($a !== null) {
echo $a->b;
}
?>
Alternatives :
- Use instanceof when checking for objects
- Use is_array() when checking for arrays. Also consider is_string(), is_int(), etc.
- Use typehint when the variable is an argument
Method Signature Must Be Compatible
[Since 1.2.9] - [ -P Classes/MethodSignatureMustBeCompatible ] - [ Online docs ]
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) {}
}
?>
Alternatives :
- Fix the child class method() signature.
- Fix the parent class method() signature, after checking that it won't affect the other children.
Mismatch Type And Default
[Since 1.2.9] - [ -P Functions/MismatchTypeAndDefault ] - [ Online docs ]
The argument typehint and its default value don't match.
The code may lint and load, and even work when the arguments are provided. Though, PHP won't eventually execute it.
Most of the mismatch problems are caught by PHP at linting time. It displays the following error message : 'Argument 1 passed to foo() must be of the type integer, string given'.
The default value may be a constant (normal or class constant) : as such, PHP might find its value only at execution time, from another include. As such, PHP doesn't report anything about the situation at compile time.
The default value may also be a constant scalar expression : since PHP 7, some of the simple operators such as +, -, *, %, **, etc. are available to build default values. Among them, the ternary operator and Coalesce. Again, those expression may be only evaluated at execution time, when the value of the constants are known.
PHP reports typehint and default mismatch at compilation time, unless there is a static expression that can't be resolved within the compiled file : then it is checked only at runtime, leading to a Fatal error.
<?php
// bad definition : the string is actually an integer
const STRING = 3;
function foo(string $s = STRING) {
echo $s;
}
// works without problem
foo('string');
// Fatal error at compile time
foo();
// Fail only at execution time (missing D), and when default is needed
function foo2(string $s = D ? null : array()) {
echo $s;
}
?>
Alternatives :
- Match the typehint with the default value
- Do not rely on PHP type juggling to change the type on the fly
Check JSON
[Since 1.3.0] - [ -P Structures/CheckJson ] - [ Online docs ]
Check errors whenever JSON is encoded or decoded.
In particular, NULL is a valid decoded JSON response. If you want to avoid mistaking NULL for an error, it is recommended to call json_last_error .
<?php
$encoded = json_encode($incoming);
// Unless JSON must contains some non-null data, this mistakes NULL and error
if(json_last_error() != JSON_ERROR_NONE) {
die('Error when encoding JSON');
}
$decoded = json_decode($incoming);
// Unless JSON must contains some non-null data, this mistakes NULL and error
if($decoded === null) {
die('ERROR');
}
?>
Alternatives :
- Always check after JSON operation : encoding or decoding.
- Add a call to json_last_error()
- Configure operations to throw an exception upon error ( JSON_THROW_ON_ERROR ), and catch it.
Const Visibility Usage
[Since 1.3.0] - [ -P Classes/ConstVisibilityUsage ] - [ Online docs ]
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;
}
?>
Alternatives :
- Add constant visibility, at least 'public'.
Don't Mix ++
[Since 1.3.2] - [ -P Structures/DontMixPlusPlus ] - [ Online docs ]
++ operators, pre and post, have two distinct behaviors, and should be used separately.
When mixed in a larger expression, they are difficult to read, and may lead to unwanted behaviors.
<?php
// Clear and defined behavior
$i++;
$a[$i] = $i;
// The index is also incremented, as it is used AFTP the incrementation
// With $i = 2; $a is array(3 => 3)
$a[$i] = ++$i;
// $i is actually modified twice
$i = --$i + 1;
?>
Alternatives :
- Extract the increment from the expression, and put it on a separate line.
Can't Throw Throwable
[Since 1.3.3] - [ -P Exceptions/CantThrow ] - [ Online docs ]
Classes extending Throwable can't be thrown, unless they also extend Exception . The same applies to interfaces that extends Throwable .
Although such code lints, PHP throws a Fatal error when executing or including it : Class fooThrowable cannot implement interface Throwable, extend Exception or Error instead .
<?php
// This is the way to go
class fooException extends \Exception { }
// This is not possible and a lot of work
class fooThrowable implements \throwable { }
?>
Alternatives :
- Extends the \Exception class
- Extends the \Error class
Abstract Or Implements
[Since 1.3.3] - [ -P Classes/AbstractOrImplements ] - [ Online docs ]
A class must implements all abstract methods of it parents, or be abstract too.
PHP detect such error when all classes are loaded: in a code source where classes are split by files, such error it won't be detected until execution, where PHP stops 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
}
?>
Alternatives :
- Implements all the abstract methods of the class
- Make the class abstract
ext/eio
[Since 1.3.3] - [ -P Extensions/Exteio ] - [ Online docs ]
Extension EIO.
This is a PHP extension wrapping functions of the libeio library written by Marc Lehmann.
Libeio is a an asynchronous I/O library. Features basically include asynchronous versions of POSIX API(read, write, open, close, stat, unlink, fdatasync, mknod, readdir etc.); sendfile (native on Solaris, Linux, HP-UX, FreeBSD); readahead. libeio itself emulates the system calls, if they are not available on specific(UNIX-like) platform.
<?php
$str = str_repeat('1', 20);
$filename = '/tmp/tmp_file' .uniqid();
@unlink($filename);
touch($filename);
eio_open($filename, EIO_O_RDWR, NULL, EIO_PRI_DEFAULT, function($filename, $fd) use ($str) {
eio_write($fd, $str, strlen($str), 0, null, function($fd, $written) use ($str, $filename) {
var_dump([
'written' => $written,
'strlen' => strlen($str),
'filesize' => filesize($filename),
'count' => substr_count(file_get_contents($filename), '1')
]);
}, $fd);
}, $filename);
eio_event_loop();
?>
See also libeio and PHP extension for libeio .
Incompatible Signature Methods
[Since 1.3.3] - [ -P Classes/IncompatibleSignature ] - [ Online docs ]
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) {}
}
?>
Alternatives :
- Make signatures compatible again
Ambiguous Visibilities
[Since 1.3.4] - [ -P Classes/AmbiguousVisibilities ] - [ Online docs ]
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;
?>
Alternatives :
- Sync visibilities for both properties, in the different classes
- Use different names for properties with different usages
Undefined ::class
[Since 1.3.5] - [ -P Classes/UndefinedStaticclass ] - [ Online docs ]
::class doesn't check if a corresponding class exists.
::class must be checked with a call to class_exists(). Otherwise, it may lead to a Class 'foo' not found or even silent dead code : this happens also with Catch and instanceof commands with undefined classes. PHP doesn't raise an error in that case.
<?php
class foo() {}
// prints foo
echo foo::class;
// prints bar though bar doesn't exist.
echo bar::class;
?>
Alternatives :
- Create the missing class
- Fix the name part of the syntax
- Check the name part of syntax with class_exists()
ext/lzf
[Since 1.3.5] - [ -P Extensions/Extlzf ] - [ Online docs ]
Extension LZF.
LZF is a very fast compression algorithm, ideal for saving space with only slight speed cost. It can be optimized for speed or space at the time of compilation.
<?php
$compressed = lzf_compress("This is test of LZF extension");
echo base64_encode($compressed);
?>
See also lzf and liblzf.
ext/msgpack
[Since 1.3.5] - [ -P Extensions/Extmsgpack ] - [ Online docs ]
Extension msgPack.
This extension provide API for handling MessagePack serialization, both encoding and decoding.
<?php
$serialized = msgpack_serialize(array('a' => true, 'b' => 4));
$unserialized = msgpack_unserialize($serialized);
?>
See also msgpack for PHP and MessagePack.
Case Insensitive Constants
[Since 1.3.9] - [ -P Constants/CaseInsensitiveConstants ] - [ Online docs ]
PHP constants used to be able to be case insensitive, when defined with define() and the third argument.
This feature is deprecated since PHP 7.3 and is removed since PHP 8.0.
<?php
// case sensitive
define('A', 1);
// case insensitive
define('B', 1, true);
echo A;
// This is not possible
//echo a;
// both possible
echo B;
echo b;
?>
See also define.
Handle Arrays With Callback
[Since 1.3.7] - [ -P Arrays/WithCallback ] - [ Online docs ]
This rule marks method and function calls that accepts array callbacks as argument.
It marks the method call, and not the argument.
<?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.
Assert Function Is Reserved
[Since 1.3.9] - [ -P Php/AssertFunctionIsReserved ] - [ Online docs ]
Avoid defining an assert function in namespaces.
While they work fine when the assertions are active ( zend.assertions=1 ), calls to unqualified assert are optimized away when assertions are not active.
Since PHP 7.3, a fatal error is emitted : Defining a custom assert() function is deprecated, as the function has special semantics .
<?php
// Run this with zend.assertions=1 and
// Then run this with zend.assertions=0
namespace Test {
function assert() {
global $foo;
$foo = true;
}
}
namespace Test {
assert();
var_dump(isset($foo));
}
?>
Alternatives :
- Rename the custom function with another name
Could Be Abstract Class
[Since 1.3.9] - [ -P Classes/CouldBeAbstractClass ] - [ Online docs ]
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()
?>
Alternatives :
- Make this class an abstract class
Continue Is For Loop
[Since 1.3.9] - [ -P Structures/ContinueIsForLoop ] - [ Online docs ]
break and continue are very similar in PHP : they both break out of loop or switch. Yet, continue should be reserved for loops.
Since PHP 7.3, the execution emits a warning when finding a continue inside a switch : '"continue" targeting switch is equivalent to "break". Did you mean to use "continue 2"?'
<?php
while ($foo) {
switch ($bar) {
case 'baz':
continue; // In PHP: Behaves like 'break;'
// In C: Behaves like 'continue 2;'
}
}
?>
Alternatives :
- Replace continue by break
Trailing Comma In Calls
[Since 1.4.0] - [ -P Php/TrailingComma ] - [ Online docs ]
The last argument may be left empty.
This feature was introduced in PHP 7.3.
<?php
// VCS friendly call
// PHP 7.3 and more recent
foo(1,
2,
3,
);
// backward compatible call
// All PHP versions
foo(1,
2,
3
);
?>
See also PHP RFC: Allow a trailing comma in function calls.
Must Call Parent Constructor
[Since 1.4.1] - [ -P Php/MustCallParentConstructor ] - [ Online docs ]
Some PHP native classes require a call to parent::__construct() to be stable.
As of PHP 7.3, two classes currently need that call : SplTempFileObject and SplFileObject .
The error is only emitted if the class is instantiated, and a parent class is called.
<?php
class mySplFileObject extends \SplFileObject {
public function __construct() {
// Forgottent call to parent::__construct()
}
}
(new mySplFileObject())->passthru();
?>
Alternatives :
- Add a call to the parent's constructor
- Remove the extension of the parent class
Undefined Variable
[Since 1.4.2] - [ -P Variables/UndefinedVariable ] - [ Online docs ]
Variable that is used before any initialisation.
It is recommended to use a default value for every variable used. When not specified, the default value is set to NULL by PHP.
Variable may be created in various ways : assignation, arguments, foreach blind variables, static and global variables.
This analysis doesn't handle dynamic variables, such as $$x . It also doesn't handle variables outside a method or function.
<?php
// Adapted from the PHP manual
$var = 'Bob';
$Var = 'Joe';
// The following line may emit a warning : Undefined variable: $undefined
echo "$var, $Var, $undefined"; // outputs "Bob, Joe, "
?>
Alternatives :
- Remove the expression that is using the undefined variable
- Fix the variable name
- Define the variable by assigning a value to it, before using it
Undefined Insteadof
[Since 1.4.2] - [ -P Traits/UndefinedInsteadof ] - [ Online docs ]
Insteadof tries to replace a method with another, but it doesn't exists. This happens when the replacing class is refactored, and some of its definition are dropped.
Insteadof may replace a non-existing method with an existing one, but not the contrary.
This error is not linted : it only appears at execution time.
<?php
trait A {
function C (){}
}
trait B {
function C (){}
}
class Talker {
use A, B {
B::C insteadof A;
B::D insteadof A;
}
}
new Talker();
?>
Alternatives :
- Remove the insteadof expression
- Fix the original method and replace it with an existing method
Method Collision Traits
[Since 1.4.2] - [ -P Traits/MethodCollisionTraits ] - [ Online docs ]
Two or more traits are included in the same class, and they have methods collisions.
Those collisions should be solved with a use expression. When they are not, PHP stops execution with a fatal error : Trait method M has not been applied, because there are collisions with other trait methods on C .
The code shown lints, but doesn't execute.
<?php
trait A {
public function A() {}
public function M() {}
}
trait B {
public function B() {}
public function M() {}
}
class C {
use A, B;
}
class D {
use A, B{
B::M insteadof A;
};
}
?>
See also Traits.
Class Could Be Final
[Since 1.4.3] - [ -P Classes/CouldBeFinal ] - [ Online docs ]
Any class that has no extension should be final by default.
As stated by Matthias Noback : If a class is not marked final, it has at least one subclass .
Prevent the 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
?>
Alternatives :
- Make the class final
- Extends the class
Can't Disable Class
[Since 0.8.4] - [ -P Security/CantDisableClass ] - [ Online docs ]
This is the list of potentially dangerous PHP class being used in the code, such as \Phar.
This analysis is the base for suggesting values for the disable_classes directive.
<?php
// This script uses ftp_connect(), therefore, this function shouldn't be disabled.
$phar = new Phar();
?>
ext/seaslog
[Since 1.4.4] - [ -P Extensions/Extseaslog ] - [ Online docs ]
Extension Seaslog.
An effective, fast, stable log extension for PHP.
<?php
$basePath_1 = SeasLog::getBasePath();
SeasLog::setBasePath('/log/base_test');
$basePath_2 = SeasLog::getBasePath();
var_dump($basePath_1,$basePath_2);
/*
string(19) "/log/seaslog-ciogao"
string(14) "/log/base_test"
*/
$lastLogger_1 = SeasLog::getLastLogger();
SeasLog::setLogger('testModule/app1');
$lastLogger_2 = SeasLog::getLastLogger();
var_dump($lastLogger_1,$lastLogger_2);
/*
string(7) "default"
string(15) "testModule/app1"
*/
?>
See also ext/SeasLog on Github and SeasLog.
Only Variable For Reference
[Since 1.4.6] - [ -P Functions/OnlyVariableForReference ] - [ Online docs ]
When a method is requesting an argument to be a reference, it cannot be called with a literal value.
The call must be made with a variable, or any assimilated data container : array, property or static property.
Note that PHP may detect this error at linting time, if the method is defined after being called : at that point, PHP will only check the problem during execution. This is definitely the case for methods, compared to functions or static methods.
<?php
// This is not possible
foo(1,2);
// This is working
foo($a, $b);
function foo($a, &$b) {}
?>
Alternatives :
- Put the literal value in a variable before calling the method.
- Omit the arguments, when it won't be used.
Wrong Access Style to Property
[Since 1.4.9] - [ -P Classes/UndeclaredStaticProperty ] - [ Online docs ]
Use the right syntax when reaching for a property. Static properties use the :: operator, and non-static properties use -> .
Mistaking one of the other raise two different reactions from PHP : Access to undeclared static property is a fatal error, while PHP Notice: Accessing static property aa::$a as non static is a notice.
This analysis reports both static properties with a `->` access, and non-static properties with a `::` access.
<?php
class a {
static public $a = 1;
function foo() {
echo self::$a; // right
echo $this->a; // WRONG
}
}
class b {
public $b = 1;
function foo() {
echo $this->$b; // right
echo b::$b; // WRONG
}
}
?>
Alternatives :
- Match the property call with the definition
- Make the property static
Invalid Pack Format
[Since 1.4.9] - [ -P Structures/InvalidPackFormat ] - [ Online docs ]
Some characters are invalid in a pack() format string.
pack() and unpack() accept the following format specifiers : aAhHcCsSnviIlLNVqQJPfgGdeExXZ .
unpack() also accepts a name after the format specifier and an optional quantifier.
All other situations is not a valid, and produces a warning : pack()
Check pack() documentation for format specifiers that were introduced in various PHP version, namely 7.0, 7.1 and 7.2.
<?php
$binarydata = pack("nvc*", 0x1234, 0x5678, 65, 66);
// the first unsigned short is stored as 'first'. The next matches are names with numbers.
$res = unpack('nfirst/vc*', $binarydata);
?>
Alternatives :
- Fix the packing format with correct values
Repeated Interface
[Since 1.4.9] - [ -P Interfaces/RepeatedInterface ] - [ Online docs ]
A class should implements only once an interface. An interface can only extends once another interface. In both cases, parent classes or interfaces must be checked.
PHP accepts multiple times the same interface in the implements clause. In fact, it doesn't do anything beyond the first implement.
This code may compile, but won't execute.
<?php
use i as j;
interface i {}
// Multiple ways to reference an interface
class foo implements i, \i, j {}
// This applies to interfaces too
interface bar extends i, \i, j {}
?>
Alternatives :
- Remove the interface usage at the lowest class or interface
Don't Read And Write In One Expression
[Since 1.4.9] - [ -P Structures/DontReadAndWriteInOneExpression ] - [ Online docs ]
Avoid giving value and using it at the same time, in one expression. This is an undefined behavior of PHP, and may change without warning.
One of those changes happens between PHP 7.2 and 7.3 :
<?php
$arr = [1];
$ref =& $arr[0];
var_dump($arr[0] + ($arr[0] = 2));
// PHP 7.2: int(4)
// PHP 7.3: int(3)
?>
Alternatives :
- Split the expression in two separate expressions
Pack Format Inventory
[Since 1.5.0] - [ -P Type/Pack ] - [ Online docs ]
All format used in the code with pack() and unpack().
<?php
$binarydata = "\x04\x00\xa0\x00";
$array = unpack("cn", $binarydata);
$initial = pack("cn", ...$array);
?>
See also `pack() Printf Format Inventory
[Since 1.5.0] - [ -P Type/Printf ] - [ Online docs ]
All format used in the code with printf(), vprintf(), sprintf(), scanf() and fscanf().
<?php
// Display a number with 2 digits
echo printf("%'.2d\n", 123);
?>
ext/decimal
[Since 1.5.2] - [ -P Extensions/Extdecimal ] - [ Online docs ]
Extension php-decimal, by Rudi Theunissen .
This library provides a PHP extension that adds support for correctly-rounded, arbitrary-precision decimal floating point arithmetic. Applications that rely on accurate numbers (ie. money, measurements, or mathematics) can use Decimal instead of float or string to represent numerical values.
<?php
use Decimal\Decimal;
$op1 = new Decimal("0.1", 4);
$op2 = "0.123456789";
print_r($op1 + $op2);
use Decimal\Decimal;
/**
* @param int $n The factorial to calculate, ie. $n!
* @param int $p The precision to calculate the factorial to.
*
* @return Decimal
*/
function factorial(int $n, int $p = Decimal::DEFAULT_PRECISION): Decimal
{
return $n < 2 ? new Decimal($n, $p) : $n * factorial($n - 1, $p);
}
echo factorial(10000, 32);
?>
See also PHP Decimal and libmpdec.
ext/psr
[Since 1.5.2] - [ -P Extensions/Extpsr ] - [ Online docs ]
Extension PSR : PHP Standards Recommendations.
This PHP extension provides the interfaces from the PSR standards as established by the PHP-FIG group. You can use interfaces provided by this extension in another extension easily - see this example.
Currently supported PSR :
- PSR-3 - `psr/http-message`
- PSR-11 - `psr/container`
- PSR-13 - `psr/link`
- PSR-15 - `psr/http-server`
- PSR-16 - `psr/simple-cache`
<?php
// Example from the tests, for Cache (PSR-6)
use Psr\Cache\CacheException;
class MyCacheException extends Exception implements CacheException {}
$ex = new MyCacheException('test');
var_dump($ex instanceof CacheException);
var_dump($ex instanceof Exception);
try {
throw $ex;
} catch( CacheException $e ) {
var_dump($e->getMessage());
}
?>
See also php-psr and PHP-FIG.
Should Yield With Key
[Since 1.5.2] - [ -P Functions/ShouldYieldWithKey ] - [ Online docs ]
iterator_to_array() overwrite generated values with the same key.
PHP generators are based on the yield keyword. They also delegate some generating to other methods, with yield from .
When delegating, yield from uses the keys that are generated with yield , and otherwise, it uses auto-generated index, starting with 0.
The trap is that each yield from reset the index generation and start again with 0. Coupled with iterator_to_array(), this means that the final generated array may lack some values, while a foreach() loop would yield all of them.
Thanks to Holger Woltersdorf for pointing this.
<?php
function g1() : Generator {
for ( $i = 0; $i < 4; $i++ ) { yield $i; }
}
function g2() : Generator {
for ( $i = 5; $i < 10; $i++ ) { yield $i; }
}
function aggregator() : Generator {
yield from g1();
yield from g2();
}
print_r(iterator_to_array());
/*
Array
(
[0] => 6
[1] => 7
[2] => 8
[3] => 9
[4] => 4 // Note that 4 and 5 still appears
[5] => 5 // They are not overwritten by the second yield
)
*/
foreach ( aggregator() as $i ) {
print $i.PHP_EOL;
}
/*
0 // Foreach has no overlap and yield it all.
1
2
3
4
5
6
7
8
9
*/
?>
Alternatives :
- Use iterator_to_array() on each generator separately, and use array_merge() to merge all the arrays.
- Always yield with distinct keys
- Avoid iterator_to_array() and use foreach()
Useless Method Alias
[Since 1.5.6] - [ -P Traits/UselessAlias ] - [ Online docs ]
It is not possible to declare an alias of a method with the same name.
PHP reports that Trait method f has not been applied, because there are collisions with other trait methods on x , which is a way to say that the alias will be in conflict with the method name.
When the method is the only one bearing a name, and being imported, there is no need to alias it. When the method is imported in several traits, the keyword insteadof is available to solve the conflict.
This code lints but doesn't execute.
<?php
trait t {
function h() {}
}
class x {
use t {
// This is possible
t::f as g;
// This is not possible, as the alias is in conflict with itself
// alias are case insensitive
t::f as f;
}
}
?>
Alternatives :
- Remove the alias
- Fix the alias or the origin method name
- Switch to insteadof, and avoid as keyword
ext/sdl
[Since 1.5.6] - [ -P Extensions/Extsdl ] - [ Online docs ]
Extensions ext/sdl.
Simple DirectMedia Layer (SDL) is a cross-platform software development library designed to provide a hardware abstraction layer for computer multimedia hardware components.
<?php
/**
* Example of how to change screen properties such as title, icon or state using the PHP-SDL extension.
*
* @author Santiago Lizardo <santiagolizardo@php.net>
*/
require 'common.php';
SDL_Init( SDL_INIT_VIDEO );
$screen = SDL_SetVideoMode( 640, 480, 16, SDL_HWSURFACE );
if( null == $screen )
{
fprintf( STDERR, 'Error: %s' . PHP_EOL, SDL_GetError() );
}
for( $i = 3; $i > 0; $i-- )
{
SDL_WM_SetCaption( "Switching to fullscreen mode in $i seconds...", null );
SDL_Delay( 1000 );
}
SDL_WM_ToggleFullscreen( $screen );
SDL_Delay( 3000 );
SDL_WM_ToggleFullscreen( $screen );
SDL_WM_SetCaption( "Back from fullscreen mode. Quitting in 2 seconds...", null );
SDL_Delay( 2000 );
SDL_FreeSurface( $screen );
SDL_Quit();
?>
See also phpsdl, Simple DirectMedia Layer and About SDL.
ext/wasm
[Since 1.5.7] - [ -P Extensions/Extwasm ] - [ Online docs ]
Extension WASM.
The goal of the project is to be able to run WebAssembly binaries from PHP directly. So much fun coming!
From the php-ext-wasm documentation :
<?php
//There is a toy program in examples/simple.rs, written in Rust (or any other language that compiles to WASM):
// Stored in file __DIR__ . '/simple.wasm'
/*
#[no_mangle]
pub extern "C" fn sum(x: i32, y: i32) -> i32 {
x + y
}
*/
$instance = new WASM\Instance(__DIR__ . '/simple.wasm');
var_dump(
$instance->sum(5, 37) // 42!
);
?>
See also php-ext-wasm.
Method Could Be Static
[Since 1.5.7] - [ -P Classes/CouldBeStatic ] - [ Online docs ]
A method that doesn't make any usage of $this could be turned into a static method.
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;
}
}
?>
Alternatives :
- Make the method static
- Make the method a standalone function
- Make use of $this in the method : may be it was forgotten.
Path lists
[Since 1.5.8] - [ -P Type/Path ] - [ Online docs ]
List of all paths that were found in the code.
Path are identified with this regex : ^(.*/)([^/]*)\.\w+$ . In particular, the directory delimiter is / : Windows delimiter \ are not detected.
URL are ignored when the protocol is present in the literal : http://www.example.com is not mistaken with a file.
<?php
// the first argument is recognized as an URL
fopen('/tmp/my/file.txt', 'r+');
// the string argument is recognized as an URL
$source = 'https://www.other-example.com/';
?>
See also Dir predefined constants and Supported Protocols and Wrappers.
Possible Missing Subpattern
[Since 1.6.1] - [ -P Php/MissingSubpattern ] - [ Online docs ]
When capturing subpatterns are the last ones in a regex, PHP doesn't fill their spot in the resulting array. This leads to a possible missing index in the result array.
The same applies to preg_replace() : the pattern may match the string, but no value is available is the corresponding sub-pattern.
In PHP 7.4, a new option was added : PREG_UNMATCHED_AS_NULL , which always provides a value for the subpatterns.
<?php
// displays a partial array, from 0 to 1
preg_match('/(a)(b)?/', 'adc', $r);
print_r($r);
/*
Array
(
[0] => a
[1] => a
)
*/
// displays a full array, from 0 to 2
preg_match('/(a)(b)?/', 'abc', $r);
print_r($r);
/*
Array
(
[0] => ab
[1] => a
[2] => b
)
*/
// double 'b' when it is found
print preg_replace(',^a(b)?,', './$1$1', 'abc'); // prints ./abbc
print preg_replace(',^a(b)?,', './$1$1', 'adc'); // prints ./dc
?>
Alternatives :
- Add an always capturing subpatterns after the last ?
- Move the ? inside the parenthesis, so the parenthesis is always on, but the content may be empty
- Add a test on the last index of the resulting array, to ensure it is available when needed
- Use the PREG_UNMATCHED_AS_NULL option (PHP 7.4+)
Assign And Compare
[Since 1.6.3] - [ -P Structures/AssigneAndCompare ] - [ Online docs ]
Assignation has a lower precedence than comparison. As such, the assignation always happens after the comparison. This leads to the comparison being stored in the variable, and not the value being compared.
<?php
if ($id = strpos($string, $needle) !== false) {
// $id now contains a boolean (true or false), but not the position of the $needle.
}
// probably valid comparison, as $found will end up being a boolean
if ($found = strpos($string, $needle) === false) {
doSomething();
}
// always valid comparison, with parenthesis
if (($id = strpos($string, $needle)) !== false) {
// $id now contains a boolean (true or false), but not the position of the $needle.
}
// Being a lone instruction, this is always valid : there is no double usage with if condition
$isFound = strpos($string, $needle) !== false;
?>
Alternatives :
- Use parenthesis
- Separate assignation and comparison
- Drop assignation or comparison
Typed Property Usage
[Since 1.6.2] - [ -P Php/TypedPropertyUsage ] - [ Online docs ]
PHP properties may be typed. Since PHP 7.4, it is possible to type properties, just like arguments and return values.
<?php
class User {
public int $id;
public string $name;
public function __construct(int $id, string $name) {
$this->id = $id;
$this->name = $name;
}
}
?>
See also Typed Properties 2.0 and Typed Properties in PHP 7.4.
Variable Is Not A Condition
[Since 1.6.5] - [ -P Structures/NoVariableIsACondition ] - [ Online docs ]
Avoid using a lone variable as a condition. It is recommended to use a comparative value, or one of the filtering function, such as isset(), empty().
Using the raw variable as a condition blurs the difference between an undefined variable and an empty value. By using an explicit comparison or validation function, it is easier to understand what the variable stands for.
Thanks to the PMB team for the inspiration.
<?php
if (isset($error)) {
echo 'Found one error : '.$error!;
}
//
if ($errors) {
print count($errors).' errors found : '.join('', $errors).PHP_EOL;
echo 'Not found';
}
?>
Alternatives :
- Make the validation explicit, by using a comparison operator, or one of the validation function.
ext/weakref
[Since 1.6.5] - [ -P Extensions/Extweakref ] - [ Online docs ]
Weak References for PHP.
Weak references provide a non-intrusive gateway to ephemeral objects. Unlike normal (strong) references, weak references do not prevent the garbage collector from freeing that object. For this reason, an object may be destroyed even though a weak reference to that object still exists. In such conditions, the weak reference seamlessly becomes invalid.
<?php
class MyClass {
public function __destruct() {
echo "Destroying object!\n";
}
}
$o1 = new MyClass;
$r1 = new WeakRef($o1);
if ($r1->valid()) {
echo "Object still exists!\n";
var_dump($r1->get());
} else {
echo "Object is dead!\n";
}
unset($o1);
if ($r1->valid()) {
echo "Object still exists!\n";
var_dump($r1->get());
} else {
echo "Object is dead!\n";
}
?>
See also Weak references and PECL extension that implements weak references and weak maps in PHP.
ext/pcov
[Since 1.6.5] - [ -P Extensions/Extpcov ] - [ Online docs ]
CodeCoverage compatible driver for PHP.
A self contained CodeCoverage compatible driver for PHP7. CodeCoverage provides collection, processing, and rendering functionality for PHP code coverage information.
<?php
\pcov\start();
$d = [];
for ($i = 0; $i < 10; $i++) {
$d[] = $i * 42;
}
\pcov\stop();
var_dump(\pcov\collect());
?>
See also PCOV and phpunit/php-code-coverage.
Insufficient Typehint
[Since 1.6.6] - [ -P Functions/InsufficientTypehint ] - [ Online docs ]
An argument is typehinted, but it actually calls methods that are not listed in the interface.
Classes may be implementing more methods than the one that are listed in the interface they also implements. This means that filtering objects with a typehint, but calling other methods will be solved at execution time : if the method is available, it will be used; if it is not, a fatal error is reported.
Inspired by discussion with Brandon Savage.
<?php
class x implements i {
function methodI() {}
function notInI() {}
}
interface i {
function methodI();
}
function foo(i $x) {
$x->methodI(); // this call is valid
$x->notInI(); // this call is not garanteed
}
?>
Alternatives :
- Extend the interface with the missing called methods
- Change the body of the function to use only the methods that are available in the interface
- Change the used objects so they don't depend on extra methods
Constant Dynamic Creation
[Since 1.6.7] - [ -P Constants/DynamicCreation ] - [ Online docs ]
Registering constant with dynamic values. Dynamic values include values read in external sources (files, databases, remote API, ... ), random sources (time, rand(), ...)
Dynamic constants are not possible with the const keyword, though static constant expression allows for a good range of combinations, including conditions.
<?php
$a = range(0, 4);
foreach($array as $i) {
define("A$i", $i);
define("N$i", true);
}
define("C", 5);
?>
See also PHP Constants.
PHP 8.0 Removed Functions
[Since 1.6.8] - [ -P Php/Php80RemovedFunctions ] - [ Online docs ]
The following PHP native functions were deprecated in PHP 8.0, and will be removed in PHP 9.0. * image2wbmp() * png2wbmp() * jpeg2wbmp() * ldap_sort() * hebrevc() * convert_cyr_string() * ezmlm_hash() * money_format() * get_magic_quotes_gpc() * get_magic_quotes_gpc_runtime() * create_function() * each() * read_exif_data() * gmp_random() * fgetss() * restore_include_path() * gzgetss() * mbregex_encoding() * mbereg() * mberegi() * mbereg_replace() * mberegi_replace() * mbsplit() * mbereg_match() * mbereg_search() * mbereg_search_pos() * mbereg_search_regs() * mbereg_search_init() * mbereg_search_getregs() * mbereg_search_getpos() * mbereg_search_setpos()
- Remove the code related to those functions
PHP 8.0 Removed Constants
[Since 1.6.8] - [ -P Php/Php80RemovedConstant ] - [ Online docs ]
The following PHP native constants were removed in PHP 8.0.
- INTL_IDNA_VARIANT_2003 (See Deprecate and remove INTL_IDNA_VARIANT_2003)
- MB_OVERLOAD_MAIL
- MB_OVERLOAD_STRING
- MB_OVERLOAD_REGEX
- Remove usage of those constants
An OOP Factory
[Since 1.6.7] - [ -P Patterns/Factory ] - [ Online docs ]
A method or function that implements a factory. A factory is a class that handles the creation of an object, based on parameters. The factory hides the logic that leads to the creation of the object.
<?php
class AutomobileFactory {
public static function create($make, $model) {
$className = "\Automaker\Brand$make";
return new $className($model);
}
}
// The factory is able to build any car, based on their
$fuego = AutomobileFactory::create('Renault', 'Fuego');
print_r($fuego->getMakeAndModel()); // outputs "Renault Fuego"
?>
See also Factory (object-oriented programming) and Factory.
Type Must Be Returned
[Since 1.6.9] - [ -P Functions/TypehintMustBeReturned ] - [ Online docs ]
When using a type for a method, it is compulsory to use a at least one return in the method's body. This is true for nullable type too : return alone won't be sufficient.
When the method contains a return expression, PHP doesn't lint unless the return expression has a value. Any value will do, and it will actually checked at execution time.
When the method contains no return expression, PHP only checks it at execution time.
There is no need for a return expression when the method throws an expression, yield values, triggers an error or triggers an assertion. Even in case of inheritance or implementation, the return type may be replaced by never .
<?php
// The function returns a value (here, correct object)
function foo() : Bar { return new Bar(); }
// The function should at least, return a value
function foo() : Bar { }
// The function should at least, return a value : Null or an object. Void, here, is not acceptable.
function foo() : ?Bar { return; }
?>
Alternatives :
- Add a return with a valid value
- Add a throw expression
- Add a trigger_error() call
- Add a assert(false, ...) expression
- If the method doesn't return, change the returntype to `never`
Clone With Non-Object
[Since 1.7.0] - [ -P Classes/CloneWithNonObject ] - [ Online docs ]
The clone keyword must be used on variables, properties or results from a function or method call.
clone cannot be used with constants or literals.
Cloning a non-object lint but won't execute.
<?php
class x { }
$x = new x();
// Valid clone
$y = clone $x;
// Invalid clone
$y = clone x;
?>
Alternatives :
- Only clone containers (like variables, properties...)
- Add typehint to injected properties, so they are checked as objects.
Check On __Call Usage
[Since 1.7.2] - [ -P Classes/CheckOnCallUsage ] - [ Online docs ]
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);
}
}
?>
Alternatives :
- Add a call to method_exists() before using any method name
- Relay the call to another object that doesn't handle __call() or __callStatic()
PHP Overridden Function
[Since 1.7.6] - [ -P Php/OveriddenFunction ] - [ Online docs ]
It is possible to declare and use a function with the same name as a PHP native, in a namespace.
Within the declaration namespace, it is easy to confuse the local version and the global version, unless the function has been prefixed with \ .
When a piece of code use overridden function, any newcomer may be confused by the usage of classic PHP native function in surprising situations.
It is recommended to avoid redeclare PHP native function in namespaces.
<?php
namespace A {
use function A\dirname as split;
function dirname($a, $b) { return __FUNCTION__; }
echo dirname('/a/b/c');
echo split('a', 'b');
echo \dirname('/a/b/c');
}
?>
Alternatives :
- Change the name of the function, in its declaration and usage.
ext/svm
[Since 1.7.8] - [ -P Extensions/Extsvm ] - [ Online docs ]
Extension SVM .
SVM is in interface with the libsvm , from . libsvm is a library for Support Vector Machines, a classification tool for machine learning.
<?php
$data = array(
array(-1, 1 => 0.43, 3 => 0.12, 9284 => 0.2),
array(1, 1 => 0.22, 5 => 0.01, 94 => 0.11),
);
$svm = new SVM();
$model = $svm->train($data);
$data = array(1 => 0.43, 3 => 0.12, 9284 => 0.2);
$result = $model->predict($data);
var_dump($result);
$model->save('model.svm');
?>
See also SVM, LIBSVM -- A Library for Support Vector Machines, ext/svm and ianbarber/php-svm.
Avoid option arrays in constructors
[Since 1.7.9] - [ -P Classes/AvoidOptionArrays ] - [ Online docs ]
Avoid option arrays in constructors. Use one parameter per injected element.
<?php
class Foo {
// Distinct arguments, all typehinted if possible
function __construct(A $a, B $b, C $c, D $d) {
$this->a = $a;
$this->b = $b;
$this->c = $c;
$this->d = $d;
}
}
class Bar {
// One argument, spread over several properties
function __construct(array $options) {
$this->a = $options['a'];
$this->b = $options['b'];
$this->c = $options['c'];
$this->d = $options['d'];
}
}
?>
Alternatives :
- 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
ext/ffi
[Since 1.7.9] - [ -P Extensions/Extffi ] - [ Online docs ]
Extension FFI : Foreign Function Interface .
This extension allows the loading of shared libraries (.DLL or .so), calling of C functions and accessing of C data structures in pure PHP, without having to have deep knowledge of the Zend extension API, and without having to learn a third “intermediate” language. The public API is implemented as a single class FFI with several static methods (some of them may be called dynamically), and overloaded object methods, which perform the actual interaction with C data.
<?php
//Example : Calling a function from shared library
// create FFI object, loading libc and exporting function printf()
$ffi = FFI::cdef(
"int printf(const char *format, ...);", // this is a regular C declaration
"libc.so.6");
// call C's printf()
$ffi->printf("Hello %s!\n", "world");
?>
See also ext/ffi and A PHP Compiler, aka The FFI Rabbit Hole.
ext/password
[Since 0.8.4] - [ -P Extensions/Extpassword ] - [ Online docs ]
Extension password.
The password hashing API provides an easy to use wrapper around crypt() and some other password hashing algorithms, to make it easy to create and manage passwords in a secure manner.
<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';
if (password_verify('rasmuslerdorf', $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
?>
See also Password Hashing and crypt man page.
ext/zend_monitor
[Since 1.7.9] - [ -P Extensions/Extzendmonitor ] - [ Online docs ]
Extension zend_monitor .
The Zend Monitor component is integrated into the runtime environment and serves as an alerting and collection mechanism for early detection of PHP script problems.
<?php
zend_monitor_pass_error();
?>
See also Zend Monitor - PHP API and `Zend Monitor `ext/uuid
[Since 1.7.9] - [ -P Extensions/Extuuid ] - [ Online docs ]
Extension UUID . A universally unique identifier (UUID) is a 128-bit number used to identify information in computer systems.
An interface to the libuuid system library. The libuuid library is used to generate unique identifiers for objects that may be accessible beyond the local system. The Linux implementation was created to uniquely identify ext2 filesystems created by a machine. This library generates UUIDs compatible with those created by the Open Software Foundation (OSF) Distributed Computing Environment (DCE) utility uuidgen.
<?php
// example from the test suitee of the extension.
// check basic format of generated UUIDs
$uuid = uuid_create();
if (preg_match("/[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}/", $uuid)) {
echo "basic format ok\n";
} else {
echo "basic UUID format check failed, generated UUID was $uuid\n";
}
?>
See also libuuid and ext/uuid.
Already Parents Trait
[Since 1.8.0] - [ -P Traits/AlreadyParentsTrait ] - [ Online docs ]
Trait is already used a parent's class or trait. There is no use to include it a second time, so one of them can be removed.
<?php
trait ta {
use tb;
}
trait t1 {
use ta;
use tb; // also used by ta
}
class b {
use t1; // also required by class c
use ta; // also required by trait t1
}
class c extends b {
use t1;
}
?>
Alternatives :
- Eliminate the trait in the parent class
- Eliminate the trait in the child class
Trait Not Found
[Since 1.7.9] - [ -P Traits/TraitNotFound ] - [ Online docs ]
A unknown trait is mentioned in the use expression.
The used traits all exist, but in the configuration block, some unmentioned trait is called.
Be aware that the traits used in any configuration block may originate in any use expression. PHP will check the configuration block at instantiation only, and after compiling : at that moment, it will know all the used traits across the class.
<?php
class x {
// c is not a used trait
use a, b { c::d insteadof e;}
// e is a used trait, even if is not in the use above.
use e;
}
?>
Alternatives :
- Switch the name of the trait to an existing and used trait
- Drop the expression that rely on the non-existent trait
Casting Ternary
[Since 1.8.0] - [ -P Structures/CastingTernary ] - [ Online docs ]
Type casting has a precedence over ternary operator, and is applied first. When this happens, the condition is cast, although it is often useless as PHP will do it if needed.
This applies to the ternary operator, the coalesce operator ?: and the null-coalesce operator ??.
The last example generates first an error `Undefined variable: b`, since $b is first cast to a string. The result is then an empty string, which leads to an empty string to be stored into $a. Multiple errors cascade.
<?php
$a = (string) $b ? 3 : 4;
$a = (string) $b ?: 4;
$a = (string) $b ?? 4;
?>
Alternatives :
- Add parenthesis around the ternary operator
- Skip the casting
- Cast in another expression
Concat Empty String
[Since 1.8.0] - [ -P Structures/ConcatEmpty ] - [ Online docs ]
Using a concatenation to make a value a string should be replaced with a type cast.
Type cast to a string is done with (string) operator. There is also the function strval(), although it is less recommended.
<?php
$a = 3;
// explicite way to cast a value
$b = (string) $a; // $b is a string with the content 3
// Wrong way to cast a value
$c = $a . ''; // $c is a string with the content 3
$c = '' . $a; // $c is a string with the content 3
$a .= ''; // $a is a string with the content 3
// Wrong way to cast a value
$c = $a . '' . $b; // This is not reported. The empty string is useless, but not meant to type cast
?>
Alternatives :
- Avoid concatenating with empty strings
- Use (string) operator to cast to string
- Remove any concatenated empty string
Concat And Addition
[Since 1.8.0] - [ -P Php/ConcatAndAddition ] - [ Online docs ]
Precedence between addition and concatenation has changed. In PHP 7.4, addition has precedence, and before, addition and concatenation had the same precedence.
From the RFC : Currently the precedence of '.', '+' and '-' operators are equal. Any combination of these operators are simply evaluated left-to-right .
This is counter-intuitive though: you rarely want to add or subtract concatenated strings which in general are not numbers. However, given PHP's capability of seamlessly converting an integer to a string, concatenation of these values is desired.
This analysis reports any addition and concatenation that are mixed, without parenthesis. Addition also means substraction here, aka using `+` or `-`.
The same applies to bitshift operations, << and >>``. There is no RFC for this change.
<?php
// Extracted from the RFC
echo "sum: " . $a + $b;
// current behavior: evaluated left-to-right
echo ("sum: " . $a) + $b;
// desired behavior: addition and subtraction have a higher precendence
echo "sum :" . ($a + $b);
?>
Alternatives :
- Add parenthesis around the addition to ensure its expected priority
- Move the addition outside the concatenation
Useless Argument
[Since 1.8.0] - [ -P Functions/UselessArgument ] - [ Online docs ]
The argument is always used with the same value. This value could be hard coded in the method, and save one argument slot.
There is no indication that this argument will be used with other values. It may be a development artifact, that survived without cleaning.
Methods with less than 3 calls are not considered here, to avoid reporting methods used once. Also, arguments with a default value are omitted.
The chances of useless arguments decrease with the number of usage. The parameter maxUsageCount prevents highly called methods (more than the parameter value) to be processed.
<?php
// All foo2 arguments are used with different values
function foo2($a, $b) {}
foo2(1, 2);
foo2(2, 2);
foo2(3, 3);
// The second argument of foo is always used with 2
function foo($a, $b) {}
foo(1, 2);
foo(2, 2);
foo(3, 2);
?>
Alternatives :
- Remove the argument and hard code its value inside the method
- Add the value as default in the method signature, and drop it from the calls
- Add calls to the method, with more varied arguments
No Append On Source
[Since 1.8.2] - [ -P Structures/NoAppendOnSource ] - [ Online docs ]
Do not append new elements to an array in a foreach loop. Since PHP 7.0, the array is still used as a source, and will be augmented, and used again.
Thanks to Frederic Bouchery for the reminder.
<?php
// Relying on the initial copy
$a = [1];
$initial = $a;
foreach($initial as $v) {
$a[] = $v + 1;
}
// Keep new results aside
$a = [1];
$tmp = [];
foreach($a as $v) {
$tmp[] = $v + 1;
}
$a = array_merge($a, $tmp);
unset($tmp);
// Example, courtesy of Frederic Bouchery
// This is an infinite loop
$a = [1];
foreach($a as $v) {
$a[] = $v + 1;
}
?>
Alternatives :
- Use a copy of the source, to avoid modifying it during the loop
- Store the new values in a separate storage
Memoize MagicCall
[Since 1.8.3] - [ -P Performances/MemoizeMagicCall ] - [ Online docs ]
Cache calls to magic methods in local variable. Local cache is faster than calling again the magic method as soon as the second call, provided that the value hasn't changed.
__get is slower, as it turns a simple member access into a full method call.
The caching is not possible if the processing of the object changes the value of the property.
<?php
class x {
private $values = array();
function __get($name) {
return $this->values[$name];
}
// more code to set values to this class
}
function foo(x $b) {
$a = $b->a;
$c = $b->c;
$d = $c; // using local cache, no new access to $b->__get($name)
$e = $b->a; // Second access to $b->a, through __get
}
function bar(x $b) {
$a = $b->a;
$c = $b->c;
$b->bar2(); // this changes $b->a and $b->c, but we don't see it
$d = $b->c;
$e = $b->a; // Second access to $b->a, through __get
}
?>
Alternatives :
- Cache the value in a local variable, and reuse that variable
- Make the property concrete in the class, so as to avoid __get() altogether
Unused Class Constant
[Since 1.8.4] - [ -P Classes/UnusedConstant ] - [ Online docs ]
The class constant is unused. Consider removing it or using it.
Class constants may be used in expressions, in static expressions, when building other constants, or in default values.
<?php
class foo {
public const UNUSED = 1; // No mention in the code
private const USED = 2; // used constant
function bar() {
echo self::USED;
}
}
?>
Alternatives :
- Remove the class constant
- Use the class constant
Infinite Recursion
[Since 1.8.6] - [ -P Structures/InfiniteRecursion ] - [ Online docs ]
A method is calling itself, with unchanged arguments. This might repeat indefinitely.
This rules applies to recursive functions without any condition. This also applies to function which inject the incoming arguments, without modifications.
<?php
function foo($a, $b) {
if ($a > 10) {
return;
}
foo($a, $b);
}
function foo2($a, $b) {
++$a; // $a is modified
if ($a > 10) {
return;
}
foo2($a, $b);
}
?>
Alternatives :
- Modify arguments before injecting them again in the same method
- Use different values when calling the same method
Null Or Boolean Arrays
[Since 1.8.6] - [ -P Arrays/NullBoolean ] - [ Online docs ]
Null, int, floats, booleans are valid with PHP array syntx. Yet, they only produces null values. They also did not emits any warning until PHP 7.4.
Older code used to initialize variables as null, sometimes explictly, and then, use them as arrays. The current support for this syntax is for backward compatibility.
Illegal keys, such as another array, will also generate a NULL value, instead of a Fatal error.
<?php
// outputs NULL
var_dump(null[0]);
var_dump(null[[]]);
const MY_CONSTANT = true;
// outputs NULL
var_dump(MY_CONSTANT[10]);
?>
Alternatives :
- Avoid using the array syntax on null and boolean
- Avoid using null and boolean on constant that are expecting arrays
Dependant Abstract Classes
[Since 1.8.6] - [ -P Classes/DependantAbstractClass ] - [ Online docs ]
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;
}
?>
Alternatives :
- Make the class only use its own resources
- Split the class in autonomous classes
- Add local property definitions to make the class independent
Wrong Type Returned
[Since 1.8.7] - [ -P Functions/WrongReturnedType ] - [ Online docs ]
The returned value is not compatible with the specified return type.
<?php
// classic error
function bar() : int {
return 'A';
}
// classic static error
const B = 2;
function bar() : string {
return B;
}
// undecideable error
function bar($c) : string {
return $c;
}
// PHP lint this, but won't execute it
function foo() : void {
// No return at all
}
?>
Alternatives :
- Match the return type with the return value
- Remove the return expression altogether
- Add a typecast to the returning expression
Overwritten Source And Value
[Since 1.8.9] - [ -P Structures/ForeachSourceValue ] - [ Online docs ]
In a foreach(), it is best to keep source and values distinct. Otherwise, they overwrite each other.
Since PHP 7.0, PHP makes a copy of the original source, then works on it. This makes possible to use the same name for the source and the values.
When the source is used as the value, the elements in the array are successively assigned to itself. After the loop, the original array has been replaced by its last element.
The same applies to the index, or to any variable in a list() structure, used in a foreach().
<?php
// displays 0-1-2-3-3
$array = range(0, 3);
foreach($array as $array) {
print $array . '-';
}
print_r($array);
/* displays 0-1-2-3-Array
(
[0] => 0
[1] => 1
[2] => 2
[3] => 3
)
*/
$array = range(0, 3);
foreach($array as $v) {
print $v . '-';
}
print_r($array);
?>
Alternatives :
- Keep the source, the index and the values distinct
Avoid mb_dectect_encoding()
[Since 1.8.9] - [ -P Php/AvoidMbDectectEncoding ] - [ Online docs ]
mb_dectect_encoding() is bad at guessing encoding.
For example, UTF-8 and ISO-8859-1 share some common characters : when a string is build with them it is impossible to differentiate the actual encoding.
<?php
$encoding = mb_encoding_detect($_GET['name']);
?>
Alternatives :
- Store and transmit the data format
array_key_exists() Works On Arrays
[Since 1.9.0] - [ -P Php/ArrayKeyExistsWithObjects ] - [ Online docs ]
array_key_exists() requires arrays as second argument. Until PHP 7.4, objects were also allowed, yet it is now deprecated.
<?php
// Valid way to check for key
$array = ['a' => 1];
var_dump(array_key_exists('a', $array))
// Deprecated since PHP 7.4
$object = new Stdclass();
$object->a = 1;
var_dump(array_key_exists('a', $object))
?>
Alternatives :
- Use the (array) cast to turn the object into an array
- Use the native PHP function proprety_exists() or isset() on the property to check them.
Numeric Literal Separator
[Since 1.9.0] - [ -P Php/IntegerSeparatorUsage ] - [ Online docs ]
Integer and floats may be written with internal underscores. This way, it is possible to separate large number into smaller groups, and make them more readable.
Numeric Literal Separators were introduced in PHP 7.4 and are not backward compatible.
<?php
$a = 1_000_000_000; // A billion
$a = 1000000000; // A billion too...
$b = 107_925_284.88; // 6 light minute to kilometers = 107925284.88 kilometers
$b = 107925284.88; // Same as above
?>
See also PHP RFC: Numeric Literal Separator.
Class Without Parent
[Since 1.9.0] - [ -P Classes/NoParent ] - [ Online docs ]
Classes should not refer to parent when it is not extending another class.
In PHP 7.4, it is a Deprecated warning. In PHP 7.3, it was a Fatal error, when the code was eventually executed. In PHP 8.0, PHP detects this error at compile time, except for parent keyword in a closure.
parent usage in trait is detected. It is only reported when the trait is used inside a class without parent, as the trait may also be used in another class, which has a parent.
<?php
class x {
function foo() {
parent::foo();
}
}
?>
Alternatives :
- 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
Scalar Are Not Arrays
[Since 1.9.0] - [ -P Php/ScalarAreNotArrays ] - [ Online docs ]
It is wrong to use a scalar as an array, a Warning is emitted. PHP 7.4 emits a Warning in such situations.
Typehinted argument with a scalar are reported by this analysis. Also, nullable arguments, both with typehint and return type hint.
<?php
// Here, $x may be null, and in that case, the echo will fail.
function foo(?A $x) {
echo $x[2];
}
?>
Alternatives :
- Update type hints to avoid scalar values
- Remove the array syntax in the code using the results
- Cast to string type, so the array notation is accessible
array_merge() And Variadic
[Since 1.9.2] - [ -P Structures/ArrayMergeAndVariadic ] - [ Online docs ]
Always check value in variadic before using it with array_merge() and array_merge_recursive().
Before PHP 7.4, array_merge() and array_merge_recursive() would complain when no argument was provided. As such, using the spread operator `...` on an empty array() would yield no argument, and an error.
<?php
$b = array_merge(...$x);
?>
Alternatives :
- Add a check to the spread variable to ensure it is not empty
- Append an empty array to to the spread variable to ensure it is not empty
Implode() Arguments Order
[Since 1.9.2] - [ -P Structures/ImplodeArgsOrder ] - [ Online docs ]
implode() used to accept two signatures, but is only recommending one. Both types orders of string then array, and array then string have been possible until PHP 7.4.
In PHP 7.4, the order array then string is deprecated, and emits a warning. It will be removed in PHP 8.0.
<?php
$glue = ',';
$pieces = range(0, 4);
// documented argument order
$s = implode($glue, $pieces);
// Pre 7.4 argument order
$s = implode($pieces, $glue);
// both produces 0,1,2,3,4
?>
Alternatives :
- Always use the array as the second argument
PHP 7.4 Removed Directives
[Since 1.9.3] - [ -P Php/Php74RemovedDirective ] - [ Online docs ]
List of directives that are removed in PHP 7.4.
- allow_url_include
- Stop using this directive
strip_tags() Skips Closed Tag
[Since 1.9.3] - [ -P Structures/StripTagsSkipsClosedTag ] - [ Online docs ]
strip_tags() skips non-self closing tags. This means that tags such as
will be ignored from the second argument of the function.
<?php
$input = 'a<br />';
// Displays 'a' and clean the tag
echo strip_tags($input, '<br>');
// Displays 'a<br />' and skips the allowed tag
echo strip_tags($input, '<br/>');
?>
Alternatives :
- Do not use self-closing tags in the second parameter
No Spread For Hash
[Since 1.9.3] - [ -P Arrays/NoSpreadForHash ] - [ Online docs ]
The spread operator ... used to work only on integer-indexed arrays. This limitation was removed in PHP 8.1 and more recent.
<?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]);
?>
Alternatives :
- Add a call to array_values() instead of the hash
- Check the arguments beforehand with array_is_list()
- Upgrade to PHP 8.1
Use Covariance
[Since 1.9.3] - [ -P Php/UseCovariance ] - [ Online docs ]
Covariance is compatible return typehint. A child class may return an object of a child class of the return type of its parent's method.
Since a children class may return a children class of the return type, the evolution is in the same order.
Covariance is a PHP 7.4 feature. Covariance is distinct from argument contravariance.
<?php
class X {
function m(Y $z): X {}
}
// m is overwriting the parent's method.
// The return type is different.
// The return type is compatible, as Y is also a sub-class of X.
class Y extends X {
function m(X $z): Y {}
}
?>
See also Covariant Returns and Contravariant Parameters and `Php/UseContravariance`_.
Use Contravariance
[Since 1.9.3] - [ -P Php/UseContravariance ] - [ Online docs ]
Contravariance is compatible argument typehint. A child class may accept an object of a parent class of the argument type of its parent's method.
Since a children class may accept a parent class of the argument type, the evolution is in opposite order.
Contravariance is a PHP 7.4 feature. Contravariance is distinct from return type covariance.
<?php
class X {
function m(Y $z): X {}
}
// m is overwriting the parent's method.
// The return type is different.
// The return type is compatible, as Y is also a sub-class of X.
class Y extends X {
function m(X $z): Y {}
}
?>
See also Covariant Returns and Contravariant Parameters and `Php/UseCovariance`.
Use Arrow Functions
[Since 1.9.4] - [ -P Functions/UseArrowFunctions ] - [ Online docs ]
Arrow functions are closures that require one expression of code. They also include all the variables of the current context, unless they are made static.
Arrow functions were introduced in PHP 7.4. They added the reserved keyword fn .
<?php
array_map(fn(A $b): int => $b->c, $array);
function array_values_from_keys($arr, $keys) {
return array_map(fn($x) => $arr[$x], $keys);
}
?>
See also RFC : Arrow functions and Arrow functions in PHP.
Max Level Of Nesting
[Since 1.9.3] - [ -P Structures/MaxLevelOfIdentation ] - [ Online docs ]
Avoid nesting structures too deep, as it hurts readability.
Nesting structures are : if/then, switch, for, foreach, while, do...while. Ternary operator, try/catch are not considered a nesting structures.
Closures, and more generally, functions definitions are counted separatedly.
This analysis checks for 4 levels of nesting, by default. This may be changed by configuration.
<?php
// 5 levels of indentation
function foo() {
if (1) {
if (2) {
if (3) {
if (4) {
if (5) {
51;
} else {
5;
}
} else {
4;
}
} else {
3;
}
} else {
2;
}
} else {
1;
}
}
// 2 levels of indentation
function foo() {
if (1) {
if (2) {
// 3 levels of indentation
return function () {
if (3) {
if (4) {
if (5) {
51;
} else {
5;
}
} else {
4;
}
} else {
3;
}
}
} else {
2;
}
} else {
1;
}
}
?>
Alternatives :
- Refactor code to avoid nesting
- Export some nested blocks to an external method or function
Spread Operator For Array
[Since 1.9.4] - [ -P Php/SpreadOperatorForArray ] - [ Online docs ]
The variadic operator may be used with arrays. This has been introduced in PHP 7.4.
list() is not allowed to use this operator, as list() expected variables, not values.
<?php
$array = [1, 2, 3];
$extended_array = [...$array, 4, 5, 6];
// invalid syntax
[...$a] = [1,2,3];
?>
See also Spread Operator in Array Expression.
Nested Ternary Without Parenthesis
[Since 1.9.4] - [ -P Php/NestedTernaryWithoutParenthesis ] - [ Online docs ]
It is not allowed to nest ternary operator within itself, without parenthesis. This has been implemented in PHP 7.4.
The reason behind this feature is to keep the code expressive. See the Warning message for more explanations
<?php
$a ? 1 : ($b ? 2 : 3);
// Still valid, as not ambiguous
$a ? $b ? 1 : 2 : 3;
// Produces a warning
//Unparenthesized `a ? b : c ? d : e` is deprecated. Use either `(a ? b : c) ? d : e` or `a ? b : (c ? d : e)`
$a ? 1 : $b ? 2 : 3;
?>
Alternatives :
- Add parenthesis to nested ternary calls
Should Use Explode Args
[Since 1.9.4] - [ -P Structures/ShouldUseExplodeArgs ] - [ Online docs ]
explode() has a third argument, which limits the amount of exploded elements. With it, it is possible to collect only the first elements, or drop the last ones.
<?php
$exploded = explode(DELIMITER, $string);
// use explode(DELIMITER, $string, -1);
array_pop($exploded);
// use explode(DELIMITER, $string, -2);
$c = array_slice($exploded, 0, -2);
// with explode()'s third argument :
list($a, $b) = explode(DELIMITER, $string, 2);
// with list() omitted arguments
list($a, $b, ) = explode(DELIMITER, $string);
?>
Alternatives :
- Add the third argument to the explode() call
Use array_slice()
[Since 1.9.5] - [ -P Performances/UseArraySlice ] - [ Online docs ]
Array_slice() is de equivalent of substr() for arrays.
array_splice() is also available, to remove a portion of array inside the array, not at the ends. This has no simple equivalent for strings.
<?php
$array = range(0, 9);
// Extract the 5 first elements
print_r(array_slice($array, 0, 5));
// Extract the 4 last elements
print_r(array_slice($array, -4));
// Extract the 2 central elements : 4 and 5
print_r(array_splice($array, 4, 2));
// slow way to remove the last elementst of an array
for($i = 0; $i < 4) {
array_pop($array);
}
?>
Alternatives :
- Use array_slice()
Too Many Array Dimensions
[Since 1.9.4] - [ -P Arrays/TooManyDimensions ] - [ Online docs ]
This analysis reports when arrays have too many dimensions. This happens when arrays are too deeply nested inside other arrays.
PHP has no nesting limit, and accepts any number of of dimensions. This is usually very memory hungry, and could be better replaced with classes.
The default threshold for this rule is 3 (see examples above).
<?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
?>
Alternatives :
- Replace the arrays by classes
- Flatten the structure of the arrays
Coalesce And Concat
[Since 1.9.4] - [ -P Structures/CoalesceAndConcat ] - [ Online docs ]
The concatenation operator . has precedence over the coalesce operator ?? .
It is recommended to add parenthesis to make this expression explicit.
<?php
// Parenthesis are the right solution when in doubt
echo a . ($b ?? 'd') . $e;
// 'a' . $b is evaluated first, leading ot a useless ?? operator
'a' . $b ?? $c;
// 'd' . 'e' is evaluated first, leading to $b OR 'de'.
echo $b ?? 'd' . 'e';
?>
Alternatives :
- Add parenthesis around ?? operator to avoid misbehavior
- Add parenthesis around the else condition to avoid misbehavior ( ?? ($a . $b))
- Do not use dot and ?? together in the same expression
Comparison Is Always The Same
[Since 1.9.4] - [ -P Structures/AlwaysFalse ] - [ Online docs ]
Based on the incoming type of arguments, the comparison always yields the same value. The whole condition might be useless.
<?php
function foo(array $a) {
// This will always fail
if ($a === 1) {
} elseif (is_int($a)) {
}
// This will always succeed
if ($a !== null) {
} elseif (is_null($a)) {
}
}
?>
Alternatives :
- Remove the constant condition and its corresponding blocks
- Make the constant condition variable
Incompatible Signature Methods With Covariance
[Since 1.3.3] - [ -P Classes/IncompatibleSignature74 ] - [ Online docs ]
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) {}
}
?>
Alternatives :
- Make signatures compatible again
Interfaces Is Not Implemented
[Since 1.9.5] - [ -P Interfaces/IsNotImplemented ] - [ Online docs ]
Classes that implements interfaces, must implements each of the interface's methods. Otherwise, the class shall be marked as abstract .
This problem tends to occur in code that splits interfaces and classes by file. This means that PHP's linting will skip the definitions and not find the problem. At execution time, the definitions will be checked, and a Fatal error will occur.
This situation usually detects code that was forgotten during a refactorisation of the interface or the class and its siblings.
<?php
class x implements i {
// This method implements the foo method from the i interface
function foo() {}
// The method bar is missing, yet is requested by interface i
function foo() {}
}
interface i {
function foo();
function bar();
}
?>
Alternatives :
- Implements all the methods from the interfaces
- Remove the class
- Make the class abstract
- Make the missing methods abstract
No Literal For Reference
[Since 1.9.5] - [ -P Functions/NoLiteralForReference ] - [ Online docs ]
Method arguments and return values may be by reference. Then, they need to be a valid variable.
Objects are always passed by reference, so there is no need to explicitly declare it.
Expressions, including ternary operator, produce value, and can't be used by reference directly. This is also the case for expression that include one or more reference.
Wrongly passing a value as a reference leads to a PHP Notice.
<?php
// variables, properties, static properties, array items are all possible
$a = 1;
foo($a);
//This is not possible, as a literal can't be a reference
foo(1);
function foo(&$int) { return $int; }
// This is not a valid reference
function &bar() { return 2; }
function &bar2() { return 2 + $r; }
?>
Alternatives :
- Remove the reference in the method signature (argument or return value)
- Make the argument an object, by using a typehint (non-scalar)
- Put the value into a variable prior to call (or return) the method
Interfaces Don't Ensure Properties
[Since 1.9.5] - [ -P Interfaces/NoGaranteeForPropertyConstant ] - [ Online docs ]
When using an interface as a type, properties are not enforced. They might be not available, and lead to a Fatal Error.
An interface is a template for a class, which specify the minimum amount of methods and constants. Properties are never defined in an interface, and should not be relied upon.
Properties may be defined in an abstract class.
<?php
interface i {
function m () ;
}
class x implements i {
public $p = 1;
function m() {
return $this->p;
}
}
function foo(i $i, x $x) {
// this is invalid, as $p is not defined in i, so it may be not available
echo $i->p;
// this is valid, as $p is defined in $x
echo $x->p;
}
?>
Alternatives :
- Use classes for type when properties are accessed
- Only use methods and constants which are available in the interface
- Use an abstract class
Non Nullable Getters
[Since 1.9.6] - [ -P Classes/NonNullableSetters ] - [ Online docs ]
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;
}
}
?>
Alternatives :
- Remove the nullable option and the tests on null .
Too Many Dereferencing
[Since 1.9.7] - [ -P Classes/TooManyDereferencing ] - [ Online docs ]
Linking too many properties and methods, one to the other.
This analysis counts both static calls and normal call; methods, properties and constants. It also takes into account arrays along the way.
The default limit of chaining methods and properties is set to 7 by default.
Too many chained methods is harder to read.
<?php
// 9 chained calls.
$main->getA()->getB()->getC()->getD()->getE()->getF()->getG()->getH()->getI()->property;
?>
Can't Implement Traversable
[Since 1.9.8] - [ -P Interfaces/CantImplementTraversable ] - [ Online docs ]
It is not possible to implement the Traversable interface. The alternative is to implement Iterator or IteratorAggregate , which also implements Traversable .
Traversable may be useful when used with instanceof .
<?php
// This lints, but doesn't run
class x implements Traversable {
}
if( $argument instanceof Traversable ) {
// doSomething
}
?>
Alternatives :
- Implement Iterator or IteratorAggregate
Is_A() With String
[Since 1.9.9] - [ -P Php/IsAWithString ] - [ Online docs ]
When using is_a() with a string as first argument, the third argument is compulsory. The third argument is $allow_string , and is necessary to work on strings.
<?php
// is_a() works with string as first argument, when the third argument is 'true'
if (is_a('A', 'B', true)) {}
// is_a() works with object as first argument
if (is_a(new A, 'A')) {}
?>
Alternatives :
- Add the third argument, and set it to true
- Use an object as a first argument
Mbstring Unknown Encoding
[Since 1.9.9] - [ -P Structures/MbstringUnknownEncoding ] - [ Online docs ]
The encoding used is not known to the ext/mbstring extension.
This analysis takes in charge all mbstring encoding and aliases. The full list of supported mbstring encoding is available with mb_list_encodings(). Each encoding alias is available with mb_encoding_aliases().
<?php
// Invalid encoding
$str = mb_strtolower($str, 'utf_8');
// Valid encoding
$str = mb_strtolower($str, 'utf8');
$str = mb_strtolower($str, 'UTF8');
$str = mb_strtolower($str, 'UTF-8');
?>
Alternatives :
- Use a valid mbstring encoding
Mbstring Third Arg
[Since 1.9.9] - [ -P Structures/MbstringThirdArg ] - [ Online docs ]
Some mbstring functions use the third argument for offset, not for encoding.
Those are the following functions :
- mb_strrichr()
- mb_stripos()
- mb_strrpos()
- mb_strstr()
- mb_stristr()
- mb_strpos()
- mb_strripos()
- mb_strrchr()
- mb_strrichr()
<?php
// Display BC
echo mb_substr('ABC', 1 , 2, 'UTF8');
// Yields Warning: mb_substr() expects parameter 3 to be int, string given
// Display 0 (aka, substring from 0, for length (int) 'UTF8' => 0)
echo mb_substr('ABC', 1 ,'UTF8');
?>
Alternatives :
- Add a third argument
- Use the default encoding (aka, omit both third AND fourth argument)
Typo 3 usage
[Since 1.9.9] - [ -P Vendors/Typo3 ] - [ Online docs ]
This rule reports usage of the Typo 3 CMS API in the code.
TYPO3 is a free enterprise-class CMS based on PHP. It combines open source code with reliability and true scalability.
<?php
declare(strict_types=1);
namespace MyVendor\SjrOffers\Controller;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class OfferController extends ActionController
{
// action methods will be following here
}
?>
See also Typo3.
Concrete5 usage
[Since 1.9.9] - [ -P Vendors/Concrete5 ] - [ Online docs ]
This analysis reports usage of the Concrete 5 framework.
<?php
namespace Application\Controller\PageType;
use Concrete\Core\Page\Controller\PageTypeController;
class BlogEntry extends PageTypeController
{
public function view()
{
}
}
?>
See also Concrete 5.
Immutable Signature
[Since 1.9.9] - [ -P Classes/ImmutableSignature ] - [ Online docs ]
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.
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.
<?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) {}
}
?>
See also Covariance and contravariance (computer science) and extends.
Merge If Then
[Since 1.9.9] - [ -P Structures/MergeIfThen ] - [ Online docs ]
Two nested if/then may be merged into one, by merging the two conditions. This is often a development artifact.
<?php
// two merged conditions
if ($a == 1 && $b == 2) {
// doSomething()
}
// two distinct conditions
// two nesting
if ($a == 1) {
if ($b == 2) {
// doSomething()
}
}
?>
Alternatives :
- Merge the two structures into one
Wrong Type With Call
[Since 1.9.9] - [ -P Functions/WrongTypeWithCall ] - [ Online docs ]
This analysis checks that a call to a method uses the types.
This analysis is compatible with Union types and with Intersection types.
Currently, this analysis doesn't take into account strict_types = 1 . As such, int and string won't be compatible.
<?php
function foo(string $a) {
}
// wrong type used
foo(1);
// wrong type used
foo("1");
?>
Alternatives :
- Use the right type with all arguments
- Force the type with a cast
- Check the type before calling
Shell commands
[Since 1.9.9] - [ -P Type/Shellcommands ] - [ Online docs ]
Shell commands, called from PHP.
Shell commands are detected with the italic quotes, and using shell_exec(), system(), exec() and proc_open().
<?php
// Shell command in a shell_exec() call
shell_exec('ls -1');
// Shell command with backtick operator
`ls -1 $path`;
?>
See also Execution operator, shell_exec and exec.
Links Between Parameter And Argument
[Since 2.0.6] - [ -P Dump/ParameterArgumentsLinks ] - [ Online docs ]
Collect various stats about arguments and parameter usage.
A parameter is one slot in the method definition. An argument is a slot in the method call. Both are linked by the method and their respective position in the argument list.
- Total number of argument usage, linked to a parameter : this excludes arguments from external libraries and native PHP functions. For reference.
- Number of identical parameter : cases where argument and parameter have the same name.
- Number of different parameter : cases where argument and parameter have the different name.
- Number of expression argument : cases where argument is an expression
- Number of constant argument : cases where the argument is a constant
<?php
function foo($a, $b) {
// some code
}
// $a is the same as the parameter
// $c is different from the paramter $b
foo($a, $c);
const C = 1;
// Foo is called with a constant (1rst argument)
// Foo is called with a expression (2nd argument)
foo(C, 1+3);
?>
Not Equal Is Not !==
[Since 2.0.6] - [ -P Structures/NotEqual ] - [ Online docs ]
Not and Equal operators, used separately, don't amount to the different operator !== .
!$a == $b first turns $a into the opposite boolean, then compares this boolean value to $b . On the other hand, $a !== $b compares the two variables for type and value, and returns a boolean.
Note that the instanceof operator may be use with this syntax, due to operator precedence.
<?php
if ($string != 'abc') {
// doSomething()
}
// Here, string will be an boolean, leading
if (!$string == 'abc') {
// doSomething()
}
// operator priority may be confusing
if (!$object instanceof OneClass) {
// doSomething()
}
?>
Alternatives :
- Use the != or !==
- Use parenthesis
Php 8.0 Variable Syntax Tweaks
[Since 2.0.8] - [ -P Php/Php80VariableSyntax ] - [ Online docs ]
Several variable syntaxes are added in version 8.0. They extends the PHP 7.0 syntax updates, and fix a number of edges cases.
In particular, new and instanceof now support a way to inline the expression, rather than use a temporary variable.
Magic constants are now accessible with array notation, just like another constant. It is also possible to use method calls : although this is Syntacticly correct for PHP, this won't be executed, as the left operand is a string, and not an object.
<?php
// array name is dynamically build
echo "foo$bar"[0];
// static method
"foo$bar"::baz();
// static property
"foo$bar"::$baz;
// Syntactly correct, but not executable
"foo$bar"->baz();
// expressions with instanceof and new
$object = new ("class_".$name);
$x instanceof ("class_$name");
// PHP 7.0 style
$className = "class_".$name;
$object = new $className;
?>
See also PHP RFC: Variable Syntax Tweaks and scalar_objects in PHP.
Don't Collect Void
[Since 2.0.9] - [ -P Functions/DontUseVoid ] - [ Online docs ]
When a method has the return type void, there is no need to collect the result. The collected value is always null .
With a void return type, the method is intented to be without return value. This analysis also include methods which are not returning anything, and could be completed with a void returntype.
<?php
function foo() : void {
// doSomething()
}
// This is useless
$result = foo();
// This is useless. It looks like this is a left over from code refactoring
echo foo();
// No explicitly returned values
function goo() {
// doSomething()
}
?>
Alternatives :
- Move the call to the function to its own expression with a semi-colon.
- Remove assignations of the result of these methods calls.
- Do not use these methods as parameters.
Php 8.0 Only TypeHints
[Since 2.0.9] - [ -P Php/Php80OnlyTypeHints ] - [ Online docs ]
Three scalar typehints are introduced in version 8.0. They are mixed , false and null .
false represents a false boolean, and nothing else. It is more restrictive than a boolean, which accepts true too.
null is an alternative syntax to ? : it allows the type to be null .
mixed is an special typehint which explicitly means any type.
An interface stringable was also introduced to identify objects that may be turned into a string.
Both the above typehints are to be used in conjunction with other types : they can't be used alone.
In PHP 7.0, both those values could not be used as a class or interface name, to avoid confusion with the actual booleans, nor null value.
<?php
// function accepts an A object, or null.
function foo(A|null $x) {}
// same as above
function foo2(A|null $x) {}
// returns an object of class B, or false
function bar($x) : false|B {}
?>
See also PHP RFC: Union Types 2.0.
Union Typehint
[Since 2.0.9] - [ -P Php/Php80UnionTypehint ] - [ Online docs ]
Union typehints allows the specification of several typehint for the same argument or return value.
Several typehints are specified at the same place as a single one. The different values are separated by a pipe character | , like for exceptions
Nullable is reported as union type. Mixed and iterable are not reported as a union type.
Union types are a PHP 8.0 new feature. They are not compatible with PHP 7.4 and older.
<?php
// Example from the RFC https://wiki.php.net/rfc/union_types_v2
class Number {
private int|float $number;
public function setNumber(int|float $number): void {
$this->number = $number;
}
public function getNumber(): int|float {
return $this->number;
}
}
?>
See also PHP RFC: Union Types 2.0, PHP 8 Union types and Type declarations.
Wrong Typed Property Default
[Since 2.0.9] - [ -P Classes/WrongTypedPropertyInit ] - [ Online docs ]
Property is typed, yet receives an incompatible value at constructor time.
Initialized type might be a new instance, the return of a method call or an interface compatible object.
PHP compiles such code, but won't execute it, as it detects the incompatibility at execution time.
<?php
class x {
private A $property;
private B $incompatible;
function __construct() {
// This is compatible
$this->property = new A();
// This is incompatible : new B() expected
$this->incompatible = new C();
}
}
?>
Alternatives :
- Remove the type hint of the property
- Fix the initialization call
- Use an interface for typehint
[Since 2.1.0] - [ -P Classes/HiddenNullable ] - [ Online docs ]
Argument with default value of null are nullable. It works both with the null typehint (PHP 8.0), or the ? operator are not used, setting the default value to null is allowed, and makes the argument nullable.
This works with single types, both classes and scalars; it works with union types but not with intersection types.
This doesn't happen with properties : they must be defined with the nullable type to accept a null value as default value.
This doesn't happen with constant, whose value must be explicitely defined.
In PHP 8.4, the implicit nullable type are deprecated. They might be removed in PHP 9.0.
<?php
// explicit nullable parameter $s
function bar(?string $s = null) {
// implicit nullable parameter $s
function foo(string $s = null) {
echo $s ?? 'NULL-value';
}
// both display NULL-value
foo();
foo(null);
?>
Alternatives :
- Change the default value to a compatible literal : for example, string $s = ''
- Add the explicit ? nullable operator, or null with PHP 8.0
- Remove the default value
Fn Argument Variable Confusion
[Since 2.1.0] - [ -P Functions/FnArgumentVariableConfusion ] - [ Online docs ]
Avoid using local variables as arrow function arguments.
When a local variable name is used as an argument's name in an arrow function, the local variable from the original scope is not imported. They are now two distinct variables.
When the local variable is not listed as argument, it is then imported in the arrow function.
<?php
function foo() {
$locale = 1;
// Actually ignores the argument, and returns the local variable $locale .
$fn2 = fn ($argument) => $locale;
// Seems similar to above, but returns the incoming argument
$fn2 = fn ($locale) => $locale;
}
?>
Alternatives :
- Change the name of the local variable
- Change the name of the argument
Missing Abstract Method
[Since 2.1.0] - [ -P Classes/MissingAbstractMethod ] - [ Online docs ]
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() ;
}
?>
Alternatives :
- Implement the missing methods
- Remove the partially implemented class
- Mark the partially implemented class abstract
Undefined Constant Name
[Since 2.1.1] - [ -P Variables/UndefinedConstantName ] - [ Online docs ]
When using the syntax for variable, the name used must be a defined constant. It is not a simple string, like 'x', it is an actual constant name.
Interestingly, it is possible to use a qualified name within , full or partial. PHP will lint such code, and will collect the value of the constant immediately. Since there is no fallback mechanism for fully qualified names, this ends with a Fatal error.
<?php
const x = "a";
$a = "Hello";
// Display 'Hello' -> $a -> Hello
echo ;
// Yield a PHP Warning
// Use of undefined constant y - assumed 'y' (this will throw an Error in a future version of PHP)
echo ;
// Yield a PHP Fatal error as PHP first checks that the constant exists
//Undefined constant 'y'
echo ;
?>
Alternatives :
- Define the constant
- Turn the dynamic syntax into a normal variable syntax
- Use a fully qualified name (at least one \ ) to turn this syntax into a Fatal error when the constant is not found. This doesn't fix the problem, but may make it more obvious during the diagnostic.
Using Deprecated Method
[Since 2.1.2] - [ -P Functions/UsingDeprecated ] - [ Online docs ]
A call to a deprecated method has been spotted. A method is deprecated when it bears a @deprecated parameter in its typehint definition.
Deprecated methods which are not called are not reported.
<?php
// not deprecated method
not_deprecated();
// deprecated methods
deprecated();
$object = new X();
$object->deprecatedToo();
/**
* @deprecated since version 2.0.0
*/
function deprecated() {}
// PHP 8.0 attribute for deprecation
class X {
#[ Deprecated]
function deprecatedToo() {}
}
function not_deprecated() {}
?>
Alternatives :
- Replace the deprecated call with a stable call
- Remove the deprecated attribute from the method definition
- Remove the deprecated call
Protocol lists
[Since 2.1.3] - [ -P Type/Protocols ] - [ Online docs ]
List of all protocols that were found in the code.
From the manual : PHP comes with many built-in wrappers for various URL-style protocols for use with the filesystem functions such as fopen(), copy(), file_exists() and filesize().
<?php
// Example from the PHP manual, with the glob:// wrapper
// Loop over all *.php files in ext/spl/examples/ directory
// and print the filename and its size
$it = new DirectoryIterator("glob://ext/spl/examples/*.php");
foreach($it as $f) {
printf("%s: %.1FK\n", $f->getFilename(), $f->getSize()/1024);
}
?>
See also Supported Protocols and Wrappers.
Cyclic References
[Since 2.1.3] - [ -P Classes/CyclicReferences ] - [ Online docs ]
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.
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.
<?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;
}
}
?>
Alternatives :
- Use a different object when calling the child objects.
- Refactor your code to avoid the cyclic reference.
Double Object Assignation
[Since 2.1.2] - [ -P Structures/DoubleObjectAssignation ] - [ Online docs ]
The same object is assigned to two distinct variables. Given that objects are actually references to the same data, this is usually not necessary. Make sure that this is the intended purpose.
<?php
// $x and $y are the same object, as they both hold a reference to the same object.
// This means that changing $x, also changes $y.
$x = $y = new Z();
// $a and $b are distinct values, by default
$a = $b = 1;
?>
Alternatives :
- Split the double assignation to two distinct instantiations.
- Split the double assignation to two distinct lines.
Wrong Argument Type
[Since 2.1.3] - [ -P Functions/WrongArgumentType ] - [ Online docs ]
Checks that the type of the argument is consistent with the type of the called method.
This analysis is valid with PHP 8.0.
<?php
function foo(int $a) { }
//valid call, with an integer
foo(1);
//invalid call, with a string
foo('asd');
?>
Alternatives :
- Always use a valid type when calling methods.
Mismatch Properties Typehints
[Since 2.1.4] - [ -P Classes/MismatchProperties ] - [ Online docs ]
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.
This code will lint, but not execute.
<?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;
}
?>
Alternatives :
- 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)
No Need For Triple Equal
[Since 2.1.4] - [ -P Structures/NoNeedForTriple ] - [ Online docs ]
There is no need for the identity comparison when the methods returns the proper type.
<?php
// foo() returns a string.
if ('a' === foo()) {
// doSomething()
}
function foo() : string {
return 'a';
}
?>
Array_merge Needs Array Of Arrays
[Since 2.1.4] - [ -P Structures/ArrayMergeArrayArray ] - [ Online docs ]
When collecting data to feed array_merge(), use an array of array as default value. array(array()) is the neutral value for array_merge();
This analysis also reports when the used types are not an array : array_merge() does not accept scalar values, but only arrays.
Since PHP 7.4, it is possible to call array_merge() without an argument : this means the default value may an empty array.
<?php
// safe default value
$a = array(array());
// when $list is empty, this will trigger an error during array_merge()
foreach($list as $l) {
$a[] = $l;
}
$b = array_merge(...$a);
?>
Alternatives :
- Use array(array()) or [[]] as default value for array_merge()
- Remove any non-array value from the values in the default array
Wrong Type For Native PHP Function
[Since 2.1.5] - [ -P Php/WrongTypeForNativeFunction ] - [ Online docs ]
This rule reports calls to a PHP native function with a wrongly typed value.
<?php
// valid calls
echo exp(1);
echo exp(2.5);
// invalid calls
echo exp("1");
echo exp(array(2.5));
// valid call, but invalid math
// -1 is not a valid value for log(), but -1 is a valid type (int) : it is not reported by this analysis.
echo log(-1);
?>
Alternatives :
- Set the code to the valid type, when calling a PHP native function
Catch With Undefined Variable
[Since 2.1.5] - [ -P Exceptions/CatchUndefinedVariable ] - [ Online docs ]
Always initialize every variable before the try block, when they are used in a catch block. If the exception is raised before the variable is defined, the catch block may have to handle an undefined variable, leading to more chaos.
<?php
$a = 1;
try {
mayThrowAnException();
$b = 2;
} catch (\Exception $e) {
// $a is already defined, as it was done before the try block
// $b may not be defined, as it was initialized after the exception-throwing expression
echo $a + $b;
}
?>
Alternatives :
- Always define the variable used in the catch clause, before the try block.
Swapped Arguments
[Since 2.1.5] - [ -P Classes/SwappedArguments ] - [ Online docs ]
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.
This analysis reports argument lists that differs in ordering. This analysis doesn't report argument lists that also differs in argument names.
<?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) {}
}
?>
Alternatives :
- Make sure the names of the argument are in the same order in all classes and interfaces
Different Argument Counts
[Since 2.1.6] - [ -P Classes/DifferentArgumentCounts ] - [ Online docs ]
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) {}
}
?>
Alternatives :
- Extract the extra arguments into other methods
- Remove the extra arguments
- Add the extra arguments to all the signatures
Use PHP Attributes
[Since 2.1.6] - [ -P Php/UseAttributes ] - [ Online docs ]
This rule reports if PHP 8.0 attributes are in use.
Attributes look like special comments #[...] . They are linked to classes, traits, interfaces, enums, class constants, functions, methods, and parameters.
<?php
#[foo(4)]
class x {
}
?>
See also PHP RFC: Shorter Attribute Syntax, Attributes Amendements and Shorter Attribute Syntax Change.
Use NullSafe Operator
[Since 2.1.6] - [ -P Php/UseNullSafeOperator ] - [ Online docs ]
The nullsafe operator ?-> is an alternative to the object operator -> . It silently fails the whole expression if a null is used for object.
<?php
$o = null;
// PHP 8.0 Failsafe : $r = null;
$r = $o->method();
// PHP 7.4- : Call to a member function method() on null
$r = $o->method();
?>
See also PHP RFC: Nullsafe operator.
Use Closure Trailing Comma
[Since 2.1.6] - [ -P Php/UseTrailingUseComma ] - [ Online docs ]
Use a trailing comma in the closure's use list.
A trailing comma doesn't add any argument, not even a void or null one. It is a convenient for VCS to make diff with the previous code, and have them more readable.
This feature was added in PHP 8.0.
<?php
// PHP 8.0 valid syntax
$f = function foo() use ($a, ) { };
// always valid syntax for closure
$f = function foo() use ($a ) { };
?>
Alternatives :
- Add a trailing comma when there are more than one argument in the use expression
Unknown Parameter Name
[Since 2.1.6] - [ -P Functions/UnknownParameterName ] - [ Online docs ]
The name of the parameter doesn't belong to the method signature. Named arguments were introduced in PHP 8.0.
Named arguments errors will also arise when spreading a hash array with arbitrary number of arguments. For example, with array_merge(), the array should not use named keys.
<?php
// All good
foo(a:1, b:2, c:3);
foo(...['a':1, 'b':2, 'c':3]);
// A is not a parameter name, it should be a : names are case sensitive
foo(A:1, b:2, c:3);
foo(...['A':1, 'b':2, 'c':3]);
function foo($a, $b, $c) {}
array_merge(['a' => [1], 'b' => [2]]);
?>
Alternatives :
- Fix the name of the parameter and use a valid one
- Remove the parameter name, and revert to positional notation
Missing Some Returntype
[Since 2.1.7] - [ -P Typehints/MissingReturntype ] - [ Online docs ]
The specified typehints are not compatible with the returned values.
The code of the method may return other types, which are not specified and will lead to a PHP fatal error. It is the case for insufficient typehints, when a typehint is missing, or inconsistent typehints, when the method returns varied types.
The analysis reports a method when it finds other return types than the one expected. In the case of multiple typehints, as for the last example, the PHP code may require an upgrade to PHP 8.0.
<?php
// correct return typehint
function fooSN() : ?string {
return shell_exec('ls -hla');
}
// insufficient return typehint
// shell_exec() may return null or string. Here, only string in specified for fooS, and that may lead to a Fatal error
function fooS() : string {
return shell_exec('ls -hla');
}
// inconsistent return typehint
function bar() : int {
return rand(0, 10) ? 1 : "b";
}
?>
Alternatives :
- Update the typehint to accept more types
- Update the code of the method to fit the expected returntype
Don't Pollute Global Space
[Since 2.1.7] - [ -P Php/DontPolluteGlobalSpace ] - [ Online docs ]
Avoid creating definitions in the global name space.
The global namespace is the default namespace, where all functions, classes, constants, traits and interfaces live. The global namespace is also known as the root namespace.
In particular, PHP native classes usually live in that namespace. By creating functions in that namespace, the code may encounter naming conflict, when the PHP group decides to use a name that the code also uses. This already happened in PHP version 5.1.1, where a Date native class was introduced, and had to be disabled in the following minor version.
Nowadays, conflicts appear between components, which claim the same name.
<?php
// This is not polluting the global namespace
namespace My/Namespace {
class X {}
}
// This is polluting the global namespace
// It might be in conflict with PHP classes in the future
namespace {
class X {}
}
?>
Alternatives :
- Create a namespace for your code, and store your definition there.
Cast Unset Usage
[Since 2.1.8] - [ -P Php/CastUnsetUsage ] - [ Online docs ]
Usage of the `(unset)` cast operator was removed. The operator was deprecated since PHP 7.2.0.
<?php
$a = 1;
(unset) $a;
// functioncall is OK
unset($a);
?>
Alternatives :
- Replace (unset)` with a call to `unset().
- Remove the unset call altogether.
- Set the value to NULL.
$php_errormsg Usage
[Since 2.1.8] - [ -P Php/PhpErrorMsgUsage ] - [ Online docs ]
$php_errormsg is removed since PHP 8.0. $php_errormsg tracks the last error message, with the directive track_errors`. All was removed in PHP 8.0, and shall be replaced with `error_get_last().
<?php
function foo() {
global $php_errormsg;
echo 'Last error: '.$php_errormsg;
echo 'Also, last error: '.error_get_last();
}
?>
Alternatives :
- Use error_get_last() instead.
Mismatch Parameter Name
[Since 2.1.8] - [ -P Functions/MismatchParameterName ] - [ Online docs ]
Parameter name change in overwritten method. This may lead to errors when using PHP 8.0 named arguments.
PHP use the name of the parameter in the method whose code is executed. When the name change between the method and the overwritten method, the consistency is broken.
Here is another example, in early PHP 8.0 (courtesy of Carnage).
<?php
class x {
function getValue($name) {}
}
class y extends x {
// consistent with the method above
function getValue($name) {}
}
class z extends x {
// inconsistent with the method above
function getValue($label) {}
}
?>
Alternatives :
- Make sure all the names are the same, between methods
Multiple Declaration Of Strict_types
[Since 2.1.8] - [ -P Php/MultipleDeclareStrict ] - [ Online docs ]
At least two declare() commands are declaring `strict_types` in one file. Only one is sufficient, and should be the first expression in the file.
Indeed, any `strict_types` set to 1 will have the final word. Setting `strict_types` to 0 will not revert the configuration, wherever is this call made.
<?php
declare(strict_types=1);
declare(strict_types=1);
// rest of the code
?>
Alternatives :
- Remove all but one of them. Keep the first one.
Array_Fill() With Objects
[Since 2.1.9] - [ -P Structures/ArrayFillWithObjects ] - [ Online docs ]
array_fill() fills an array with identical objects, not copies nor clones. This means that all the filled objects are a reference to the same object. Changing one of them will change any of them.
Make sure this is the intended effect in the code.
This applies to array_pad() too. It doesn't apply to array_fill_keys(), as objects will be cast to a string before usage in this case.
<?php
$x = new StdClass();
$array = array_fill(0, 10, $x);
$array[3]->y = "Set in object #3";
// displays "Set in object #3"
echo $array[5]->y;
?>
Alternatives :
- Use a loop to fill in the array with cloned() objects.
Modified Typed Parameter
[Since 2.1.9] - [ -P Functions/ModifyTypedParameter ] - [ Online docs ]
Reports modified parameters, which have a non-scalar typehint. Such variables should not be changed within the body of the method. Unlike typed properties, which always hold the expected type, typed parameters are only guaranteed type at the beginning of the method block.
This problem doesn't apply to scalar types : by default, PHP pass scalar parameters by value, not by reference. Class types are always passed by reference.
This problem is similar to Classes/DontUnsetProperties : the static specification of the property may be unset, leading to confusing 'undefined property', while the class hold the property definition.
<?php
class x {
function foo(Y $y) {
// $y is type Y
// A cast version of $y is stored into $yAsString. $y is untouched.
$yAsString = (string) $y;
// $y is of type 'int', now.
$y = 1;
// Some more code
// display the string version.
echo $yAsString;
// so, Y $y is now raising an error
echo $y->name;
}
}
?>
Alternatives :
- Use different variable names when converting a parameter to a different type.
- Only use methods and properties calls on a typed parameter.
Assumptions
[Since 2.1.9] - [ -P Php/Assumptions ] - [ Online docs ]
Assumptions in the code, that leads to possible bugs.
Some conditions may be very weak, and lead to errors. For example, the code below checks that the variable `$a` is not null, then uses it as an array. There is no relationship between 'not null' and 'being an array', so this is an assumption.
<?php
// Assumption : if $a is not null, then it is an array. This is not always the case.
function foo($a) {
if ($a !== null) {
echo $a['name'];
}
}
// Assumption : if $a is not null, then it is an array. Here, the typehint will ensure that it is the case.
// Although, a more readable test is is_array()
function foo(?array $a) {
if ($a !== null) {
echo $a['name'];
}
}
?>
Alternatives :
- Make the context of the code more explicit
- Use a class to handle specific array index
- Avoid using named index by using foreach()
PHP 8.0 Removed Directives
[Since 2.1.9] - [ -P Php/Php80RemovedDirective ] - [ Online docs ]
List of directives that are removed in PHP 8.0.
In PHP 8.0, `track_errors` was removed.
You can detect valid directives with ini_get(). This native function will return false, when the directive doesn't exist, while actual directive values will be returned as a string.
See `Deprecation track_errors
Alternatives :
<?php
var_dump(ini_get('track_errors'));
?>
Unsupported Types With Operators
[Since 2.1.9] - [ -P Structures/UnsupportedTypesWithOperators ] - [ Online docs ]
Arrays, resources and objects are generally not accepted with unary and binary operators.
The operators are `+`, `-`, `*`, `/`, `**`, `%`, `<<`, `>>`, `&`, `|`, `^`, `~`, `++` and `--`.
In PHP 8.0, the rules have been made stricter and more consistent.
The only valid operator is `+`, combined with arrays in both operands. Other situations throw TypeError .
<?php
var_dump([] % [42]);
// int(0) in PHP 7.x
// TypeError in PHP 8.0 +
// Also impossible usage : index are string or int
$a = [];
$b = $c[$a];
?>
Alternatives :
- Do not use those values with those operators
- Use a condition to skip this awkward situation
- Add an extra step to turn this value into a valid type
Negative Start Index In Array
[Since 2.1.9] - [ -P Arrays/NegativeStart ] - [ Online docs ]
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
)
*/
?>
Alternatives :
- 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.
Nullable With Constant
[Since 2.1.9] - [ -P Functions/NullableWithConstant ] - [ Online docs ]
Arguments are automatically nullable with a literal null. They used to also be nullable with a constant null, before PHP 8.0.
<?php
// Extracted from https://github.com/php/php-src/blob/master/UPGRADING
// Replace
function test(int $arg = CONST_RESOLVING_TO_NULL) {}
// With
function test(?int $arg = CONST_RESOLVING_TO_NULL) {}
// Or
function test(int $arg = null) {}
?>
Alternatives :
- Use the valid syntax
PHP 8.0 Resources Turned Into Objects
[Since 2.2.0] - [ -P Php/Php80RemovesResources ] - [ Online docs ]
Multiple PHP native functions now return objects, not resources. Any check on those values with is_resource() is now going to fail. The affected functions are the following :
- curl_init()
- curl_multi_init()
- curl_share_init()
- deflate_init()
- enchant_broker_init()
- enchant_broker_request_dict()
- enchant_broker_request_pwl_dict()
- inflate_init()
- msg_get_queue()
- openssl_csr_new()
- openssl_csr_sign()
- openssl_pkey_new()
- openssl_x509_read()
- sem_get()
- shm_attach()
- shmop_open()
- socket_accept()
- socket_addrinfo_bind()
- socket_addrinfo_connect()
- socket_create_listen()
- socket_create()
- socket_import_stream()
- socket_wsaprotocol_info_import()
- xml_parser_create_ns()
- xml_parser_create()
- Change the condition from is_resource() to instanceof
PHP 80 Named Parameter Variadic
[Since 2.2.0] - [ -P Php/Php80NamedParameterVariadic ] - [ Online docs ]
Named parameter with variadic have been renamed from 0 to 'parameter name' in PHP 8.0.
In PHP 7.0, with positional argument only, the content of $b is in an array, index 0. This is also true with PHP 8.0.
In PHP 8.0, with named arguments, the content of $b is in an array, index 'b';
Since the behavior of the variadic depends on the calling syntax (with or without named parameter), the receiving must ensure the correct reception, and handle both cases.
<?php
function foo($a, ...$b) {
print_r($b);
}
foo(3, 4);
foo(3, b: 4); // PHP 8 only
foo(...[2, "b"=> [3, 4]]); // PHP 8 only
?>
Alternatives :
- Apply array_values() to the variadic arguments.
Wrong Attribute Configuration
[Since 2.2.0] - [ -P Php/WrongAttributeConfiguration ] - [ Online docs ]
A class is attributed to the wrong PHP structure. A class may be an attribute, and it may also be configured to be used with different structures : classes, function, parameters, etc. When an attribute has a configuration, it must be used with the correct structure.
<?php
#[Attribute(Attribute::TARGET_CLASS)]
class ClassAttribute { }
// Wrong
#[ClassAttribute]
function foo () {}
// OK
#[ClassAttribute]
class y {}
?>
Alternatives :
- Remove the attribute from the wrongly attributed structure
- Extend the configuration of the attribute with Attribute::TARGET_*
Cancelled Parameter
[Since 2.2.0] - [ -P Functions/CancelledParameter ] - [ Online docs ]
A parameter is cancelled, when its value is hardcoded, and cannot be changed by the calling expression. The argument is in the signature, but it is later hardcoded to a literal value : thus, it is not usable, from the caller point of view.
Reference argument are omitted in this rule, as their value changes, however hardcoded, may have an impact on the calling code.
<?php
function foo($a, $b) {
// $b is cancelled, and cannot be changed.
$b = 3;
// $a is the only parameter here
return $a + $b;
}
function bar($a, $b) {
// $b is actually processed
$c = $b;
$c = process($c);
$b = $c;
// $a is the only parameter here
return $a + $b;
}
?>
Alternatives :
- Remove the parameter in the method signature
Constant Typo Looks Like A Variable
[Since 2.2.0] - [ -P Variables/ConstantTypo ] - [ Online docs ]
A constant bears the same name as a variable. This might be a typo.
When the constant doesn't exist, PHP 8.0 yields a Fatal Error and stops execution. PHP 7.4 turns the undefined constant into its string equivalent.
This analysis is case sensitive.
<?php
// Get an object or null
$object = foo();
// PHP 8.0 stops here, with a Fatal Error
// PHP 7.4 makes this a string, and the condition is always true
if (!empty(object)) {
// In PHP 7.4, this is not protected by the condition, and may yield an error.
$object->doSomething();
}
?>
Alternatives :
- Add a $ sign to the constant
- Use a different name for the variable, or the constant
Final Private Methods
[Since 2.2.0] - [ -P Classes/FinalPrivate ] - [ Online docs ]
PHP's private methods cannot be overwritten, as they are dedicated to the current class. That way, the final 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() {}
}
?>
Alternatives :
- Remove the final keyword
- Relax visibility
Array_Map() Passes By Value
[Since 2.2.0] - [ -P Structures/ArrayMapPassesByValue ] - [ Online docs ]
array_map() requires the callback to receive elements by value. Unlike array_walk(), which accepts by value or by reference, depending on the action taken.
PHP 8.0 and more recent emits a Warning
<?php
// Example, courtery of Juliette Reinders Folmer
function trimNewlines(&$line, $key) {
$line = str_replace(array("\n", "\r" ), '', $line);
}
$original = [
"text\n\n",
"text\n\r"
];
$array = $original;
array_walk($array, 'trimNewlines');
var_dump($array);
array_map('trimNewlines', $original, [0, 1]);
?>
Alternatives :
- Make the callback first argument a reference
Missing __isset() Method
[Since 2.2.0] - [ -P Php/MissingMagicIsset ] - [ Online docs ]
When using empty() on magic properties, the magic method __isset() must be implemented.
<?php
class foo {
function __get($name) { return 'foo'; }
// No __isset method
}
// Return TRUE, until __isset() exists
var_dump(
empty((new foo)->bar);
);
?>
Alternatives :
- Implement __isset() method when using empty on magic properties
Modify Immutable
[Since 2.2.0] - [ -P Attributes/ModifyImmutable ] - [ Online docs ]
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
?>
Alternatives :
- Removed the modification
- Clone the immutable object
Reserved Match Keyword
[Since 2.2.1] - [ -P Php/ReservedMatchKeyword ] - [ Online docs ]
match is a new instruction in PHP 8.0.
For that, it becomes a reserved keyword, and cannot be used in various situations: type, class, function, global constant name.
<?php
// Match as a standalone keyword is not possible
use X as Match;
// No more use as a type
function foo(match $a ) : match {}
$a instanceof match;
// No use as method name
match(a, 4) ;
// Match in a Fully qualified name is OK
b\match ;
// Match as a property name or a class constant is OK
$match->match;
C::MATCH;
// Match as a method is OK
$method->match();
$static::match();
?>
Alternatives :
- Change the name from Match to something else.
Avoid get_object_vars()
[Since 2.2.1] - [ -P Php/AvoidGetobjectVars ] - [ Online docs ]
get_object_vars() changes behavior between PHP 7.3 and 7.4. It also behaves different within and outside a class.
<?php
// Illustration courtesy of Doug Bierer
$obj = new ArrayObject(['A' => 1,'B' => 2,'C' => 3]);
var_dump($obj->getArrayCopy());
var_dump(get_object_vars($obj));
?>
Alternatives :
- Use ArrayObject and getArrayCopy() method
Cannot Use Static For Closure
[Since 2.2.2] - [ -P Functions/CannotUseStaticForClosure ] - [ Online docs ]
The reported closures and arrow functions cannot use the static keyword.
Closures that makes use of the $this pseudo-variable cannot use the static keyword, at it prevents the import of the $this context in the closure. It will fail at execution.
Closures that makes use of the bindTo() method, to change the context of execution, also cannot use the static keyword. Even if $this is not used in the closure, the static keyword prevents the call to bindTo().
<?php
class x {
function foo() {
// Not possible, $this is now undefined in the body of the closure
static function () { return $this->a;};
}
function foo2() {
// Not possible, $this is now undefined in the body of the arrow function
static fn () => $this->a;
}
function foo3() {
// Not possible, the closure gets a new context before being called.
$a = static fn () => $ba;
$this->foo4($a);
}
function foo4($c) {
$c->bindTo($this);
$c();
}
}
?>
Alternatives :
- Remove the static keyword
- Remove the call to bindTo() method
- Remove the usage of the $this variable
Only First Byte
[Since 2.2.2] - [ -P Structures/OnlyFirstByte ] - [ Online docs ]
When assigning a char to a string with an array notation, only the first byte is used.
<?php
$str = 'xy';
// first letter is now a
$str[0] = 'a';
// second letter is now b, c is ignored
$str[1] = 'bc';
?>
Alternatives :
- Remove extra bytes when assigning to a string
- Use concatenation
- Use strpos() and substr() functions
- Use explode(), implode() functions and array manipulations
Restrict Global Usage
[Since 2.2.2] - [ -P Php/RestrictGlobalUsage ] - [ Online docs ]
$GLOBALS access, as whole, is forbidden. In PHP 8.1, it is not possible to this as a variable, but only access its individual values.
<?php
// Example extracted from the RFC (see link below)
// Continues to work:
foreach ($GLOBALS as $var => $value) {
echo $var . ' => ' . $value . PHP_EOL;
}
// Generates compile-time error:
$GLOBALS = [];
$GLOBALS += [];
$GLOBALS =& $x;
$x =& $GLOBALS;
unset($GLOBALS);
?>
Alternatives :
- Copy values individually from $GLOBALS
Inherited Property Type Must Match
[Since 2.2.2] - [ -P Classes/InheritedPropertyMustMatch ] - [ Online docs ]
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.
}
?>
Alternatives :
- Remove the definition in the child class
- Synchronize the definition of the property in the child class
No Object As Index
[Since 2.2.2] - [ -P Structures/NoObjectAsIndex ] - [ Online docs ]
PHP accepts objects as index, though it will report various error messages when this happens.
Thanks to George Peter Banyard for the inspiration.
<?php
$s = 'Hello';
$o = new stdClass();
try {
$s[$o] = 'A';
} catch (\Throwable $e) {
echo $e->getMessage(), "\n";
//Cannot access offset of type stdClass on string
}
?>
Alternatives :
- Filter values being used as index
- Filter values being used as array
Class Overreach
[Since 2.2.2] - [ -P Classes/ClassOverreach ] - [ Online docs ]
An object of class A may reach any private or protected properties, constants or methods in another object of the same class. This is a PHP feature, though seldom known.
This feature is also called class invasion.
<?php
class A {
private $p = 1;
public function foo(A $a) {
return $a->p + 1;
}
}
echo (new A)->foo(new A);
?>
Alternatives :
- Use a getter to reach inside the other object private properties
Inherited Static Variable
[Since 2.2.2] - [ -P Variables/InheritedStaticVariable ] - [ Online docs ]
Static variables are distinct when used in an inherited static method. In PHP 8.1, the static variable will also be inherited, and shared between the two methods, like a static property.
<?php
// Code extracted from the RFC
class A {
public static function counter() {
static $i = 0;
return ++$i;
}
}
class B extends A {}
var_dump(A::counter()); // int(1)
var_dump(A::counter()); // int(2)
var_dump(B::counter()); // int(1)
var_dump(B::counter()); // int(2)
?>
Alternatives :
- Define the method in the child class to enforce the distinct behavior
- Replace the static variable by a static property to make this PHP 8.1 ready
Enum Usage
[Since 2.2.2] - [ -P Php/EnumUsage ] - [ Online docs ]
This rule reports usage of PHP native enumerations. Enumerations were introduced in PHP 8.1 and are not backward compatible.
<?php
enum X {
case A;
case B;
}
foo(X::A);
?>
See also Enumerations in PHP.
PHP 8.1 Removed Directives
[Since 2.2.3] - [ -P Php/Php81RemovedDirective ] - [ Online docs ]
List of directives that are removed in PHP 8.1. In PHP 8.1, the following directives were removed : * mysqlnd.fetch_data_copy` * `filter.default` * `filter.default_options` * `auto_detect_line_endings` * `oci8.old_oci_close_semantics` You can detect valid directives with `ini_get(). This native function will return false, when the directive doesn't exist, while actual directive values will be returned as a string.
- Remove usage of the directives.
Htmlentities Using Default Flag
[Since 2.2.3] - [ -P Structures/HtmlentitiescallDefaultFlag ] - [ Online docs ]
htmlspecialchars(), htmlentities(), htmlspecialchars_decode(), html_entity_decode() and get_html_translation_table(), are used to prevent injecting special characters in HTML code. As a bare minimum, they take a string and encode it for HTML.
The second argument of the functions is the type of protection. The protection may apply to quotes or not, to HTML 4 or 5, etc. It is highly recommended to set it explicitly.
In PHP 8.1, the default value of this parameter has changed. It used to be ENT_COMPAT and is now ENT_QUOTES | ENT_SUBSTITUTE . The main difference between the different configuration is that the single quote, which was left intact so far, is now protected HTML style.
<?php
$str = 'A quote in <b>bold</b> : \' and ""';
// PHP 8.0 outputs, without depending on the php.ini: A quote in <b>bold</b> : ' and "
echo htmlentities($str);
// PHP 8.1 outputs, while depending on the php.ini: A quote in <b>bold</b> : ' and "
echo htmlentities($str);
?>
Alternatives :
- Always use the second argument to explicitly set the desired protection
Openssl Encrypt Default Algorithm Change
[Since 2.2.3] - [ -P Php/OpensslEncryptAlgoChange ] - [ Online docs ]
openssl_pkcs7_encrypt() and openssl_cms_encrypt() will now default to using AES-128-CBC rather than RC2-40. The RC2-40 cipher is considered insecure and not enabled by default in OpenSSL 3.
This means that the default argument of OPENSSL_CIPHER_RC2_40 is replaced by OPENSSL_CIPHER_AES_128_CBC.
<?php
// extracted from the PHP documentation
// encrypt it
if (openssl_pkcs7_encrypt("msg.txt", "enc.txt", $key,
array("To" => "nighthawk@example.com", // keyed syntax
"From: HQ <hq@example.com>", // indexed syntax
"Subject" => "Eyes only"))) {
// message encrypted - send it!
exec(ini_get("sendmail_path") . " < enc.txt");
}
?>
Alternatives :
- Explicitly set the 5th and 6th argument of the functioncalls to avoid a disruption.
- Update the target service to handle the new cipher algorithm.
PHP 8.1 Removed Constants
[Since 1.6.8] - [ -P Php/Php81RemovedConstant ] - [ Online docs ]
The following PHP native constants were disabled in PHP 8.1. They are not removed, but they have no more effect.
- MYSQLI_STMT_ATTR_UPDATE_MAX_LENGTH
- MYSQLI_STORE_RESULT_COPY_DATA
- FILE_BINARY
- FILE_TEXT
- FILTER_SANITIZE_STRING
- Remove usage of those constants
Wrong Argument Name With PHP Function
[Since 2.2.3] - [ -P Functions/WrongArgumentNameWithPhpFunction ] - [ Online docs ]
The name of the argument provided is not a valid parameter name for that PHP native function or method.
This analysis may be configured with extra PHP extensions or external packages.
<?php
// those are the valid names
strcmp(string1: 'a', string2: 'b');
// those are not the valid names
strcmp(string: 'a', stringToo: 'b');
?>
Alternatives :
- Use the correct parameter name
- Remove all the parameter names from the call
- Create a relay function with the correct parameter names
Duplicate Named Parameter
[Since 2.2.3] - [ -P Functions/DuplicateNamedParameter ] - [ Online docs ]
Two parameters have the same name in a method call. This will yield a Fatal error at execution time.
<?php
function foo($a, $b) {}
// parameters are all distinct
foo(a:1, b:2);
// parameter a is double
foo(a:1, a:1);
?>
Alternatives :
- Review the parameters names and remove the duplicates
- Review the parameters names and makes the names unique
PHP Native Class Type Compatibility
[Since 2.2.4] - [ -P Php/NativeClassTypeCompatibility ] - [ Online docs ]
PHP enforces the method compatibility with native classes and interfaces.
This means that classes that extends native PHP classes or interfaces must declare compatible types. They can't omit typing, like it was the case until PHP 8.0.
This is needed for compatibility with PHP 8.0. This is probably good for older versions too, although it is not reported.
The attribute ReturnTypeWillChange is taken into account by this rule. Note that it is not detected when auditing with PHP < 8.0, so it won't have effect until this version. The attribute was declared in PHP 8.1, though it is also taken into account when auditing with PHP 8.0.
<?php
class a extends RecursiveFilterIterator {
// fully declared method
function hasChildren(): bool {
return true;
}
// key() returns mixed. Omitting the type used to be quiet
function key() {}
// #[\ReturnTypeWillChange] is taken into account
}
?>
Alternatives :
- Make sure the methods are compatible or identical to the parent's method signature.
Missing Attribute Attribute
[Since 2.2.4] - [ -P Attributes/MissingAttributeAttribute ] - [ Online docs ]
A class that servers as attribute, should have the attribute #[Attribute] .
While not strictly required, it is still recommended to create an actual class for every attribute.
<?php
namespace Example;
use Attribute;
#[Attribute]
class MyAttribute
{
}
#Missing the above attribute
class MyOtherAttribute
{
}
?>
Alternatives :
- Add the Attribute attribute to those classes
No Null For Native PHP Functions
[Since 2.2.5] - [ -P Php/NoNullForNative ] - [ Online docs ]
Null is not acceptable anymore as an argument, for PHP native functions that require a non-nullable argument.
Until PHP 8.1, it was magically turned into an empty string.
<?php
$haystack = 'abc';
// $needle was omitted...
echo strpos($haystack, $needle);
?>
See also PHP RFC: Deprecate passing null to non-nullable arguments of internal functions.
Calling Static Trait Method
[Since 2.2.5] - [ -P Php/CallingStaticTraitMethod ] - [ Online docs ]
Calling directly a static method, defined in a trait is deprecated. It emits a deprecation notice in PHP 8.1.
Calling the same method, from the class point of view is valid.
<?php
trait T {
public static function t() {
//
}
}
T::t();
?>
Alternatives :
- Call the method from one of the class using the trait
- Move the method to a class
No Referenced Void
[Since 2.2.4] - [ -P Functions/NoReferencedVoid ] - [ Online docs ]
There is no point returning a reference with a void type. This is now reported as deprecated in PHP 8.1.
<?php
function &test(): void {}
?>
Alternatives :
- Removes the reference operator from the function definition
PHP Native Interfaces and Return Type
[Since 2.3.0] - [ -P Php/JsonSerializeReturnType ] - [ Online docs ]
Native PHP interface which define a type, expect the derived methods to use the same time. In particular, a mixed return type was added to the jsonSerialize() of the JsonSerialize PHP interface.
In PHP 8.1, the mixed return type is now enforced, and a deprecated notice is displayed.
One solution is to add the good return type, or to use the `#[ReturnTypeWillChange]` attribute.
This rule covers the following interfaces :
- ArrayAccess
- Countable
- Exception
- FilterIterator
- Iterator
- JsonSerializable
- php_user_filter
- SessionHandlerInterface
<?php
class MyJsonSerialize implements jsonserialize {
function jsonserialize() : int {}
}
?>
Alternatives :
- Add the mixed returntype to all implementation of the jsonSerialize method
- Add the #[\ReturnTypeWillChange] attribute to the method
Final Constant
[Since 2.3.0] - [ -P Php/FinalConstant ] - [ Online docs ]
This rule lists the usage of the final modifier for class constants. The support of final for class constants was added in PHP 8.1.
<?php
class MyClass {
final const X = 1; // This constant cannot be redefined
const Y = 2; // This constant might be redefined in a child
private const Z = 3; // This private, and can't be made final, because it is not available anyway
}
?>
See also https://www.php.net/manual/en/language.oop5.final.php and https://php.watch/versions/8.1/final-class-const.
Never Typehint Usage
[Since 2.3.0] - [ -P Php/NeverTypehintUsage ] - [ Online docs ]
Never is a typehint, which characterize methods that never return a value. It will either terminate the execution or throw an exception.
<?php
function redirect(string $url): never {
header('Location: ' . $url);
exit();
}
?>
See also The “never” Return Type for PHP.
PHP 8.1 Typehints
[Since 2.3.0] - [ -P Php/PHP81scalartypehints ] - [ Online docs ]
A new scalar typehint was introduced : never.
It can't be used before PHP 8.1, and will be confused with classes or interfaces.
<?php
function test() : never
{
exit();
}
?>
See also PHP RFC: noreturn type.
Named Parameter Usage
[Since 2.3.0] - [ -P Php/NamedParameterUsage ] - [ Online docs ]
Named parameters is a way to call a method, by specifying the name of the argument, instead of their position order.
Named parameters works for both custom methods and PHP native functions.
<?php
// named parameters
foo(a : 1, b : 2);
foo(b : 2, a : 1);
// positional parameters
foo(1, 2);
function foo($a, $b) { }
?>
First Class Callable
[Since 2.3.0] - [ -P Php/FirstClassCallable ] - [ Online docs ]
A syntax using ellipsis was introduced to make it easy to make a method into a callable.
<?php
// Using ellipsis as the only argument
$a = $object->method(...);
// Old style equivalent
$a = array($object, 'method');
// calling the closure.
$a();
?>
See also PHP RFC: First-class callable syntax.
New Functions In PHP 8.1
[Since 2.3.0] - [ -P Php/Php81NewFunctions ] - [ Online docs ]
New functions are added to new PHP version.
The following functions are now native functions in PHP 8.1. It is compulsory to rename any custom function that was created in older versions. One alternative is to move the function to a custom namespace, and update the use list at the beginning of the script.
- array_is_list()
- enum_exists()
- fsync()
- fdatasync()
- imagecreatefromavif()
- imageavif()
- mysqli_fetch_column()
- sodium_crypto_core_ristretto255_add()
- sodium_crypto_core_ristretto255_from_hash()
- sodium_crypto_core_ristretto255_is_valid_point()
- sodium_crypto_core_ristretto255_random()
- sodium_crypto_core_ristretto255_scalar_add()
- sodium_crypto_core_ristretto255_scalar_complement()
- sodium_crypto_core_ristretto255_scalar_invert()
- sodium_crypto_core_ristretto255_scalar_mul()
- sodium_crypto_core_ristretto255_scalar_negate()
- sodium_crypto_core_ristretto255_scalar_random()
- sodium_crypto_core_ristretto255_scalar_reduce()
- sodium_crypto_core_ristretto255_scalar_sub()
- sodium_crypto_core_ristretto255_sub()
- sodium_crypto_scalarmult_ristretto255()
- sodium_crypto_scalarmult_ristretto255_base()
- sodium_crypto_stream_xchacha20()
- sodium_crypto_stream_xchacha20_keygen()
- sodium_crypto_stream_xchacha20_xor()
<?php
$array = [1,2,3];
var_dump(array_is_list($array)); // true
?>
Alternatives :
- Move custom functions with the same name to a new namespace
- Change the name of any custom functions with the same name
- Add a condition to the functions definition to avoid conflict
PHP 8.1 Removed Functions
[Since 2.3.0] - [ -P Php/Php81RemovedFunctions ] - [ Online docs ]
The following PHP native functions were deprecated in PHP 8.1, and will be removed in PHP 9.0.
- image2wbmp()
- png2wbmp()
- jpeg2wbmp()
- ldap_sort()
- hebrevc()
- convert_cyr_string()
- ezmlm_hash()
- money_format()
- get_magic_quotes_gpc()
- get_magic_quotes_gpc_runtime()
- create_function()
- each()
- read_exif_data()
- gmp_random()
- fgetss()
- restore_include_path()
- gzgetss()
<?php
echo hebrevc(abc);
?>
Alternatives :
- Avoid using those functions anymore
Never Keyword
[Since 2.3.0] - [ -P Php/NeverKeyword ] - [ Online docs ]
Never becomes a PHP keyword. It is used for typing functions which never returns anything (either dies or throw an exception).
It should be avoided in namespaces, classes, traits and interfaces. Methods, constants and functions are OK.
<?php
// This is OK
function never() { }
// This is no OK
class never { }
?>
Alternatives :
- Rename the classes, traits and interfaces with a different name.
Mixed Keyword
[Since 2.3.0] - [ -P Php/MixedKeyword ] - [ Online docs ]
Never becomes a PHP keyword. It is used for typing functions which never returns anything (either dies or throw an exception).
It should be avoided in classes, traits and interfaces. Methods, anonymous classes (sic), namespaces and functions are OK.
Setting a `never` class in a namespaces doesn't make it legit.
<?php
// This is OK
function never() { }
// This is no OK
class never { }
?>
Alternatives :
- Rename the classes, traits and interfaces with a different name.
Mixed Typehint Usage
[Since 2.3.0] - [ -P Php/MixedUsage ] - [ Online docs ]
Usage of the mixed typehint.
<?php
function foo() : mixed {
switch(rand(0, 3)) {
case 0:
return false;
case 1:
return 'a';
case 2:
return [];
default:
return null;
}
}
?>
See also Type declarations.
False To Array Conversion
[Since 2.3.0] - [ -P Php/FalseToArray ] - [ Online docs ]
The auto vivification of false is deprecated. This feature is the automagical conversion of a boolean into an array, if needed.
Until PHP 8.1, this was possible. This feature is deprecated in PHP 8.1, and will be removed in PHP 9.0.
<?php
$a = false;
//valid in PHP
$a[3] = 1;
?>
Alternatives :
- Change the typehints from bool or false to array
- Validate the type returned values of an functioncall before using it
Float Conversion As Index
[Since 2.3.1] - [ -P Arrays/FloatConversionAsIndex ] - [ Online docs ]
Only integers and strings are possible types when used for array index. Until PHP 8.1, floats were converted to integer by truncation. Since PHP 8.1, a deprecated message is emitted.
The implicit conversion of float to int which leads to a loss in precision is now deprecated. This affects array keys, int type declarations in coercive mode, and operators working on integers.
<?php
$a = [];
$a[15.5]; // deprecated, as key value loses the 0.5 component
$a[15.0]; // ok, as 15.0 == 15
?>
Alternatives :
- Use the cast operator (string) to convert the floats into strings. The reverse cast is most often possible.
Cannot Call Static Trait Method Directly
[Since 2.3.1] - [ -P Traits/CannotCallTraitMethod ] - [ Online docs ]
From the migration docs : Calling a static method, or accessing a static property directly on a trait is deprecated. Static methods and properties should only be accessed on a class using the trait.
<?php
trait t { static public function t() {}}
a::t();
// OK
t::t();
//Calling static trait method t::t is deprecated, it should only be called on a class using the trait
class a {
use t;
}
?>
Alternatives :
- Use the trait in a class, and call the method from the class.
Nested Attributes
[Since 2.3.1] - [ -P Attributes/NestedAttributes ] - [ Online docs ]
Nested attribute are attribute in attributes.
Nested attributes are not available in PHP 8.0 and older. It is reported as an invalid constant expression.
<?php
// Extracted from PHP 8.1 addendum (https://www.php.net/releases/8.1/en.php#new_in_initializers)
class User
{
#[\Assert\All(
new \Assert\NotNull,
new \Assert\Length(min: 6))
]
public string $name = '';
}
?>
See also PHP RFC: New in initializers and `New initializers`.
New Initializers
[Since 2.3.1] - [ -P Php/NewInitializers ] - [ Online docs ]
Parameters, static variables and global constants may be initialized with an object.
This feature is available in PHP 8.1 and more recent. It is reported as an invalid constant expression in older PHP versions.
<?php
function foo( $a = new A) {
static $static = new B;
}
const A = new C;
?>
See also PHP RFC: New in initializers and `Nested Attributes`_.
Deprecated Callable
[Since 2.3.1] - [ -P Functions/DeprecatedCallable ] - [ Online docs ]
Callable functions that are supported by call_user_func($callable) , but not with the $callable() syntax are deprecated.
One important aspect is the loss of context : 'self::method' may be created anywhere in the code, while `self::class` can only be used inside a class, and, in that case, inside the target class.
It is recommended to update the code with any PHP version, to prepare for the future removal of the feature.
<?php
class x {
// This will fail
function foo(callable $callable) {
$callable();
}
function method() {
}
}
$x = new x;
$x->foo('self::method');
?>
Alternatives :
- Replace the keyword (such as self) by the full class name, with self::class.
- Use a variable and the $s(...) syntax.
Promoted Properties
[Since 2.3.1] - [ -P Classes/PromotedProperties ] - [ Online docs ]
Promoted properties is a way to declare the properties within the constructor, and have them assigned to the constructing value at instantiation.
<?php
class CustomerDTO
{
public function __construct(
public string $name,
public string $email,
public DateTimeImmutable $birth_date,
) {}
}
?>
See also Constructor Promotion and PHP 8: Constructor property promotion.
Overwritten Foreach Var
[Since 2.3.2] - [ -P Structures/OverwrittenForeachVar ] - [ Online docs ]
When using standard blind variable names, nested foreach may lead to overwriting the variables.
Careful coding may take advantage of that feature. Though, it tends to be error prone, and will generate bugs if the code is refactored.
<?php
foreach($array as $key => $value) {
foreach($array as $key2 => $value) {
// $value is now the one of the 2nd foreach, not the first.
}
}
?>
Alternatives :
- Change the name of one of the blind variable to use a distinct name
- Remove usage of one of the double variable
- Remove the nested foreach()
- Move the nested foreach() to a method
Checks Property Existence
[Since 2.3.3] - [ -P Classes/ChecksPropertyExistence ] - [ Online docs ]
This analysis reports checks property existence.
In PHP 8.2, non-specified properties are discouraged : they should always be defined in the class code. When this guideline is applied, properties always exists, and a call to property_exists() is now useless.
Some situations are still legit :
- When the class is stdClass , where no property is initially defined. This may be the case of JSON data, or arrays cast to objects.
- When the class uses magic methods, in particular __get(), __set() and __isset().
While the behavior is deprecated in PHP 8.2, it is recommended to review older code and remove it. It will both ensure forward compatibility and cleaner, faster local code.
<?php
class x {
private $a = 1;
function foo() {
$this->cache = $this->a + 1;
}
}
?>
Extends stdClass
[Since 2.3.2] - [ -P Classes/ExtendsStdclass ] - [ Online docs ]
Those classes extends stdClass .
Traditionally, classes are defined independently, without any native class extension.
In PHP 8.2, dynamic properties are deprecated, and yield a warning in the logs. Adding 'extends \stdClass' to classes signature removes this warning, as stdclass is the empty class, without any method, property nor constants.
<?php
class myClass extends \stdClass {
function __set($a, $b) {
$this->$a = $b;
}
}
?>
Cant Overload Constants
[Since 2.3.2] - [ -P Interfaces/CantOverloadConstants ] - [ Online docs ]
It was not possible to overload class constants within a class, when the constant was defined in an interface.
<?php
interface i {
const A = 1;
}
//This lints, but doesn't executin in PHP 8.0 and older.
class x implements i {
const A = 1;
}
?>
Alternatives :
- Avoid overloading constants
- Define the constants only in the classes
Intersection Typehint
[Since 2.3.3] - [ -P Php/Php81IntersectionTypehint ] - [ Online docs ]
Intersection typehints allows the combination of several typehint for the same argument or return value.
Several typehints are specified at the same place as a single one. The different values are separated by a ampersand character & .
Intersection types are a PHP 8.1 new feature.
<?php
class Number {
private A&B $object;
}
?>
See also PHP RFC: Pure intersection types, Type declarations and How the New Intersection Types in PHP 8.1 Give You More Flexibility.
Recycled Variables
[Since 2.3.3] - [ -P Variables/RecycledVariables ] - [ Online docs ]
A recycled variable is a variable used for two distinct missions in a method. There is usually a first part, with its own initialization, then, later in the method, a second part with a new initialization and a distinct usage of the variable.
Recycled variables leads to confusion: with the new initialization, the variable changes its purpose. Yet, with the same name, and with a bit of lost context, it is easy to confuse it later.
<?php
function foo() {
$variable = "initial"; // first initialisation
$variable = goo($variable); // processing the variable
hoo($variable); // sending the variable to a final destination
$variable = "second" ; // second initialisation
hoo2($variable); // sending the variable to a different final destination
}
?>
Alternatives :
- Use distinct names for each variable
- Split the method into smaller methods and unique variable name usage
Check Division By Zero
[Since 2.3.3] - [ -P Structures/CheckDivision ] - [ Online docs ]
Always check before dividing by a value. If that value is cast to 0, PHP might stop the processing with an exception, or keep processing it with 0 as a result. Both will raise problems.
The best practise is to check the incoming value before attempting the division. On possible alternative is to catch the DivisionByZeroError exception, that PHP 8.0 and more recent will raise.
<?php
// Check the value before usage
function foo($a = 1) {
if ($a !== 0) {
return 42 / $a;
} else {
// process an error
}
}
// Check the value after usage (worse than the above)
function foo($a = 1) {
try {
return 42 / $a;
} catch (DivisionByZero) {
// fix the situation now
}
}
// This might fails with a division by 0
function foo($a = 1) {
return 42 / $a;
}
?>
See also DivisionByZeroError.
Readonly Usage
[Since 2.3.5] - [ -P Classes/ReadonlyUsage ] - [ Online docs ]
Usage of the readonly option on classes and properties. Readonly is available on classes starting with PHP 8.2.
<?php
class x {
private readonly int $property = 1;
}
readonly class y {
private int $property = 1;
}
?>
See also Readonly properties.
Don't Reuse Foreach Source
[Since 2.3.5] - [ -P Structures/DontReuseForeachSource ] - [ Online docs ]
It is dangerous to reuse the same variable inside a loop that use it as a source.
PHP actually takes a copy of the source, so the foreach() loop is not affected by the modification. On the other hand, the source of the loop is modified after the loop (and actually, also during), which may come as a surprise to a later coder.
<?php
$properties = [];
foreach ($values as $i => $vars) {
// $values is used again here, now as a blind variable
foreach ($vars as $class => $values) {
foreach ($values as $name => $v) {
$properties[$class][$name][$i] = $v;
}
}
}
?>
Alternatives :
- Do not reuse the source as another variable
- Use different names to disambiguate their purpose
Unreachable Method
[Since 2.3.6] - [ -P Classes/UnreachableMethod ] - [ Online docs ]
A method that is never called from the code.
The method has the following characteristics :
- not private, aka public or protected
- The direct class is never instantiated
- All children classes overwrite this method
- parent:: is never used to reach it
Then, this class is actually dead code.
<?php
class x {
protected function foox() {}
}
class xx extends x {
protected function foox() {}
}
?>
Alternatives :
- Make the method abstract and remove the block
- Move the code to one of the child
Unfinished Object
[Since 2.3.6] - [ -P Classes/UnfinishedObject ] - [ Online docs ]
Some of the properties are not assigned a value before or at constructor time. Then, they might be called when one of the other public method is called, and yield a fatal error.
<?php
class x {
private $p;
private $p2;
function __construct($p) {
$this->p = $p;
// $p2 is not assigned
}
function foo() {
$this->p->goo();
// This is not valid
$this->p2->goo();
}
}
?>
Alternatives :
- Make sure the object is finished at construction time
Use class_alias()
[Since 2.3.6] - [ -P Php/UseClassAlias ] - [ Online docs ]
class_alias() is a PHP features, that allows the creation of class alias, at execution time.
Those class aliases are application wide, as they are valid everywhere, yet they have a lower precedence over the use expression. This means that even when a class_alias() was called, the local use expression will have right of execution.
<?php
// static type of aliasing
use a as c;
class a {}
class_alias('a', 'b');
new b;
?>
See also class_alias.
Undefined Enumcase
[Since 2.3.6] - [ -P Enums/UndefinedEnumcase ] - [ Online docs ]
The enumeration case does not exists. It may also be a constant.
<?php
enum theEnum {
case A; // an enum case
// a constant
const C = 1;
}
function foo(theEnum $a) {}
foo(theEnum::A);
foo(theEnum::C);
?>
Don't Add Seconds
[Since 2.3.9] - [ -P Structures/DontAddSeconds ] - [ Online docs ]
Avoid adding seconds to a date, and use DateTime::modify to add an interval.
This method will handle situations like daylight savings, leap seconds and even leap days.
<?php
// Tomorrow, same time
$tomorrow = new DateTime('now')->modify('+1 day');
// Tomorrow, but may be not at the same hour
$tomorrow = date('now') + 86400;
?>
See also DateTime::modify and datetime.
Use Constants As Returns
[Since 2.3.9] - [ -P Functions/UseConstantsAsReturns ] - [ Online docs ]
When a native PHP function returns only constants, it is recommended to use those constants to identify the returned values.
<?php
if (preg_last_error() != PREG_NO_ERROR ) {
// An error occured with the last Regex call
}
// Who will guess PREG_JIT_STACKLIMIT_ERROR ?
if (preg_last_error() == 6 ) {
// An error occured with the last Regex call
}
?>
Alternatives :
- Use the valid constants to identify the results
Identical Variables In Foreach
[Since 2.3.9] - [ -P Structures/IdenticalVariablesInForeach ] - [ Online docs ]
Do not use the same variable names as a foreach() source and one of its blind variables.
Foreach() makes a copy of the original data while working on it : this prevents any interference. Yet, when the source and the blind variable is the same, the source will have changed after the loop.
<?php
// classic way to use a foreach loop
foreach($array as $key => $value) {
// doSomething with $key and $value
}
// unusual way to end up with a name conflict
foreach($a as $a => [$b, $c, $a]) {
// doSomething with $a and $a, $b, $c
}
// classic way to use a foreach loop
foreach($a as $a => $b) {
// doSomething with $a and $a
}
// Now, after the loop, $a is an integer or a string!
?>
Alternatives :
- Use a different name for the source of the array and the blind values
Can't Overwrite Final Constant
[Since 2.3.9] - [ -P Classes/CantOverwriteFinalConstant ] - [ Online docs ]
A class constant may be final , and can't be overwritten in a child class. final is a way to make sure a constant cannot be changed in children classes.
private constants can't be made final, as they are not accessible to any other class.
<?php
class y extends x {
const F = 1;
const P = 2;
}
class x {
final const F = 3;
private const PRI = 5; // Private can't be final
const P = 4;
}
?>
Alternatives :
- Remove the final keyword in the parent class
- Remove the class constant in the child class
- Rename the class constant in the child class
String Int Comparison
[Since 2.3.9] - [ -P Php/StringIntComparison ] - [ Online docs ]
While PHP allows direct comparison of integer and strings, with some type conversion, the rules of conversion changed in PHP 8.0. This lead to a change in behavior for comparison.
In particular, strings that are equal to 0, or empty strings, have changed.
This doesn't affect identity comparison, since the type is initially checked.
<?php
PHP 7 PHP 8
var_dump(0 == "0"); true true
var_dump(0 == "0.0"); true true
var_dump(0 == "foo"); false false
var_dump(0 > ''); false true
var_dump(0 < ''); false false
var_dump(0 >= ''); true true
var_dump(0 <= ''); true false
?>
Alternatives :
- Force a conversion to integer before the comparison to make sure of the behavior.
ext/protobuf
[Since 0.8.4] - [ -P Extensions/Extprotobuf ] - [ Online docs ]
Extension Protobuf.
Protocol Buffers (a.k.a., protobuf) are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data.
<?php
// Example extracted from https://developers.google.com/protocol-buffers/docs/reference/php-generated
// given a simple message
//message Foo {}
/*
The protocol buffer compiler generates a PHP class called Foo. This class inherits from a common base class, Google\Protobuf\Internal\Message, which provides methods for encoding and decoding your message types, as shown in the following example:
*/
$from = new Foo();
$from->setInt32(1);
$from->setString('a');
$from->getRepeatedInt32()[] = 1;
$from->getMapInt32Int32()[1] = 1;
$data = $from->serializeToString();
try {
$to->mergeFromString($data);
} catch (Exception $e) {
// Handle parsing error from invalid data.
...
}
?>
See also Protocol Buffers, PHP Protocol Buffers and protobuf-php on packagist.
Unsupported Operand Types
[Since 1.7.2] - [ -P Structures/UnsupportedOperandTypes ] - [ Online docs ]
This error is raised when trying to combine an array and a scalar value.
Always checks that the types are compatible with the planned operations.
PHP detects this error at linting time, when using literal values. When static expression are involved, this error will appear at execution time.
<?php
const MY_ARRAY = 'error';
// This leads to the infamous "Unsupported operand types" error
$b = MY_ARRAY + array(3,4);
?>
Alternatives :
- Make sure all the planned operations are compatible with the type used.
version_compare Operator
[Since 2.3.1] - [ -P Php/VersionCompareOperator ] - [ Online docs ]
version_compare()'s third argument is checked for value. The third argument specifies the operator, which may be only one of the following : `<`, `lt`, `<=`, `le`, `>`, `gt`, `>=`, `ge`, `==`, `=`, `eq`, `!=`, `<>`, `ne`. The operator is case sensitive.
Until PHP 8.1, it was silently reverted to the default value. It is a deprecated warning in PHP 8.1 and will be finalized in PHP 9.0. It is recommended to fix this parameter in any PHP version.
<?php
// return true
var_dump(version_compare('2.0', '2.1', '<'));
// returns false
var_dump(version_compare('2.0', '2.1', '>'));
// returns NULL and might be interpreted as false
var_dump(version_compare('2.0', '2.1', 'as'));
?>
Alternatives :
- Use a valid comparison operator
PHP 8.1 Resources Turned Into Objects
[Since 2.2.0] - [ -P Php/Php81RemovesResources ] - [ Online docs ]
Multiple PHP native functions now return objects, not resources. Any check on those values with is_resource() is now going to fail.
The affected functions are the following :
- finfo_open()
- ftp_connect()
- imap_open()
- ldap_connect()
- ldap_list()
- ldap_search()
- ldap_first_entry()
- ldap_next_entry ()
- ldap_read()
- pg_connect()
- pg_pconnect()
- pg_query()
- pg_execute ()
- pg_lo_create()
- pspell_config_create()
- pspell_new()
- pspell_new_personal()
- pspell_new_config()
<?php
$pspell = new pspell_new(en, , , ,
(PSPELL_FAST|PSPELL_RUN_TOGETHER));
var_dump(is_resource($pspell)); // true in PHP 8.0,
// false in PHP 8.1
?>
Alternatives :
- Change the condition from is_resource() to instanceof
Do Not Cast To Int
[Since 0.10.6] - [ -P Php/NoCastToInt ] - [ Online docs ]
Do not cast floats values to int. Uses conversion functions like intval(), round(), floor() or ceil() to convert the value to integer, with known behavior.
Use functions like floor(), round() or ceil() : they use an explicit method for rounding, that helps keeping the side effects under control.
<?php
// echoes 7!
echo (int) ( (0.1 + 0.7) * 10 );
?>
Alternatives :
- Upgrade PHP version to 8.0, as those behavior are now the same
- Use one of the dedicated function : intval(), round(), floor() or ceil()
Constant Scalar Expression
[Since 0.8.4] - [ -P Php/ConstantScalarExpression ] - [ Online docs ]
Since PHP 5.6, it is possible to use expression with Constants and default values. One may only use simple operators.
<?php
const THREE = 1 + 2;
const ARRAY = array(1,2,3);
// dynamic version
define('ARRAY', array(1,2,3));
// constant scalar expression are available for default values
function foo($a = 1 + M_PI) {
}
?>
Alternatives :
- Upgrade to PHP 7.0
- Use a special value as the default value, and turn it into the actual value at constructor time
Could Be Spaceship
[Since 2.4.0] - [ -P Structures/CouldBeSpaceship ] - [ Online docs ]
The spaceship operator compares values and returns 0 for equality, 1 for superior and -1 for inferior.
It is the same as below, and prevents lots of code.
<?php
if ($a) {
return 1;
} elseif ($b) {
return 0;
} else {
return -1;
}
?>
Alternatives :
- Adopt the spaceship operator
Sylius usage
[Since 2.4.0] - [ -P Vendors/Sylius ] - [ Online docs ]
This analysis reports usage of the Sylius framework.
Sylius is an Open Source Headless eCommerce Platform for mid-market and enterprise brands that need custom solutions.
<?php
declare(strict_types=1);
namespace App\Controller;
use Sylius\Bundle\ResourceBundle\Controller\ResourceController;
use Sylius\Component\Resource\ResourceActions;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ProductController extends ResourceController
{
public function showAction(Request $request): Response
{
$configuration = $this->requestConfigurationFactory->create($this->metadata, $request);
$this->isGrantedOr403($configuration, ResourceActions::SHOW);
$product = $this->findOr404($configuration);
// some custom provider service to retrieve recommended products
$recommendationService = $this->get('app.provider.product');
$recommendedProducts = $recommendationService->getRecommendedProducts($product);
$this->eventDispatcher->dispatch(ResourceActions::SHOW, $configuration, $product);
if ($configuration->isHtmlRequest()) {
return $this->render($configuration->getTemplate(ResourceActions::SHOW . '.html'), [
'configuration' => $configuration,
'metadata' => $this->metadata,
'resource' => $product,
'recommendedProducts' => $recommendedProducts,
$this->metadata->getName() => $product,
]);
}
return $this->createRestView($configuration, $product);
}
}
?>
See also sylius.
Dollar Curly Interpolation Is Deprecated
[Since 2.4.1] - [ -P Php/DeprecateDollarCurly ] - [ Online docs ]
Among the different variable interpolation is strings, ```` is deprecated. It is made obsolete in PHP 8.2, and should disappear in PHP 9.0.
There are still several interpolation ways : variables, array elements (one index-level) and curly brackets. It is also possible to use string concatenation.
<?php
$a = 'a';
$a2 = 'surprise!';
$b = "\$\{$a . 2}";
echo $b;
// display 'surprise!'
?>
Alternatives :
- Use another interpolation style
- Use string concatenation
- Use a templating engine
- Use string replacement tool, such as str_replace()
Unused Enumeration Case
[Since 2.4.0] - [ -P Enums/UnusedEnumCase ] - [ Online docs ]
Those are enumeration cases which are defined, yet not used.
The error message when the case is missing mentions the class constant : this is because enumeration cases and class constants use the same configuration. They are only distinguished by their definition, which, here, does not exists.
<?php
enum x {
case A;
case C;
const F = 1;
}
function foo(x $a) {}
foo(x::A);
?>
Alternatives :
- Use the case in the code
- Remove the case in the code
- Fix the name of the case
- Turn the case in a constant
Useless Null Coalesce
[Since 2.4.0] - [ -P Structures/UselessNullCoalesce ] - [ Online docs ]
When the type system ensure the condition is never null, the operator becomes useless.
This is particularly true for properties (static or not) and returntype of methods and functions. And, to a lesser extend, to variables and parameters.
<?php
function foo(A $a, ?B $b) {
// $a is never null, so this is OK
$a ?? 'a';
// $b might be null, so this is OK
$b ?? 'b';
}
?>
Alternatives :
- Remove the ?? operator
- Switch to a ?: operator
- Updated the properties to accept NULL as a possible type
Throw Raw Exceptions
[Since 2.4.0] - [ -P Exceptions/ThrowRawExceptions ] - [ Online docs ]
Avoid throwing native PHP exceptions. Consider defining specific and meaningful exception, by extending the native one.
Thanks to Atif Shahab Qureshi for the inspiration.
<?php
// Throwing a raw exception
throw new exception('This is an error!');
class myException extends Exception {}
throw new myException('This is a distinguished error!');
?>
Alternatives :
- Define an adapted exception and throw it instead
Extensions yar
[Since 2.4.1] - [ -P Extensions/Extyar ] - [ Online docs ]
yar : Yet Another RPC framework.
<?php
/* assume this page can be accessed by http://example.com/operator.php */
class Operator {
/**
* Add two operands
* @param interge
* @return interge
*/
public function add($a, $b) {
return $this->_add($a, $b);
}
/**
* Sub
*/
public function sub($a, $b) {
return $a - $b;
}
/**
* Mul
*/
public function mul($a, $b) {
return $a * $b;
}
/**
* Protected methods will not be exposed
* @param interge
* @return interge
*/
protected function _add($a, $b) {
return $a + $b;
}
}
$server = new Yar_Server(new Operator());
$server->handle();
?>
See also yar.
Implicit Conversion To Int
[Since 2.4.2] - [ -P Structures/ImplicitConversionToInt ] - [ Online docs ]
PHP warns when a value is implicitely converted from float to int. This usually leads to a loss of precision and unexpected values.
The conversion happens in various situations in PHP lifecycle (extracted from the wiki article):
- Bitwise OR operator |
- Bitwise AND operator &
- Bitwise XOR operator ^
- Shift right and left operators
- Modulo operator
- The combined assignment operators of the above operators
- Assignment to a typed property of type int in coercive typing mode
- Argument for a parameter of type int for both internal and custom functions in coercive typing mode
- Returning such a value for custom functions declared with a return type of int in coercive typing mode
- Bitwise NOT operator ~
- As an array key
This features is applied to PHP 8.1 and later, yet it is also applicable to older versions of PHP.
<?php
function foo(int $i) {}
//Implicit conversion from float 1.2 to int loses precision
foo(1.2);
?>
Alternatives :
- Add an explicit cast `(int)` operator
Excimer
[Since 2.4.2] - [ -P Extensions/Extexcimer ] - [ Online docs ]
Excimer is a PHP 7.1+ extension that provides an interrupting timer and a low-overhead sampling profiler.
<?php
$timer = new ExcimerTimer;
$timer->setInterval( 10 /* seconds */ );
$timer->setCallback( function () {
throw new Exception( "The allowed time has been exceeded" );
} );
$timer->start();
do_expensive_thing();
?>
See also Excimer.
Use Same Types For Comparisons
[Since 2.4.2] - [ -P Structures/UseSameTypesForComparisons ] - [ Online docs ]
Beware when using inequality operators that the type of the values are the same on both sites of the operators.
Different types may lead to PHP type juggling, where the values are first cast to one of the used types. Other comparisons are always failing, leading to unexpected behavior.
This applies to all inequality operators, as well as the spaceship operator.
This analysis skips comparisons between integers, floats and strings, as those are usually expected.
Thanks to Jordi Boggiano and Filippo Tessarotto.
<?php
// Both are wrong, while one should be true (depending on when you read this)
var_dump('1995-06-08' < new DateTimeImmutable());
var_dump('1995-06-08' > new DateTimeImmutable());
enum x : int {
case A = 1;
case B = 2;
}
// Both are false as objects are compared, not their integer value
var_dump(x::A < x::B);
var_dump(x::A > x::B);
var_dump(x::A->value < x::b->value);
var_dump(x::A->value > x::b->value);
?>
Alternatives :
- Make sure that the same time
Wrong Locale
[Since 2.4.2] - [ -P Structures/WrongLocale ] - [ Online docs ]
This rule checks the locale used in the code, against a library of known valid locales. Unknown locales are reported: they might be typos or unknown to certain systems.
<?php
// what language ?
setLocale(LC_ALL, 'hx');
// utf8 actually needs a - : utf-8
setLocale(LC_ALL, 'utf8');
?>
Alternatives :
- Use a valid locale
ext/pkcs11
[Since 2.4.2] - [ -P Extensions/Extpkcs11 ] - [ Online docs ]
In cryptography, PKCS #11 is one of the Public-Key Cryptography Standards. This extensions provides methods to create, read and check those keys.
<?php
$key = $session->generateKey(new Pkcs11\Mechanism(Pkcs11\CKM_AES_KEY_GEN), [
Pkcs11\CKA_CLASS => Pkcs11\CKO_SECRET_KEY,
Pkcs11\CKA_SENSITIVE => true,
Pkcs11\CKA_ENCRYPT => true,
Pkcs11\CKA_DECRYPT => true,
Pkcs11\CKA_VALUE_LEN => 32,
Pkcs11\CKA_KEY_TYPE => Pkcs11\CKK_AES,
Pkcs11\CKA_LABEL => "Test AES",
Pkcs11\CKA_PRIVATE => true,
]);
?>
ext/spx
[Since 2.4.2] - [ -P Extensions/Extspx ] - [ Online docs ]
SPX, which stands for Simple Profiling eXtension, is just another profiling extension for PHP.
<?php
while ($task = get_next_ready_task()) {
spx_profiler_start();
try {
$task->process();
} finally {
spx_profiler_stop();
}
}
?>
See also `Parent Is Not Static
[Since 2.4.3] - [ -P Classes/ParentIsNotStatic ] - [ Online docs ]
The `parent` keyword behaves like `self`, not like `static`. It links to the parent of the defining expression, not to the one being called.
This may skip the parent of the calling class, and create a `Undefined method` call, or yield the wrong `::class` value. It may also skip a local version of the method.
<?php
class w {
}
class x extends w {
function foo() {
parent::method();
}
// method() is in the parent of Y, but not in the one of X.
function method() {
print __METHOD__;
}
}
class y extends x {}
(new y)->foo();
// print W::method
(new y)->method();
// print x::method
?>
Alternatives :
- Use self keyword
- Use static keyword
- Use hard-coded class name keyword
No Magic Method For Enum
[Since 2.4.2] - [ -P Enums/NoMagicMethod ] - [ Online docs ]
Enumeration cannot have magic methods, nor a constructor. Enumeration cases are created as needed, and magic methods are interfering with the default behavior of enumerations.
<?php
enum a {
function __construct($a) {}
}
?>
Alternatives :
- Remove the method
No Readonly Assignation In Global
[Since 2.4.2] - [ -P Classes/NoReadonlyAssignationInGlobal ] - [ Online docs ]
When a property is marked readonly, it may only be assigned within the class of definition.
It cannot be assigned outside this class, in the global scope. It is also immune to class invasion.
<?php
class x {
public readonly int $p;
function foo() {
$this->p -= 1; // OK
$x = new x;
$x->p = 1; // Not OK, even if $x is of type x
}
}
$x = new x;
$x->p = 1; // Not OK
?>
Stomp
[Since 2.4.2] - [ -P Extensions/Extstomp ] - [ Online docs ]
This extension allows php applications to communicate with any Stomp compliant Message Brokers through easy object-oriented and procedural interfaces.
<?php
$queue = '/queue/foo';
$msg = 'bar';
/* connection */
try {
$stomp = new Stomp('tcp://localhost:61613');
} catch(StompException $e) {
die('Connection failed: ' . $e->getMessage());
}
/* send a message to the queue 'foo' */
$stomp->send($queue, $msg);
/* subscribe to messages from the queue 'foo' */
$stomp->subscribe($queue);
/* read a frame */
$frame = $stomp->readFrame();
if ($frame->body === $msg) {
var_dump($frame);
/* acknowledge that the frame was received */
$stomp->ack($frame);
}
/* close connection */
unset($stomp);
?>
See also Stomp.
ext/CSV
[Since 2.4.2] - [ -P Extensions/Extcsv ] - [ Online docs ]
A small PHP extension to add/improve the handling of CSV strings.
<?php
$fields = [
'Hello',
'World',
];
$output = "Hello,World";
var_dump($output === CSV::arrayToRow($fields));
var_dump(CSV::rowToArray($output));
?>
See also PHP csv extension.
Overload Existing Names
[Since 2.4.2] - [ -P Namespaces/OverloadExistingNames ] - [ Online docs ]
Imported alias have precedence over existing ones, and as such, may replace existing features with unexpected ones.
This example shows how to replace strtolower() with strtoupper() while keeping the main code intact. This might be very confusing code.
This behavior is important for backward compatibility, and also to avoid naming conflicts when the coding has been done with a PHP installation which do not have some specific declaration. For example, a source may define an 'Event' class, which will be in conflict when the ext/event library is installed.
This feature is also useful to mock some native PHP structures, during tests.
This rule relies on the PDFF configuration to check for external existing structures.
<?php
// Replacing a PHP classic with another one
use function strtoupper as strtolower;
echo strtolower('pHp');
// displays PHP
?>
Alternatives :
- Use another local name than the general name
- Always code in a namespace to avoid conflict
Array Addition
[Since 2.4.2] - [ -P Structures/ArrayAddition ] - [ Online docs ]
Addition where one of the operands are arrays.
<?php
$a = [1] + [2 ,3];
?>
See also Combining arrays using + versus array_merge in PHP and Array operators.
Retyped Reference
[Since 2.4.3] - [ -P Functions/RetypedReference ] - [ Online docs ]
A parameter with a reference may be typed differently, at the end of a method call.
It is possible for a referenced and typed parameter to be retyped during a method call. As such, the type of the used variable might both be checked and changed.
Using such syntax will lead to confusion in the code.
This works on all types, scalars or objects.
This rule will detect variables which are defined with a placeholder value, or even undefined, and are filled during the method call.
<?php
$a = [1];
foo($a);
echo $a; // Now, $a is a string
function foo(array &$a) {
$a = "Now, I am a string";
}
?>
Alternatives :
- Do not change a referenced variable's type
- Set the called value to a compatible type.
Wrong Type With Default
[Since 2.4.4] - [ -P Typehints/WrongTypeWithDefault ] - [ Online docs ]
The default value is not of the declared type.
For properties, this will generate an error as soon as the default value is used : this is before constructor call for properties, and when the argument is omitted for promoted properties.
For parameters, the error happens when the argument is omitted, and the default value is fetched. Otherwise, it won't happen.
This error is immediately detected when a literal value is used. It only happens when the default is a constant (class or global) or an expression, as those are only solved at execution time.
<?php
const A = 1;
class B {
private string $c = A;
}
new B;
//Cannot assign string to property B::$c of type string
?>
See also When does PHP check for Fatal error.
Ice framework
[Since 2.4.4] - [ -P Extensions/Extice ] - [ Online docs ]
Ice - simple, fast and open-source PHP framework frozen in C-extension. Ice is loosely coupled, allowing developers to use only the components that they need.
Extensions/Exttaint
[Since 2.4.4] - [ -P Extensions/Exttaint ] - [ Online docs ]
Taint is a extension used to detect and track tainted string. It follows each assignation of the code and keeps track of its taint. And also can be used to spot sql injection vulnerabilities, shell inject, etc.
<?php
$a = trim($_GET['a']);
$file_name = '/tmp' . $a;
$output = "Welcome, {$a} !!!";
//Warning: main() [function.echo]: Attempt to echo a string that might be tainted
?>
See also taint and taint on github.
Sprintf Format Compilation
[Since 2.4.5] - [ -P Structures/SprintfFormatCompilation ] - [ Online docs ]
The sprintf() format used yields an error.
This applies to printf(), sprintf(), vprintf(), vfprintf(), vsprintf(), sscanf(), fscanf()
<?php
printf('"%we3e"', 123);
//Unknown format specifier
?>
Alternatives :
- Fix the format
Invalid Date Scanning Format
[Since 2.4.5] - [ -P Structures/InvalidDateScanningFormat ] - [ Online docs ]
The format string used with Datetime::createFromFormat() method (or similar) contains unknown characters.
This won't raise an error, though the resulting values should be checked.
<?php
// format is valid
$date = datetimeimmutable::createFromFormat('d/m/Y', $a);
// When wrong, $date is false
// The errors are in datetimeimmutable::getLastErrors();
// X is not a valid character for
$date = datetimeimmutable::createFromFormat('d/X/Y', $a);
?>
Alternatives :
- Remove the unknown characters
- Replace the unknown character with the expected one
Same Name For Property And Method
[Since 2.4.5] - [ -P Classes/PropertyMethodSameName ] - [ Online docs ]
A property and a method have the same name. While it is a valid naming scheme with PHP, it may lead to confusion while codeing.
Such naming collision may appear with words that are the same as a verb (for method) and as a noun (for property). For example, in English : query, work, debug, run, process, rain, polish, paint, etc,.
It may also happen during the life cycle of the class, as it is extended with new methods and properties, and little care is give to semantic meaning of the names, beyond the task at hand.
It is recommended to avoid those collisions, and keep properties and methods named distinctly.
That problem do not happen to constants, which are mostly written uppercase. This rule is case-insensitive.
<?php
class x {
public $foo;
function foo() {}
}
$x = new X:
$x->p = $x->foo();
?>
Alternatives :
- Fix any spelling in the names
- Rename the property or the method
Utf8 Encode And Decode Are Deprecated
[Since 2.4.5] - [ -P Php/Utf8EncodeDeprecated ] - [ Online docs ]
utf8_encode() and utf8_decode() are deprecated in PHP 8.0. They are planned removal in PHP 9.0.
- Use mbstring functions : mb_convert_encoding($latin1, 'UTF-8', 'ISO-8859-1')
- Use iconv functions : mb_convert_encoding($latin1, 'UTF-8', 'ISO-8859-1')
- Use intl functions : iconv('ISO-8859-1', 'UTF-8', $latin1)
DateTimeImmutable Is Not Immutable
[Since 2.4.5] - [ -P Php/DateTimeNotImmutable ] - [ Online docs ]
DateTimeImmutable is not really immutable because its internal state can be modified after instantiation.
Inspired by the article from Matthias Noback .
<?php
$dt = new DateTimeImmutable('now');
echo $dt->getTimestamp() . "\n";
$dt->__construct('tomorrow');
echo $dt->getTimestamp() . "\n";
?>
Alternatives :
- Remove the call to the constructor after instantation of a DateTimeImmutable object
New Functions In PHP 8.2
[Since 2.3.0] - [ -P Php/Php82NewFunctions ] - [ Online docs ]
New functions are added to new PHP version.
The following functions are now native functions in PHP 8.2. It is compulsory to rename any custom function that was created in older versions. One alternative is to move the function to a custom namespace, and update the use list at the beginning of the script.
- curl_upkeep()
- mysqli_execute_query()
- odbc_connection_string_is_quoted()
- odbc_connection_string_should_quote()
- odbc_connection_string_quote()
- ini_parse_quantity()
- memory_reset_peak_usage()
<?php
// Such function will not be possible in PHP 8.2 anymore
function memory_reset_peak_usage() {}
?>
Alternatives :
- Move custom functions with the same name to a new namespace
- Change the name of any custom functions with the same name
- Add a condition to the functions definition to avoid conflict
No Default For Referenced Parameter
[Since 2.4.7] - [ -P Functions/NoDefaultForReference ] - [ Online docs ]
Parameters with reference should not have a default value.
When they have a default value, that default value is not a reference, and it will not have impact on the calling context.
Then, the parameter behaves like a reference when the argument is provided, and not as a reference when the parameter is not provided. This makes sense : no parameter in, no parameter out.
<?php
function foo(&$i = 1) {
++$i;
}
// $i is 1, but it is not available in the calling context
foo();
// $i is 1, but it is not available in the calling context
$i = 1;
foo($i);
echo $i; // $i is now 2
?>
Alternatives :
- Remove the reference
- Make that parameter a local variable
- Remove the default value
Clone Constant
[Since 2.4.7] - [ -P Php/CloneConstant ] - [ Online docs ]
Cloning constant is only possible since PHP 8.1. Until that version, constants could not be an object, and as such, could not be cloned.
This is also valid with default values, however they are assigned to a variable, which falls back to the classic clone usage.
Backward compatibility is OK, since PHP compile such code, and only checks at execution time that the constant is an object.
<?php
// new is available in constant definition, since PHP 8.2
const A = new B();
$c = clone A;
?>
See also New in initializers.
Random extension
[Since 2.4.7] - [ -P Extensions/Extrandom ] - [ Online docs ]
The random extension. It improves the random generators from the older PHP version, and provides a OOP interface.
<?php
$rng = $is_production
? new Random\Engine\Secure()
: new Random\Engine\PCG64(1234);
$randomizer = new Random\Randomizer($rng);
$randomizer->shuffleString('foobar');
?>
See also PHP RFC: Random Extension 5.x.
Ip
[Since 2.4.7] - [ -P Type/Ip ] - [ Online docs ]
This rule lists hardocded IPs in the source. Such IPs cannot be changed, and may produce unexpected results.
<?php
$ip = '123.34.56.227';
$a = '3627734755';
$a = '000000000330.0000000072.00000000326.0343';
?>
See also IP converter.
Could Inject Parameter
[Since 2.4.7] - [ -P Classes/CouldInjectParam ] - [ Online docs ]
The parameter is immediately used to create an object. It could be interesting to replace it with an injection of that object's type to keep the method generic.
<?php
class x {
// The directory is immediately injected
function foo(Directory $dir) {
$this->dir = $dir;
}
// Path is injected, then turned into a directory
function bar(string $path) {
$this->dir = new Directory($path);
}
}
?>
Alternatives :
- Use the instantiation as the type of the parameter.
ext/scrypt
[Since 2.4.7] - [ -P Extensions/Extscrypt ] - [ Online docs ]
This is a PHP library providing a wrapper to Colin Percival's scrypt implementation. Scrypt is a key derivation function designed to be far more secure against hardware brute-force attacks than alternative functions such as PBKDF2 or bcrypt.
<?php
echo scrypt("", "", 16, 1, 1, 64) . "\n";
echo scrypt("password", "NaCl", 1024, 8, 16, 64) . "\n";
?>
See also `scrypt ext/teds
[Since 2.4.8] - [ -P Extensions/Extteds ] - [ Online docs ]
teds (Tentative Extra Data Structures) is a collection of data structures and iterable functionality.
<?php
// discards keys
$it = new Teds\BitVector(['first' => true, 'second' => false]);
foreach ($it as $key => $value) {
printf("Key: %s\nValue: %s\n", var_export($key, true), var_export($value, true));
}
var_dump($it);
var_dump((array)$it);
$it = new Teds\BitVector([]);
var_dump($it);
var_dump((array)$it);
foreach ($it as $key => $value) {
echo "Unreachable\n";
}
// Teds\BitVector will always reindex keys in the order of iteration, like array_values() does.
$it = new Teds\BitVector([2 => true, 0 => false]);
var_dump($it);
var_dump(new Teds\BitVector([-1 => false]));
?>
See also PECL TEDS.
Geospatial
[Since 2.4.7] - [ -P Extensions/Extgeospatial ] - [ Online docs ]
PHP Extension to handle common geospatial functions. The extension currently has implementations of the Haversine and Vincenty's formulas for calculating distances, an initial bearing calculation function, a Helmert transformation function to transfer between different supported datums, conversions between polar and Cartesian coordinates, conversions between Degree/Minute/Seconds and decimal degrees, a method to simplify linear geometries, as well as a method to calculate intermediate points on a LineString.
NB : description and exemples are extracted from the extension source code.
<?php
$from = array(
'type' => 'Point',
'coordinates' => array( -104.88544, 39.06546 )
);
$to = array(
'type' => 'Point',
'coordinates' => array( -104.80, 39.06546 )
);
var_dump(haversine($to, $from));
?>
See also `geospatial - PHP Geospatial Extension Feast usage
[Since 2.4.8] - [ -P Vendors/Feast ] - [ Online docs ]
This analysis reports usage of the Feast framework.
FEAST is a radically different PHP Framework that was built from the ground up to be an alternative to the dependency-heavy frameworks that exist already. Its goal is a light-weight footprint that just lets you get stuff done.
<?php
$this->httpRequest->postJson(self::URL . '/subscribers');
$this->httpRequest->addArguments($data);
$this->httpRequest->authenticate($this->apiKey, '');
$this->httpRequest->makeRequest();
$response = $this-httpRequest->getResponseAsJson();
?>
See also Feast and `Feast on githubdate() versus DateTime Preference
[Since 2.4.9] - [ -P Structures/DateTimePreference ] - [ Online docs ]
Processing dates is done with date() functions or DateTime classes.
In the date() team, there are the following functions : date(), time(), getdate(), localtime(), strtotime(), strptime(), gmdate(), strftime(), mktime(), gmktime().
In the DateTime team, there are the instantiation of DateTime and DateTimeImmutable; the DateTime::createFromInterface(), DateTime::createFromFormat(), DateTime::createFromImmutable() and DateTime::createFromMutable().
The analyzed code has less than 10% of one of them : for consistency reasons, it is recommended to make them all the same.
<?php
// be consistent
$date = date();
$time = time();
$date = date();
$time = time();
$date = date();
$time = time();
$date = date();
$time = time();
$date = date();
$time = time();
$date = date();
$time = time();
// Be consistent, always use the same.
$date = new DateTime();
?>
Unused Public Methods
[Since 2.4.9] - [ -P Classes/UnusedPublicMethod ] - [ Online docs ]
This rule lists unused public methods.
Unused public methods are declared as public in the class, but never called, including outside the class.
<?php
class x {
public function usedMethod() {}
// There is no call to this method
public function unusedMethod() {}
}
$x = new x();
$x->usedMethod();
?>
Mbstring Unknown Encodings
[Since 2.5.0] - [ -P Structures/MbStringNonEncodings ] - [ Online docs ]
mbstring functions require one of its supported encoding as parameter.
For example, mb_chr() requires encoding as second parameter. The supported encodings are available with mb_list_encodings() and mb_encoding_aliases().
A wrong encoding generates a fatal error.
Here are some of the dropped encodings, depending on PHP versions:
- PHP 7.0
- PHP 8.0
- PHP 8.1
- PHP 8.2
+ byte2be
+ byte2le
+ byte4be
+ byte4le
+ jis-ms
+ cp50220raw
+ base64
+ uuencode
+ html-entities
<?php
print mb_chr(128024, 'UTF-8')); // emoji of an elephant
//Argument #2 ($encoding) must be a valid encoding, "elephpant" given
print mb_chr($value, 'elephpant'));
}
?>
Alternatives :
- Use a valid encoding for the PHP version.
Named Argument And Variadic
[Since 2.5.0] - [ -P Php/NamedArgumentAndVariadic ] - [ Online docs ]
Variadic argument must be the last in the list of arguments. Since PHP 8.1, it is possible to use named arguments after a variadic argument.
<?php
// named arguments may be after the variadic
foo(...$a, a: 1);
// positional arguments MUST be before the variadic
foo(...$a, 1);
// Normal way
foo( 1, ...$a);
?>
Alternatives :
- Always put the variadic at the end of the argument list
Coalesce And Ternary Operators Order
[Since 2.5.0] - [ -P Structures/CoalesceNullCoalesce ] - [ Online docs ]
The ternary operator and the null-coalesce operator cannot be used in any order. The ternary operator is wider, so ot should be used last.
In particular, the ternary operator works on truthy values, and NULL is a falsy one. So, NULL might be captured by the ternary operator, and the following coalesce operator has no chance to process it.
On the other hand, the coalesce operator only process NULL, and will leave the false (or any other falsy value) to process to the ternary operator.
<?php
// Good order : NULL is processed first, otherwise, false will be processed.
$b = $a ?? 'B' ?: 'C';
// Wrong order : this will never use the ??
$b = $a ?: 'C' ?? 'B';
?>
Alternatives :
- Use the good order of operator : most specific first, then less specific.
Useless Assignation Of Promoted Property
[Since 2.5.0] - [ -P Classes/UselessAssignationOfPromotedProperty ] - [ Online docs ]
Promoted properties save the assignation of constructor argument to the property. It is useless to do it with that syntax, and in the constructor too.
<?php
class x {
private $b;
function __construct(private $a,
$b,
) {
// This is already done with the promoted property
$this->a = $a;
// This is the traditional way (up to PHP 8.0)
$this->b = $b;
}
}
?>
Alternatives :
- Remove the assignation in the constructor
Empty Loop
[Since 2.5.0] - [ -P Structures/EmptyLoop ] - [ Online docs ]
This rule reports empty loop. An empty loop has no operation in its main block.
Some empty loop may have features: they are calling methods in the condition, which may change the status of a resource.
Empty loop may come from a typo, where a semi colon detach the block from its loop.
<?php
$i = 0;
// sneaky semi-colon behind the while
while($i < 10) ; {
$i++;
}
// another sneaky semicolon
foreach($a as $b) ;
{
$i++;
}
// This skips the first empty lines
$fp = fopen('/path/to/file', 'r');
while(!($row = fgets($fp))) {
}
?>
Alternatives :
- Remove the extra semicolon
- Fill the loop with a payload
Useless Method
[Since 2.5.1] - [ -P Classes/UselessMethod ] - [ Online docs ]
This method is useless, as it actually does what PHP would do by default.
For example, relaying a method call to its parent is useless. Removing the method altogether has the same feature, although this doesn't apply to constructors.
<?php
class y {
function foo() {
// doSomething('foo')
}
function goo() {
// doSomething('goo')
}
}
class x extends y {
// No definition for goo(), so it fallback to the parent
// This definition of foo() falls back to the parent's,
// just like if it wasn't there.
function foo() {
return parent::foo();
}
}
?>
Alternatives :
- Remove the useless method
- Add more code to the method body
Weak Type With Array
[Since 2.5.1] - [ -P Arrays/WeakType ] - [ Online docs ]
Using array as a type, to use specific index later.
The type of array is too weak : it allows to know that the array syntax has to be used in the function. Yet, it doesn't enforce the presence or absence of a specific index.
<?php
function foo(array $variable) {
echo $array['display'];
}
?>
Alternatives :
- Use a class as type, instead of
Multiple Type Cases In Switch
[Since 2.5.1] - [ -P Structures/MultipleTypeCasesInSwitch ] - [ Online docs ]
This reports switch() instructions, which have several types in cases.
This might generate compatibility errors, as the comparison may succeed in different ways, depending on PHP versions. This is particularly the case for PHP 8.0, and values such as '0', '', 0, null, and false.
This situation doesn't affect match(), as it uses a strict type comparison, unlike switch().
<?php
switch($a) {
case 1:
break;
case 'a':
break;
}
?>
Alternatives :
- Make all the types identical in the cases.
- Switch to match() call, to include a type check
Plus Plus Used On Strings
[Since 2.5.1] - [ -P Php/PlusPlusOnLetters ] - [ Online docs ]
Reports strings that are incremented with the post increment operator 's'++ .
This spots issues of the famous feature of PHP : incrementing strings with letters.
This analysis checks for string to be incremented. It doesn't check if the string is a numeric string, but does check the type, implicit or explicit.
<?php
$a = 'a';
$a++;
print $a;
// prints b
?>
See also Incrementing/Decrementing Operators and Path to Saner Increment/Decrement operators.
No Max On Empty Array
[Since 2.5.2] - [ -P Structures/NoMaxOnEmptyArray ] - [ Online docs ]
Using max() or min() on an empty array leads to a valueError exception.
Until PHP 8, max() and min() would return null in case of empty array. This might be confusing with actual values, as an array can contain null . null has a specific behavior when comparing with other values, and should be avoided with max() and sorts.
Until PHP 8.0, a call on an empty array would return null, and a warning.
<?php
// Throws a value error
$a = max([]);
$array = [];
if (empty($array)) {
$a = null;
} else {
$a = max($array);
}
var_dump(min([-1, null])); // NULL
var_dump(max([-1, null])); // -1
var_dump(max([1, null])); // 1
?>
Alternatives :
No Empty String With explode()
[Since 2.5.2] - [ -P Structures/NoEmptyStringWithExplode ] - [ Online docs ]
explode() doesn't allow empty strings as separator. Until PHP 8.0, it would make a warning, and return false. After that version, it raises a ValueError.
To break a string into individual characters, it is possible to use the array notation on strings, or to use the str_split() function.
<?php
explode('', "a");
?>
Alternatives :
- Check for empty strings (or equivalent) before using explode()
- Use the array notation to access individual chars
- Use str_split() to break the string into an array
Array Access On Literal Array
[Since 2.5.2] - [ -P Structures/ArrayAccessOnLiteralArray ] - [ Online docs ]
Accessing an element on a literal array makes that array non-reusable.
It is recommended to make this array a constant or a property, for easier reusage. It also make that content more visiblem in the class definitions.
<?php
class Suit {
const NAMES = ['Club' => 1, 'Spade' => 2, 'Heart' => 3, 'Diamond' => 4];
function __construct($name) {
if (!isset(self::NAMES[$name]) {
throw new Exception('Not a suit color');
}
}
}
class HiddenSuitList {
function __construct($name) {
if (!isset(['Club' => 1, 'Spade' => 2, 'Heart' => 3, 'Diamond' => 4][$name]) {
throw new Exception('Not a suit color');
}
}
}
?>
Double Checks
[Since 2.5.2] - [ -P Structures/DoubleChecks ] - [ Online docs ]
Double checks happen when data is checked at one point, and then, checked again, with the same test, in a following call.
Some of the testing may be pushed to the type system, for example is_int() and int type. Others can't, as the check is not integrated in the type system, such as is_readable() and string , for a path.
The check may be removed from the method, when the method is not called elsewhere without protection.
Cleaning such structure leads to micro-optimisation.
<?php
if (is_writeable($path)) {
foo($path);
}
function foo(string $path) {
// This was already tested
if (!is_writeable($path)) {
return;
}
}
?>
Alternatives :
- Remove the check in the method
- Remove the check in the caller code
- Use type system
strpos() With Integers
[Since 2.5.2] - [ -P Php/StrposWithIntegers ] - [ Online docs ]
strpos() used to accept integer as second argument, and turn them into their ASCII equivalent. This was deprecated in PHP 7.x, and dropped in 8.0.
It is recommended to use casting to ensure the variable is actually strings, and strpos() behaves as expected.
<?php
strpos('abc ', 32);
// PHP 8.0+ : false, 32 is not found
// PHP 7.4- : 3, 32 is turned into space, then found
?>
Alternatives :
- Add a cast to make the data string
- Test the data to be a string before usage
Missing Assignation In Branches
[Since 2.5.2] - [ -P Structures/MissingAssignation ] - [ Online docs ]
A variable is assigned in one of the branch, but not the other. Such variable might be needed later, and when going throw this branch, it won't be available.
In this analysis, elseif() and branches that return or goto somewhere else are omitted.
<?php
if ($condition) {
$a = 1;
$b = 2;
} else {
$a = 3;
}
// $b might be missing
?>
Short Ternary
[Since 2.5.2] - [ -P Php/ShortTernary ] - [ Online docs ]
Short ternaries are the ternary operator, where the middle operand was left out.
Written that way, the operator checks if the first operand is empty() : in that case, the second operand is used; Otherwise, the first operand is used.
<?php
// $b is now 2
$b = $a ?: 2;
// $c is now 2 also
$c = $b ?: 4;
?>
See also Ternary Operator.
Deprecated Mb_string Encodings
[Since 2.5.2] - [ -P Structures/DeprecatedMbEncoding ] - [ Online docs ]
Some encodings, available in the mb_string extensions, are deprecated. Starting with PHP 8.2, the following encodings emits a warning:
- BASE64
- UUENCODE
- HTML-ENTITIES
- html
- Quoted-Printable
- qprint
This applies to the mb_detect_encoding() and mb_convert_encoding() functions.
<?php
// recommended version
$base64Encoded = base64_encode('test'));
// Deprecated version
mb_convert_encoding('test', 'base64'));
?>
Alternatives :
- Use uuencode() and uudecode() functions.
No Valid Cast
[Since 2.5.2] - [ -P Structures/NoValidCast ] - [ Online docs ]
This cast generates an error, as there is no way to convert an object to an int.
The result will be 1.
This rule applies to float and int. This doesn't apply to string cast, as the magic method __toString() allows for such conversions.
<?php
$a = (int) foo();
function foo() : A {}
?>
Alternatives :
- Create a method that convert the original object to the target type
Misused Yield
[Since 2.5.2] - [ -P Structures/MisusedYield ] - [ Online docs ]
When chaining generator, one must use the yield from keyword.
Forgetting the yield from keyword cancels the generator nature of the functioncall and nothing is emited.
Using yield on a generator, yields ... the generator, not the values of the generator.
It is legit to yield a generator, for later usage. This is just very uncommon, and worth a check.
<?php
function foo() {
yield 1;
// Goo is called, but not run as a generator
goo();
}
function hoo() {
yield 1;
// Goo is yield, but not run as a generator
yield goo();
}
function goo() {
yield 3;
}
?>
Alternatives :
- Use the yield from keyword
Use DNF
[Since 2.5.3] - [ -P Php/UseDNF ] - [ Online docs ]
This rule detects the usage of the DNF. DNF is the disjunctive Normal Form. It is a syntax to handle union and intersectional types at the same time. It was introducted in PHP 8.2.
DNF is available for every typed element of PHP : properties, arguments and returntype. It was extended to class constants on PHP 8.3.
<?php
class x {
const (A&B)|string C = 'string';
function foo((A&B)|(C&D) $e) {}
}
?>
See also PHP 8.2: DNF Types.
No Null For Index
[Since 2.5.3] - [ -P Structures/NoNullForIndex ] - [ Online docs ]
Avoid using null value as an index in an array. PHP actually cast it to the empty string. This means that later, it might be impossible to find the null in the list of keys.
<?php
$a = [];
$a[null] = 1;
print_r(array_keys($a));
// [''] empty string
?>
Alternatives :
- Always checks for null values. Given it then a valid value.
Useless Try
[Since 2.5.3] - [ -P Exceptions/UselessTry ] - [ Online docs ]
Report try clause that are useless. A try clause is useless when no exception is emitted by the code in the block.
This happens when the underlying layers removed the emission of exceptions.
<?php
try {
// Nothing is going to happen here
++$a;
} catch (Exception $e) {
}
?>
Alternatives :
- Remove the Try clause
- Add a throw among the different called methods
Converted Exceptions
[Since 2.5.3] - [ -P Exceptions/ConvertedExceptions ] - [ Online docs ]
Converted exceptions is when an exception is caught, then immediately converted into another one and thrown again.
Sometimes, extra operations take place, such as logging or error couting.
<?php
try {
doSomething();
} catch (MyException $e) {
log($e->getMessage());
throw new BadRequestException();
}
?>
Method Is Not An If
[Since 2.5.3] - [ -P Functions/MethodIsNotAnIf ] - [ Online docs ]
When a method consists only in one if statement, it might be worth refactoring.
Each of the blocks of the if/then structure may be turned into their own method, so has to keep operations distinct.
Then, the condition can be used as part of a larger method.
<?php
function foo($a) {
if ($a === 1) {
return 1;
} else {
return 2;
}
}
?>
Alternatives :
- Export the blocks to distinct functions
- Bail out early
Default Then Discard
[Since 2.5.3] - [ -P Structures/DefaultThenDiscard ] - [ Online docs ]
Discard the value before assigning it.
In the code below, the variable is assigned a default value. Then, this value is immediately tested and discarded.
It is more readable to test the value, and discard it, or assign it later, rather than assign first then discard it later.
<?php
$a = $a ?? null;
if ($a === null) {
throw new Exception();
}
doSomething();
// Alternative code
if (!isset($a) || $a === null) {
throw new Exception();
}
// $a has a valid value for the purpose
doSomething();
?>
Alternatives :
- Test the value and bail out if it is not valid before assigning it
Identical Case In Switch
[Since 2.5.3] - [ -P Structures/IdenticalCase ] - [ Online docs ]
In a switch() or match() statement, when there are identical cases, it means that multiple case labels that have the same code block.
This can happen by mistake or design. They may be merged together.
<?php
switch($a) {
case 1:
$b = 2;
break;
case 2:
$b = 12;
break;
// Identical to case 1
case 3:
$b = 2;
break;
}
?>
Alternatives :
- Merge the cases and reduce the size of code
- Review the cases code and make them different
StandaloneType True False Null
[Since 2.5.3] - [ -P Typehints/StandaloneTypeTFN ] - [ Online docs ]
Report usage of standalone types of true, false and null.
false and null were added to PHP in PHP 8.2, as standalone types : they can be used alone in a type declaration (property, argument or returntype). true was added in PHP 8.3.
<?php
// simplistic example
function foo(true $t) : false {
return false;
}
?>
See also What's the 'true' Standalone Type in PHP?.
Constants In Traits
[Since 2.5.3] - [ -P Traits/ConstantsInTraits ] - [ Online docs ]
Traits may have their own constants. This feature was introduced in PHP 8.2 and is not backward compatible.
<?php
trait t {
const A = 1;
}
?>
See also PHP RFC: Constants in Traits and Ability to use Constants in Traits in PHP 8.2.
Could Use Yield From
[Since 2.5.3] - [ -P Structures/CouldUseYieldFrom ] - [ Online docs ]
Yield from can be applied to an array or another generator. It replaces a loop and a yield call. The resulting syntax is shorter and faster.
<?php
foreach(foo() as $f) {
doSomething($f);
}
// using yield and a loop to yield all elements
function foo() {
foreach(goo() as $g) {
yield $g;
}
}
// using yield from to yield all elements
function foo2() {
yield from goo();
}
function goo() : array {
return [1,2,3];
}
?>
Alternatives :
- Use yield from keyword and shorten the syntax
Use Enum Case In Constant Expression
[Since 2.5.3] - [ -P Php/UseEnumCaseInConstantExpression ] - [ Online docs ]
Enum cases are constants, and may be used in constant definitions, as value. This is valid both with the case itself, or with their value, for the backed enum version.
<?php
enum A {
case A;
}
enum B : string {
case B = 'b';
}
class C {
const C1 = A::A;
const C2 = B::B->value;
}
?>
Readonly Property Changed By Cloning
[Since 2.5.3] - [ -P Php/ReadonlyPropertyChangedByCloning ] - [ Online docs ]
Readonly properties may be changed when cloning. This may happen in the __clone magic method.
In that method, a new object is being created. It is acting like a constructor, and may tweak some of the values of the original object, before assigning them to the new object.
<?php
class x {
public readonly int $p;
function __construct($p) {
$this->p = $p;
}
function __clone() {
// This is possible in a clone, and only once
$this->p = $this->p + 1;
// This second call is not possible, as the property was set just above
$this->p = $this->p + 2;
}
}
$a = new x(1);
print_r(clone $a);
?>
New Dynamic Class Constant Syntax
[Since 2.5.3] - [ -P Classes/NewDynamicConstantSyntax ] - [ Online docs ]
PHP has a dedicated syntax to access dynamically class constant values. This was added in PHP 8.3. It prevents using the a call to the function constant()
<?php
class x {
const A = 1;
}
$a = 'A';
echo x::{$a}; // displays 1
?>
class_alias() Supports Internal Classes
[Since 2.5.3] - [ -P Php/ClassAliasSupportsInternalClasses ] - [ Online docs ]
class_alias() accepts internal classes as first argument. Until PHP 8.3, this feature was restricted to user-defined classes.
<?php
class_alias(stdClass::class, 'standardClass');
?>
Redeclared Static Variable
[Since 2.5.3] - [ -P Variables/RedeclaredStaticVariable ] - [ Online docs ]
Static variables shall be declared only once. It is forbidden in PHP 8.3 and later. It was silently allowed in PHP 8.2 and older.
<?php
function foo() {
static $a;
static $a;
}
?>
Alternatives :
- Keep the last static call
- Keep the first static call
Static Variable Can Default To Arbitrary Expression
[Since 2.5.3] - [ -P Php/StaticVariableDefaultCanBeAnyExpression ] - [ Online docs ]
Static variables can hold any type of PHP expression. Indeed, those are variables, so their value can be build from other variables, and even functioncalls.
This feature was introduced in PHP 8.3.
<?php
function foo($init) {
static $variable = foo($a);
return $variable++;
}
?>
Inherited Class Constant Visibility
[Since 2.5.3] - [ -P Interfaces/InheritedClassConstantVisibility ] - [ Online docs ]
Visibility of class constant must be public, even when overwritten.
This was not checked until PHP 8.3, where it is now a Fatal Error. When the interface and the class are defined in different files, the error appears at execution time.
<?php
interface i {
public const I = 1;
public const J = 2;
}
class x implements i {
// This should not be possible
private const I = 10;
public const J = 20;
}
?>
Alternatives :
- Set the constant visibility in the class to public
- Remove the visibility of the constant in the class
Final Traits Are Final
[Since 2.5.3] - [ -P Traits/FinalTraitsAreFinal ] - [ Online docs ]
A final method in a trait is also final when in its importing class. This means that the importing class may redefine it, but not the children.
<?php
trait t {
final function FFinal() {}
final function FNotFinalInClass() {}
function FNotFinal() {} // This is a normal method
}
class x {
use t;
function FNotFinalInClass() {}
}
class y extends x {
function FFinal() {} // This is KO, as it is final in the trait
function FNotFinalInClass() {} // This is OK, the class as priority
function FNotFinal() {}
}
?>
Typed Class Constants Usage
[Since 2.6.0] - [ -P Classes/TypedClassConstants ] - [ Online docs ]
Class constants may be typed with the usual types, like a property or an argument.
While it appears to be a paradox to give a type to a structure which as a static value, there are several situations where the type can be enforced:
- When the class constant is modified in a children class: the children class must use the same type as the parent.
- When the class constant is build with an expression
- When the class constant is build with another constant
<?php
class x {
const int A = 1;
}
?>
See also PHP RFC: Typed class constants and `Why Class Constants Should be Typed Append And Assign Arrays
[Since 2.6.1] - [ -P Arrays/AppendAndAssignArrays ] - [ Online docs ]
This rule reports arrays that are used both with append and direct index assignation. Read access are not considered here.
Array append and direct index assignation have different impact one on the other. In particular, assign a value explicitely and later append values may have an impact on one another.
<?php
$arrayAppend = array();
$arrayAppend[] = 1;
?>
Static Variable Initialisation
[Since 2.6.1] - [ -P Variables/StaticVariableInitialisation ] - [ Online docs ]
Static variables can be initialized like any other variable, straight from the static keyword. This was added in PHP 8.3.
Indeed, static variables are variables, so they shall be initialized with any value, another variable or a functioncall. This behavior is different from the static constant expression, where only a small set of operators and constants can be used.
<?php
function foo(int $a = 0) {
static $s = 1;
static $s2 = $a + 1;
}
?>
Static Methods Cannot Call Non-Static Methods
[Since 2.6.3] - [ -P Classes/StaticCannotCallNonStatic ] - [ Online docs ]
A static method cannot call a non-static method. The object context would be missing.
On the other hand, a method may call a static method, as the context is lost, but not useful.
Magic methods cannot be static, so they are out of this rule. This applies to the constructor, when called with parent::__construct() .
<?php
class x {
function foo() {}
static function ioo() {
// This syntax is valid within a class
// yet, the call is not possible
self::foo();
}
}
?>
Alternatives :
- Make the calling method non static too
- Remove the call to the non-static method
- Make the target method static
Untyped No Default Properties
[Since 2.6.2] - [ -P Classes/UntypedNoDefaultProperties ] - [ Online docs ]
This rule reports untyped properties without default value, that are not assigned at constructor time.
This means that these properties will be assigned later, and are now running the risk to be accessed before being written. This yields a warning, and, when the property get typed, event with mixed , a fatal error.
<?php
class x {
public $noTypeNoDefaultNoConstructor;
public $noTypeNoDefaultButConstructor;
function __construct() {
// property is defined in the constructor, so always defined
$this->noTypeNoDefaultButConstructor = 1;
}
function foo() {
// possible error here
return $this->noTypeNoDefaultNoConstructor;
}
}
?>
Trait Is Not A Type
[Since 2.6.2] - [ -P Traits/TraitIsNotAType ] - [ Online docs ]
A trait cannot be used for typing. It is used by a classes, and those classes should be used for typing.
<?php
trait t {}
// No way to provide an object of type t
function foo(t $t) {
}
?>
Alternatives :
- Use the classes that use the trait as type
- Provide an interface that matches the trait, and make the using classes implements it too
Cannot Use Append For Reading
[Since 2.6.3] - [ -P Structures/CannotUseAppendForReading ] - [ Online docs ]
The append operator [] is used to add a value to an array. It doesn't provide an existing value to read. Hence, the short assignement operators, or the increment ones should not be used with the append operator. For example, the coalesce operator yields an error when used with append.
<?php
$x = [];
$x[] = 1; // normal usage
$x[] += 2; // adds a 2, but should yield an error
$x[]++; // adds a 1, but should yield an error
// variations with -= *= &= etc.
$x[] ??= 4; // yields a fatal error
?>
Alternatives :
- Remove the short assignement and build a real expression on the right hand of the assignement to append
Void Is Not A Reference
[Since 2.6.2] - [ -P Functions/VoidIsNotAReference ] - [ Online docs ]
It is not possible to return by reference, in a method that is typed void. The returned value is a literal null .
<?php
function &foo() : void {}
?>
Alternatives :
- Remove the void type
- Remove the reference on the method
Can't Call Generator
[Since 2.6.3] - [ -P Functions/CanCallGenerator ] - [ Online docs ]
It is not possible to call directly a generator: a generator is a method that uses the yield or yield from keyword.
Such structure shall be used directly in a foreach() structure, or with the function iterator_to_array()
<?php
function foo() {
echo __FUNCTION__;
yield 1;
}
// Won't display anything, even 'foo'
foo();
// displays both foo and 1
foreach(foo() as $g) {
print $g;
}
?>
Non Integer Nor String As Index
[Since 2.6.2] - [ -P Structures/NonIntStringAsIndex ] - [ Online docs ]
Report usage of non-integer and non-string types as index in an array syntax.
PHP arrays only accept integers and strings as keys. PHP convert the other types to integer or string, and that may lead to surprises when reading the arrays.
<?php
function foo (float $index, array $array) {
$array[$index];
}
?>
Cant Instantiate Non Class
[Since 2.6.2] - [ -P Classes/CantInstantiateNonClass ] - [ Online docs ]
It is not possible to instantiate anything else than a class. Interfaces, enumerations and traits cannot be instantiated.
<?php
class c {}
$object = new c;
trait t {}
new t;
?>
Check After Null Safe Operator
[Since 2.6.4] - [ -P Classes/CheckAfterNullSafeOperator ] - [ Online docs ]
Null-safe operator is ?-> , which prevents fatal errors in case the object of the call is NULL. The execution continues, though the result of the expression is now NULL too.
While it saves some checks in certain cases, the null-safe operator should be followed by a check on the returned value to process any misfire of the method.
This analysis checks that the result of the expression is collected, and compared to null.
<?php
$result = $object?->foo();
if ($result === null) {
throw new ObjectException(The object could not call $foo\n);
}
?>
Alternatives :
- Collect and check the result of the expression to null
- Remove the null-safe operator and check before calling the object's method or property
No Null With Null Safe Operator
[Since 2.6.4] - [ -P Classes/NoNullWithNullSafeOperator ] - [ Online docs ]
When building an expression with a null-safe operator, it may fail and produce a NULL as a result. When the last method of the expression also returns null (or void, which is transformed in null), then it is not possible to differentiate between a failure and a valid execution of the method.
As such, it is recommended to avoid finishing with a method that returns null, in an expression that uses a null-safe operator.
<?php
class x {
function foo($a) : ?int {
if ($a % 2) {
return $a;
} else {
return null;
}
}
}
$x = x::getInstance(x::class);
$result = $x?->foo($a);
// Is that an error or a valid result ?
if ($result === null) { }
?>
Alternatives :
- Avoid using the null-safe operator in that expression
- Make the last property / method in the expression not return null
Invalid Cast
[Since 2.6.4] - [ -P Structures/InvalidCast ] - [ Online docs ]
Some cast operations not permitted.
- (string) on an object whose class doesn't have a __toString method
- (int) on any object, except certain PHP native ones
- (string) on an array: this will produce the Array string, which is useless.
<?php
class Foo {}
(string) new Foo(); // Error
print (string) array(); // Array
?>
New Object Then Immediate Call
[Since 2.6.4] - [ -P Classes/NewThenCall ] - [ Online docs ]
This rule reports immediate calls on a new object. This can be simplified with a parenthesis structure, including with the assignation inside the parenthesis.
It is also being discussed to drop the parenthesis altogether.
<?php
$a = new Foo();
$a->bar();
($a = new Foo())->bar();
?>
Alternatives :
- Condense the two expressions into one
Wrong Precedence In Expression
[Since 2.6.4] - [ -P Structures/WrongPrecedenceInExpression ] - [ Online docs ]
These operators are not executed in the expected order. Coalesce and ternary operator have lesser precedence compared to comparisons or spaceship operators.
Thus, the comparison is executed first, and the other operator later.
It is recomended to use parenthesis in these cases.
Note that this may behave as expected, with a bit of clever placing boolean: see last example.
<?php
// This
if ($a ?? 1 == 2) {}
// is equivalent to
if ($a ?? (1 == 2)) {}
// It is different from
if (($a ?? 1) == 2) {}
// This one is also wrong, but falls back on correct values
if ($a ?? false === true) {}
?>
Alternatives :
- Add parenthesis around the coalesce operator
Only Variable Passed By Reference
[Since 2.6.4] - [ -P Php/OnlyVariablePassedByReference ] - [ Online docs ]
Some methods require a variable as argument. Those arguments are passed by reference, and they must operate on a variable, or any data container (property, array element).
This means that literal values, constants cannot be used as argument. This is also the case of literal values, returned by other methods.
This is also the case of isset() , althought with a different error message.
<?php
echo end([1,2,3]);
function foo() {
return [4,5,6];
}
echo end(foo());
?>
Alternatives :
- Put the value in a variable before using it with the function.
File_Put_Contents Using Array Argument
[Since 2.6.5] - [ -P Structures/FilePutContentsDataType ] - [ Online docs ]
file_put_contents() accepts a second argument as an array, and stores it in the file with an implicit implode. This is a documented behavior, though it is rarely used.
<?php
file_put_contents('/tmp/file.txt', [1, 2, 3, 4]);
print file_get_contents('/tmp/file.txt');
// displays 1234
?>
See also `file_put_contents() Nested Match
[Since 2.6.5] - [ -P Structures/NestedMatch ] - [ Online docs ]
Nested match calls makes the code difficult to read. It is recommended to avoid nesting match calls.
<?php
$a = match($b) {
1 => 3,
3 => 'ab',
5 => match($c) {
6 => new X,
7 => [],
}
default => false,
};
?>
Alternatives :
- Merge the two match() in one.
- Replace the nested match call by a method call.
Useless Short Ternary
[Since 2.6.5] - [ -P Structures/UselessShortTernary ] - [ Online docs ]
The short ternary operates on empty or null values. When the type of the condition is not false, boolean or null, the operator is useless.
<?php
function foo() : A { return new A; }
// This is useless
$b = foo() ? 1;
?>
Alternatives :
- Remove the ternary operator
- Refactor the types to allow for empty values
Empty Json Error
[Since 2.6.6] - [ -P Structures/EmptyJsonError ] - [ Online docs ]
json_last_error() keeps the last error that was generated while decoding a JSON string. To reset this cache to empty, one must run a call to json_decode() that succeed. This leads some code to make an apparently pointless call, just to empty the error cache, and avoid confusing the message with the one of a previous call.
<?php
// This generates an error
$json = json_decode([);
$json = json_decode($valid_json);
echo json_last_error(); // This error is confused for the last call, not the first one.
// pointless call, except to empty the cache.
$json = json_decode([]);
$json = json_decode($valid_json);
echo json_last_error(); // This error is dedicated to the last call
?>
Useless Coalesce
[Since 2.6.6] - [ -P Structures/UselessCoalesce ] - [ Online docs ]
The ?: operator needs the condition to be potentially empty. This means that the type should have the possibility to be null, false, 0, or any of the empty values.
<?php
function foo(A $a, bool $b) {
$a ?: 'a';
$b ?: 'a';
}
?>
Alternatives :
- Remove the operator.
- Extend the type to include values that may be empty.
Count() Is Not Negative
[Since 2.6.6] - [ -P Structures/CountIsNotNegative ] - [ Online docs ]
This rule reports when the Countable method count is poised to return a negative value.
It also reports when a call to count()
<?php
// count() shall not be below 0, so === is preferable here
if (count($array) <= 0) { }
?>
Exit Without Argument
[Since 2.6.6] - [ -P Php/ExitNoArg ] - [ Online docs ]
This rule reports usage of die and exit without arguments, nor parenthesis. These commands are not functions, and are allowed to be used without parenthesis: by default, they use the 0 status.
<?php
exit;
die;
?>
PHP 8.1 New Types
[Since 2.6.6] - [ -P Php/Php81NewTypes ] - [ Online docs ]
This rule reports usage of the new PHP 8.1 types. This is the `never` type.
This type is actually only available in return types in methods. This type is not available before version 8.1: as it was not a reserved keyword, it might be used with a class.
<?php
function foo() : never {
die();
}
?>
PHP 8.2 New Types
[Since 2.6.6] - [ -P Php/Php82NewTypes ] - [ Online docs ]
This rule reports usage of the new PHP 8.2 types. This is the `true` type.
This type is not available before version 8.2.
<?php
function foo() : true {
return true;
}
?>
Strpos() Less Than One
[Since 2.6.6] - [ -P Structures/StrposLessThanOne ] - [ Online docs ]
This rule reports a comparison of strpos() or stripos() with 1. This is a variable of strpos() == 0, since both false and 0 are processed the same way. Yet, 0 might be a valid value.
This rule was suggested by Yann Ouche.
<?php
// this works both when $a starts with .
// and when the . is not in the string.
if (strpos($a, '.') < 1) {
}
?>
Alternatives :
- Make sure that the 2 cases are valid business cases.
Static Inclusions
[Since 2.6.7] - [ -P Structures/StaticInclude ] - [ Online docs ]
This rule reports all static inclusion. A inclusion is static when it relies only on constants, such as literals, global and class constants, and the magic constants.
This rule is a collaboration with Bohuslav Šimek.
<?php
// a static inclusion
include __DIR__.'/lib/source.php';
$include = '/lib/helpers.inc';
include $include;
?>
Constant Used Only Once
[Since 2.6.7] - [ -P Constants/ConstantUsedOnce ] - [ Online docs ]
This rule reports constants that are used only once. Constants that are used only once may be replaced by they literal value, unless future use is expected.
This rule works on global and class constants.
<?php
const ONCE = 1;
echo ONCE;
?>
Alternatives :
- Use the constant more than once.
- Replace the constant with a literal value.
Useless Override Attribute
[Since 2.6.8] - [ -P Attributes/UselessOverride ] - [ Online docs ]
The override attribute is only useful on an extended class. It allows to mark a method that must be overriding a parent method. When the class is not extending another class, there is no point in using this attribute.
This is also valid with extended class : the top parent class has no use of the override.
There is a dedicated compile-time error.
<?php
class x {
// override in that trait are useless
use t;
// This is useless
#[Override]
function foo() {}
}
trait t {
// This is useless if loaded in a root class
#[Override]
function foo() {}
}
enum e {
// This is useless in any case
#[Override]
function foo() {}
}
?>
Neos
[Since 2.6.8] - [ -P Vendors/Neos ] - [ Online docs ]
Neos CMS allows users to easily create, manage and optimize content that is loved. Neos is a free enterprise web content management system licensed under the GPL.
Cakephp
[Since 2.6.8] - [ -P Vendors/Cakephp ] - [ Online docs ]
This rules reports when code is based on the CakePHP framework.
<?php
namespace Authentication;
use Cake\Core\ObjectRegistry;
abstract class AbstractCollection extends ObjectRegistry
{
}
?>