Чем статическое замыкание (static closure) отличается от обычного в PHP?

Ответ

Основное отличие — статическое замыкание не имеет доступа к псевдопеременной $this и не захватывает контекст объекта, в котором оно было объявлено. Ключевое слово static перед function накладывает это ограничение.

Обычное замыкание автоматически захватывает $this, если объявлено в контексте метода объекта:

class Logger {
    private string $prefix = 'LOG:';

    public function getClosure(): Closure {
        // Обычное замыкание ЗАХВАТЫВАЕТ $this
        return function ($message) {
            // Имеет доступ к private-свойству $this->prefix
            echo $this->prefix . ' ' . $message;
        };
    }
}

$logger = new Logger();
$closure = $logger->getClosure();
$closure('Test'); // Вывод: LOG: Test

Статическое замыкание не может использовать $this, даже если создано внутри метода:

class Logger {
    private string $prefix = 'LOG:';

    public function getStaticClosure(): Closure {
        $prefix = $this->prefix; // Нужно явно захватить значение
        // Статическое замыкание НЕ ЗАХВАТЫВАЕТ $this
        return static function ($message) use ($prefix) {
            // echo $this->prefix; // ФАТАЛЬНАЯ ОШИБКА: Using $this when not in object context
            echo $prefix . ' ' . $message; // Работает, т.к. $prefix захвачена явно
        };
    }
}

Зачем это нужно?

  1. Явное указание намерений: Код показывает, что замыкание не зависит от состояния объекта.
  2. Предотвращение утечек памяти: Поскольку нет ссылки на порождающий объект ($this), этот объект может быть удалён сборщиком мусора, даже если замыкание ещё существует. В обычном замыкании объект будет жить, пока живо замыкание.
  3. Использование в статическом контексте: Статические замыкания можно безопасно использовать в статических методах или там, где контекст объекта не определён.
  4. Оптимизация: Небольшой выигрыш в производительности, так как не требуется обработка контекста объекта.

Вывод: Используйте static closure, когда ваша анонимная функция является чистой (не зависит от состояния объекта) или должна пережить объект, в котором создана.

Ответ 18+ 🔞

Э, слушай, вот тебе история про замыкания в PHP, такая, что даже ядрёна вошь позавидует. Представь, что обычное замыкание — это как наёмный работяга, который приходит на завод и сразу хватает со стола начальника ($this) его личную кружку, папки и ключи от кабинета. Ему всё доступно, он в теме. Но если начальника уволят, работяга всё равно держится за его старые вещи и не даёт их выкинуть — утечка памяти, ёпта.

А статическое замыкание — это уже другой тип, полупидор такой, дисциплинированный. Ему заранее говорят: «Смотри, чувак, тебе дадут только вот этот конкретный кусок информации ($prefix), а лезть в карман к начальнику за его личными штуками ($this->prefix) — низя, получишь фейспалм в виде фатальной ошибки».

Вот смотри на живых примерах, тут всё понятно станет.

Обычное замыкание: халявщик, который всё хапнул

class Logger {
    private string $prefix = 'LOG:';

    public function getClosure(): Closure {
        // Этот чувак автоматом, по-тихому, прихватизирует $this
        return function ($message) {
            // И спокойно лезет в его приватные владения. Наглец.
            echo $this->prefix . ' ' . $message;
        };
    }
}

$logger = new Logger();
$closure = $logger->getClosure();
$closure('Test'); // Вывод: LOG: Test. Всё украл, всё работает.

Статическое замыкание: законопослушный бухгалтер

class Logger {
    private string $prefix = 'LOG:';

    public function getStaticClosure(): Closure {
        // Приходится явно выдать ему копию документа, иначе он не работает
        $prefix = $this->prefix;
        // А тут уже строгая инструкция: НИКАКОГО $this!
        return static function ($message) use ($prefix) {
            // echo $this->prefix; // Тут он попробует — и сразу труп. Фатальная ошибка.
            echo $prefix . ' ' . $message; // А так — пожалуйста, работай с тем, что дали.
        };
    }
}

И зачем, спрашивается, этот геморрой?

  1. Чтоб не было мути. Ты явно говоришь: «Эй, функция, ты независимая, тебе объект не нужен». Любой другой разработчик посмотрит на static и сразу поймёт — тут состояние объекта не юзается, всё чисто. Доверия ебать ноль к обычным замыканиям после этого.

  2. Чтоб мусор забирали вовремя. Это главная фишка. Если объект Logger больше не нужен, но обычное замыкание где-то болтается — объект будет жить вечно в его памяти, как призрак. Со статическим такого нет, нет ссылки на $this — и объект спокойно отправляется на свалку истории. Уборка на уровне.

  3. Для статичных штук. Попробуй вызвать обычное замыкание из статического метода — будет тебе хиросима, потому что $this там undefined. А статическое — всегда пожалуйста, оно от контекста объекта не зависит.

  4. Чуть-чуть быстрее. Ну да, там движку меньше думать, когда не надо привязываться к объекту. Мелочь, а приятно.

Короче, вывод простой, как три копейки: Юзай static closure, когда твоя анонимка — хитрая жопа, которая должна работать сама по себе, или когда хочешь, чтобы объект-родитель спокойно сдох, не оставляя после себя долгов в памяти. Всё остальное — на обычные замыкания.