Начиная с версии 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;
}
}
?>
На что в этом коде нужно обратить внимание:
- Используя замыкания можно передавать в метод (функцию) или возвращать из него, фрагмент готового к исполнению кода, с локальными переменными из того окружения где этот код объявлен.
- Если код, который мы передаем в метод для отложенного выполнения, должен возвращать данные — не забываем про return в замыкании.
- Вместе с фрагментом кода, через замыкание, можно передать и все необходимые переменные из того контекста, где этот код используется, используя ключевое слово use — это принципиальное отличие объявления анонимной функции в PHP 5.3 от использования create_function() в более ранних версиях.
- Переменные импортируются в замыкание по значению, поэтому если какую-либо переменную нужно внутри замыкания изменять (например счетчик $dbQueryCounter), то её нужно импортировать по ссылке.
- Анонимные функции в PHP5.3 являются экземплярами класса Closure — это обстоятельство можно использовать для контроля типа переданной в метод переменной.
Автор: bardex