Буквально на днях пришел вопрос от одного из подписчиков касательно одного из постов моего telegram канала. Его смутил вот такой кусок кода
<?phpusort($firstArray, static function($first, $second) { return $first <=> $second;});
Звучал он так:
Зачем делать callbackи в функции сортировки (usort), статическими?
И я подумал, что это действительно хороший вопрос, на который стоит обратить внимание.
В чем проблема?
Начнем с определения из документации, чтобы засинхронизироваться:
Анонимные функции, также известные как замыкания (closures), позволяют создавать функции, не имеющие определённых имён. Они наиболее полезны в качестве значенийcallable-параметров, но также могут иметь и множество других применений.
Анонимные функции реализуются с использованием классаClosure.
Там-же, но это почти никто не читает :
При объявлении в контексте класса, текущий класс будет автоматически связан с ним, делая $this доступным внутри функций класса. Если вы не хотите автоматического связывания с текущим классом, используйте статические анонимные функции.
Выходит, что когда Сlosure объявляется в
контексте класса, то класс автоматически привязывается к замыканию.
Это означает, что $this
доступен внутри области
анонимной функции:

<?phpclass ExampleTest extends TestCase{ public function testBasicTest(): void { $array = [2, 1]; usort($array, function ($first, $second) { var_dump($this); return $first <=> $second; }); self::assertTrue(true); }}
На первый взгляд "да и чёрт с ним", но стоит копнуть чуть глубже.
Замыкание, содержащее ссылку на $this, может быть не обработано сборщиком мусора, что, в свою очередь, может существенно повлиять на производительность.
Вот пример без использования static:
<?php class LargeObject { protected $array; public function __construct() { $this->array = array_fill(0, 2000, 15); } public function getItemProcessor(): Closure { return function () { // Внутри функции любые вычисления $a = 1; $b = 2; return $a + $b; }; }}function getPeakMemory(): string{ return sprintf('%.2F MiB', memory_get_peak_usage() / 1024 / 1024);}$start = microtime(true);$processors = [];for ($i = 0; $i < 2000; $i++) { $lo = new LargeObject(); $processors[] = $lo->getItemProcessor();}var_dump(getPeakMemory());
Как результат, мы получим string(10) "134.10
MiB"
Но в случае, если мы добавим static в 11
строке, то потребление памяти составит string(8) "1.19
MiB"
Всё потому, что в processors[]
мы продолжаем
накапливать массив, внутри которого находятся
Сlosures которые связаны с классом, а значит,
содержат все те данные, которые в нём хранятся.
Выводы
Если подвести короткий итог, то анонимные функции без static стоит использовать если вам необходимо привязать объект к области видимости выполнения функции. Во всех остальных случаях можно и нужно использовать static, как минимум, чтобы случайно не выстрелить себе в ногу.
P.S.
Часто для полноценного поста на Хабре мало короткой заметки. Такие выдержки я публикую в своем телеграм-канале https://t.me/beerphp. Подписывайся и сможешь получить больше интересного материала ;)