Начиная с версии 5.3, PHP позволяет создавать замыкания. К сожалению, пример их использования в официальной документации http://www.php.net/manual/en/functions.anonymous.php#example-163 (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); // не забываем return }; 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 — это обстоятельство можно использовать для контроля типа переданной в метод переменной.
