Наглядный пример использования замыканий в PHP

в 7:55, , рубрики: closure, php, замыкания, Песочница, метки: , ,

Начиная с версии 5.3, PHP позволяет создавать замыкания. К сожалению, пример их использования в официальной документации http://www.php.net/manual/en/functions.anonymous.php (example 3) обладает редкой изощрённостью и надуманностью. Надеюсь, пример под катом поможет увидеть в замыканиях другое применение, кроме как с функциями типа array_map().

Самый распространённый метод повышения производительности приложения – это кэширование, и обычно схема его применения выглядит так:

<?php 
Class Cache
{
    static function set( $key, $value);
    static function get( $key );
}

Class PostModel
{
  static function getList($dateCreated);
}

$dateCreated = date('Y-m-d');
$posts = Cache::get("posts_" . $dateCreated);

if( !$posts ) {
   $posts = PostModel::getList($dateCreated);
   Cache::set( "posts_" . $dateCreated , $posts); 
}
?>

Пытаемся получить данные из кеша, если данные не найдены — делаем запрос к БД и пишем результат в кеш. Логика каждый раз почти одинаковая и хотелось бы написать универсальную обёртку для таких случаев, но как передавать в неё не просто переменную, а кусок кода, который должен выполняться уже внутри обёртки, т.е. отложено?

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

<?php 
$dateCreated = date('Y-m-d');
$dbQueryCounter = 0;
$fallback = function() use($dateCreated, &$dbQueryCounter) { 
   $dbQueryCounter++; //счетчик импортирован в замыкание по ссылке
   return PostModel::getList($dateCreated); 
} 
Cache::wrapper( "posts_" . $dateCreated , $fallback );

Class Cache
{
    static function set( $key, $value);
    static function get( $key );
    static function wrapper( $key, Closure $fallback ) {
        $data = self::get( $key );
        if( !$data ) {
            $data = $fallback(); // отложенное выполнение кода
            self::set( $key, $data); 
        }
        return $data;
    }
}
?>

На что в этом коде нужно обратить внимание:

  1. Используя замыкания можно передавать в метод (функцию) или возвращать из него, фрагмент готового к исполнению кода, с локальными переменными из того окружения где этот код объявлен.
  2. Если код, который мы передаем в метод для отложенного выполнения, должен возвращать данные — не забываем про return в замыкании.
  3. Вместе с фрагментом кода, через замыкание, можно передать и все необходимые переменные из того контекста, где этот код используется, используя ключевое слово use — это принципиальное отличие объявления анонимной функции в PHP 5.3 от использования create_function() в более ранних версиях.
  4. Переменные импортируются в замыкание по значению, поэтому если какую-либо переменную нужно внутри замыкания изменять (например счетчик $dbQueryCounter), то её нужно импортировать по ссылке.
  5. Анонимные функции в PHP5.3 являются экземплярами класса Closure — это обстоятельство можно использовать для контроля типа переданной в метод переменной.

Автор: bardex

Поделиться

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js