Зачем нужен static при объявлении анонимных функций?

—

от автора

Буквально на днях пришел вопрос от одного из подписчиков касательно одного из постов моего telegram канала. Его смутил вот такой кусок кода

<?php  usort($firstArray, static function($first, $second) {     return $first <=> $second; });

Вопрос звучал так:

Зачем делать callback’и в функции сортировки (usort), статическими?

И я подумал, что это действительно хороший вопрос, на который стоит обратить внимание.

В чем проблема?

Начнем с определения из документации, чтобы засинхронизироваться:

Анонимные функции, также известные как замыкания (closures), позволяют создавать функции, не имеющие определённых имён. Они наиболее полезны в качестве значений callable-параметров, но также могут иметь и множество других применений.

Анонимные функции реализуются с использованием класса Closure.

Там-же, но это почти никто не читает :

При объявлении в контексте класса, текущий класс будет автоматически связан с ним, делая $this доступным внутри функций класса. Если вы не хотите автоматического связывания с текущим классом, используйте статические анонимные функции.

Выходит, что когда Сlosure объявляется в контексте класса, то класс автоматически привязывается к замыканию. Это означает, что $this доступен внутри области анонимной функции:

Код, чтобы протестить самостоятельно
<?php  class 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. Подписывайся и сможешь получить больше интересного материала.

ссылка на оригинал статьи https://habr.com/ru/post/561550/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *