When PHP Inspections suggested to change my anonymous function in array_map
to a static one, I thought: why bother? 🤷♂️ It seemed like a small style preference – not something that would really matter.
But I got curious. 🔍 Could adding static
to a function actually make your code faster or use less memory? That simple question led me down a rabbit hole 🕳️ of Pull Requests, Stack Overflow threads, and performance benchmarks.
This blog post tells the story of what I found – and why using static
might be a small habit that makes a big difference. 💡
Pull Request That Started It All
While digging around, I found a GitHub Pull Request in the Ocramius/GeneratedHydrator
repository. In it, the author replaced normal anonymous functions with static ones – and claimed the change resulted in around 15% faster execution. ⚡
This wasn’t a micro-optimisation just for fun. According to the author, removing access to $this
(which static closures do by default) helped PHP handle memory and execution more efficiently. 🧠
It was the first sign that this so-called "style suggestion" might actually lead to meaningful performance improvements. 🚀
Why $this
Matters in Closures
Digging deeper, I found a great explanation on Stack Overflow. It turns out that one key difference with static closures is that they do not bind to the current object context — meaning they don’t carry around a reference to $this
. 🧩
Why does that matter?
"The closure holding a reference to
$this
might prevent garbage collection of that object, which in turn may also impact performance significantly."
– Stack Overflow
In simpler terms: if a closure keeps a reference to its parent object, PHP might not free up that memory as quickly. 🐘 Over time, this can slow things down and eat up resources — especially if you’re creating a lot of closures in a loop.
That’s exactly what the next example demonstrates. 🔬
A Simple Benchmark That Proves the Point
To see the difference for myself, I recreated a script shared on Stack Overflow. It creates many instances of a class and stores closures returned by a method — all without needing $this
. 🛠️
Original source: Stack Overflow
class LargeObject {
protected $array;
public function __construct() {
$this->array = array_fill(0, 2000, 17);
}
public function getItemProcessor(): Closure {
return static function () {
// some logic that doesn’t use $this
};
}
}
$start = microtime(true);
$processors = [];
for ($i = 0; $i < 2000; $i++) {
$lo = new LargeObject();
$processors[] = $lo->getItemProcessor();
}
$memory = memory_get_usage() >> 20;
$time = (microtime(true) - $start) * 1000;
printf("This took %dms and %dMB of memory\n", $time, $memory);
I ran this with and without the static
keyword using PHP 8.4 and measured the results with PHPBench. 📊 Here’s what I got:
+----------------------------+-----+-------------+---------+---------+
| subject | its | memory_real | mode | mean |
+----------------------------+-----+-------------+---------+---------+
| benchUsingStaticKeyword | 100 | 2.10mb | 3.69ms | 3.70ms |
| benchNotUsingStaticKeyword | 100 | 71.30mb | 17.03ms | 17.17ms |
+----------------------------+-----+-------------+---------+---------+
That’s not a small gap – that’s more than 30x more memory and over 4x longer execution time without static
. 😳
This convinced me: using static
where $this
isn’t needed is not just a style thing. It’s a performance win. 🏆
Automating It with Rector
After seeing how much of a difference static
can make, I started looking through my own project. 🧑💻 There were many closures that didn’t use $this
and could be made static. But going through them one by one? That would take ages. 😩
So I turned to an old favourite: Rector – a tool for automated PHP refactoring. 🤖
It turns out Rector already has two rules that do exactly what I needed:
I added them to my Rector config and ran the tool on the whole codebase. It worked beautifully – all eligible closures were made static automatically. ✨
To make this a permanent habit, I left the rules in the config. Now, every Pull Request runs Rector in dry-run
mode and fails if someone forgets to use static
where it’s possible. 🔄
✅ Performance boost? Check.
✅ No manual refactoring? Check.
✅ Team-wide consistency? Check.
Conclusion
What started as a minor suggestion from a code analysis tool turned into a valuable lesson in PHP performance. 📚
By switching to static
closures when $this
isn't needed, you:
- avoid holding unnecessary references, 🚫
- help PHP free memory more effectively, 🧹
- and get faster execution times. ⏱️
It’s a simple habit with real benefits – and thanks to tools like Rector, it’s easy to make it stick. 🧰
Next time you’re writing a closure, stop and ask: do I need $this
here? If not – make it static. ✅
Top comments (3)
It is in the PHP documentation, go here and just scroll up.
I think it is a strange default. Like you mentioned there are array but also sort functions that have a callable as an argument. Functions like that will never need the binding of the parent class.
Also using
$this
in a function is just weird, it is a function not a method.PS: remove the emojis, they make reading the text more difficult. There are some that make no sense.
Hi David,
Thanks so much for pointing out the relevant PHP documentation!
On the second part of your comment - I think it really depends on the context. If things get more complex, it can make sense to extract the logic into a separate method or service. In those cases, using a non-static function might actually be the better option
For an arrow function I get the binding, because there the outside variables can be used.
But for an anonymus function
use
is needed to get outside variables into the function.It is inconsistent behaviour.