90 lines
4.8 KiB
Markdown
90 lines
4.8 KiB
Markdown
|
[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). Подписывайся и сможешь получить больше интересного материала.
|