3 tips for performant PHP code
Kristjan Siimson
Posted on July 7, 2020
Disclaimer
This is an advanced post about performance optimization. Don't blindly take this advice. Not all projects prioritize performance. If you are not familiar with the mentioned language features, take your time to understand them.
(1) Know your language constructs
Example
isset($array['key'])
over array_key_exists('key', $array)
Reasoning
Faster than function calls. There are two language constructs that particularly useful, which are isset()
and empty()
.
[Pitfalls] empty()
uses type juggling, see next section.
Opcodes
if(isset($array['key'])) {}
op | return | operands |
---|---|---|
ISSET_ISEMPTY_DIM_OBJ | ~1 | !0, 'key' |
JMPZ | ~1, ->2 |
if(array_key_exists('key', $array)) {}
op | return | operands |
---|---|---|
INIT_FCALL | 'array_key_exists' | |
SEND_VAL | 'key' | |
SEND_VAL | !0 | |
DO_ICALL | $2 | |
JMPZ | $2, ->5 |
(2) Don't fight the type system (but learn it first)
Example
if($arr) {}
over if(count($arr) > 0) {}
Reasoning
Using type juggling reduces redundant opcodes, and therefore also memory allocations.
[Pitfalls] PHP Magic Tricks: Type Juggling presentation by Insomnia Security outlines some example security exploits enabled by inappropriate use of type juggling. Perhaps the most surprising behavior is exhibited in string to integer conversion, e.g. "0e768261251903820937390661668547" == "0"
. This is intentional and well documented.
Opcodes
if($arr) {}
op | return | operands |
---|---|---|
JMPZ | !0, ->1 |
if(count($arr) > 0) {}
op | return | operands |
---|---|---|
COUNT | ~1 | !0 |
IS_SMALLER | ~2 | 0, ~1 |
JMPZ | ~2, ->3 |
(3) Clean up your variables
PHP does some nifty behind-the-scenes optimizations, and this tip is to let it do it’s job. One of the tricks PHP does is related to passing variables by value, rather than reference. PHP will optimize away assignments if the value is not assigned to any other variable, and the value doesn't change.
First example ✔️ -
<?php
$var = 'a';
function a($value) {
echo $value; // do something
}
a($var);
Here the value is assigned in the variable assignment. Even though the value is passed by value, there is actually only one assignment, because the value doesn't change.
Second example ⚠️ -
<?php
$var = ‘b’;
function a($value) {
$value = 'a';
echo $value; // do something
}
a($var);
Here the value changes and therefore there are two assignments, one in the main scope and another inside the function.
Third example ✔️ -
function a($value) {
$value = 'a';
echo $value; // do something
}
a('b');
In this example, the value is assigned just before calling a()
, and since nothing holds reference to it, PHP can mutate the value, instead of making another assignment.
Reasoning
This is not something to worry so much about when the values are small, but I’ve caught some OOM situations where the root cause was an useless variable. This also helps the garbage collector, which only cleans up the values that are not referenced by any variables.
Posted on July 7, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.