tech-tips/Языки программирования/PHP/Зачем нужен static при объявлении анонимных функций - Хабр.md

90 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[https://habr.com/ru/post/561550/](https://habr.com/ru/post/561550/)
Буквально на днях пришел вопрос от одного из подписчиков касательно [одного из постов моего telegram канала](https://t.me/beerphp/14). Его смутил вот такой кусок кода
```
<?php
usort($firstArray, static function($first, $second) {
return $first <=> $second;
});
```
Вопрос звучал так:
> Зачем делать callbackи в функции сортировки (usort), статическими?
И я подумал, что это действительно хороший вопрос, на который стоит обратить внимание.
### В чем проблема?
Начнем с определения из документации, чтобы засинхронизироваться:
> Анонимные функции, также известные как замыкания (closures), позволяют создавать функции, не имеющие определённых имён. Они наиболее полезны в качестве значений callable-параметров, но также могут иметь и множество других применений.
>
> Анонимные функции реализуются с использованием класса [Closure](https://www.php.net/manual/ru/class.closure.php).
Там-же, но это почти никто не читает :
> При объявлении в контексте класса, текущий класс будет автоматически связан с ним, делая $this доступным внутри функций класса. Если вы не хотите автоматического связывания с текущим классом, используйте статические анонимные функции.
Выходит, что когда **Сlosure** объявляется в контексте класса, то класс автоматически привязывается к замыканию. Это означает, что `$this` доступен внутри области анонимной функции:
![[c82d78e025851c7b3a33c478c884ed35.png]]
На первый взгляд "да и чёрт с ним", но стоит копнуть чуть глубже.
Замыкание, содержащее ссылку на **$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](https://t.me/beerphp). Подписывайся и сможешь получить больше интересного материала.