Так что я программирую в красивой, современной объектно-ориентированной манере. Я регулярно использую различные аспекты ООП, которые реализует PHP, но мне интересно, когда мне нужно использовать замыкания. Есть ли там эксперты, которые могут пролить свет на то, когда было бы полезно реализовать закрытие?






Когда в будущем вам понадобится функция, которая будет выполнять задачу, которую вы определили сейчас.
Например, если вы читаете файл конфигурации, и один из параметров сообщает вам, что hash_method для вашего алгоритма - это multiply, а не square, вы можете создать замыкание, которое будет использоваться везде, где вам нужно что-то хешировать.
Замыкание может быть создано (например) в config_parser(); он создает функцию с именем do_hash_method(), используя локальные переменные config_parser() (из файла конфигурации). Каждый раз, когда вызывается do_hash_method(), он имеет доступ к переменным в локальной области видимости config_parser(), даже если он не вызывается в этой области.
Надеюсь, хороший гипотетический пример:
function config_parser()
{
// Do some code here
// $hash_method is in config_parser() local scope
$hash_method = 'multiply';
if ($hashing_enabled)
{
function do_hash_method($var)
{
// $hash_method is from the parent's local scope
if ($hash_method == 'multiply')
return $var * $var;
else
return $var ^ $var;
}
}
}
function hashme($val)
{
// do_hash_method still knows about $hash_method
// even though it's not in the local scope anymore
$val = do_hash_method($val)
}
Это плохой ответ. Это бессмысленное утверждение: «Когда в будущем вам понадобится функция, выполняющая задачу, которую вы выбрали сейчас».
Помимо технических деталей, замыкания являются фундаментальной предпосылкой для стиля программирования, известного как функционально-ориентированное программирование. Замыкание примерно используется для тех же целей, что и объект в объектно-ориентированном программировании; Он связывает данные (переменные) вместе с некоторым кодом (функцией), который затем можно передать в другое место. Как таковые, они влияют на способ написания программ или - если вы не измените способ написания программ - они вообще не имеют никакого влияния.
В контексте PHP они немного странны, поскольку PHP уже сильно зависит от классовой объектно-ориентированной парадигмы, а также от более старой процедурной парадигмы. Обычно языки, у которых есть замыкания, имеют полную лексическую область видимости. Для обеспечения обратной совместимости PHP не получит этого, поэтому это означает, что замыкания здесь будут немного другими, чем на других языках. Думаю, нам еще предстоит увидеть, как именно они будут использоваться.
Мне нравится контекст, представленный в сообщении troelskn. Когда я хочу сделать что-то вроде примера Дэна Удея на PHP, я использую шаблон стратегии OO. На мой взгляд, это намного лучше, чем вводить новую глобальную функцию, поведение которой определяется во время выполнения.
http://en.wikipedia.org/wiki/Strategy_pattern
Вы также можете вызывать функции и методы, используя переменную, содержащую имя метода в PHP, и это здорово. так что другой подход к примеру Дэна будет примерно таким:
class ConfigurableEncoder{
private $algorithm = 'multiply'; //default is multiply
public function encode($x){
return call_user_func(array($this,$this->algorithm),$x);
}
public function multiply($x){
return $x * 5;
}
public function add($x){
return $x + 5;
}
public function setAlgorithm($algName){
switch(strtolower($algName)){
case 'add':
$this->algorithm = 'add';
break;
case 'multiply': //fall through
default: //default is multiply
$this->algorithm = 'multiply';
break;
}
}
}
$raw = 5;
$encoder = new ConfigurableEncoder(); // set to multiply
echo "raw: $raw\n"; // 5
echo "multiply: " . $encoder->encode($raw) . "\n"; // 25
$encoder->setAlgorithm('add');
echo "add: " . $encoder->encode($raw) . "\n"; // 10
конечно, если вы хотите, чтобы он был доступен везде, вы можете просто сделать все статичным ...
PHP будет поддерживать замыкания изначально в версии 5.3. Замыкание хорошо, когда вам нужна локальная функция, которая используется только для какой-то небольшой конкретной цели. RFC для закрытия дает хороший пример:
function replace_spaces ($text) {
$replacement = function ($matches) {
return str_replace ($matches[1], ' ', ' ').' ';
};
return preg_replace_callback ('/( +) /', $replacement, $text);
}
Это позволяет вам определять функцию replacement локально внутри replace_spaces(), так что это не так:
1) Загромождение глобального пространства имен
2) Заставить людей через три года задуматься, почему существует функция, определенная глобально, которая используется только внутри одной другой функции
Он держит вещи в порядке. Обратите внимание, что у самой функции нет имени, она просто определена и назначена как ссылка на $replacement.
Но помните, вам нужно дождаться PHP 5.3 :)
Вы также можете получить доступ к переменным вне его области действия в замыкание с помощью ключевого слова use. Рассмотрим этот пример.
// Set a multiplier
$multiplier = 3;
// Create a list of numbers
$numbers = array(1,2,3,4);
// Use array_walk to iterate
// through the list and multiply
array_walk($numbers, function($number) use($multiplier){
echo $number * $multiplier;
});
Здесь дано отличное объяснение Что такое лямбды и замыкания в php
Это прекрасное объяснение. +1
Мне понравилось объяснение Почему, что вы будете использовать замыкания. Большинство людей этого не понимают. +1
Это объяснение анонимных функций, а не объяснение закрытий. Анонимные функции, как вы говорите, похожи на именованные функции, за исключением того, что они не являются глобальными. Замыкания, с другой стороны, представляют собой функции, содержащие свободные переменные с лексической областью видимости (объявленные с помощью "use"); т.е. они могут копировать и ссылаться на значения из области, в которой они объявлены, даже после того, как все остальное было собрано сборщиком мусора.
@Warbo Это правда; в то время я не особо ощутил разницу между анонимной функцией и закрытием. Замыкания действительно имеют смысл только после того, как вы изучили анонимные функции, но и по сей день Я все еще нахожу «объяснения» того, что такое замыкание (как и мое, от 7 лет назад ;-)), которые не объясняют его аспект области видимости.
Вот почему я сказал: проверьте JavaScript, где часто используются замыкания, но имейте в виду, что правила области видимости переменных в PHP разные.
Замыкание - это в основном функция, определение которой вы пишете в одном контексте, но запускаете в другом контексте. Javascript очень помог мне в их понимании, потому что они повсеместно используются в JavaScript.
В PHP они менее эффективны, чем в JavaScript, из-за различий в области видимости и доступности «глобальных» (или «внешних») переменных внутри функций. Тем не менее, начиная с PHP 5.4, замыкания могут обращаться к объекту $ this при запуске внутри объекта, что делает их намного более эффективными.
В этом суть замыканий, и этого должно быть достаточно, чтобы понять, что написано выше.
Это означает, что должна быть возможность написать где-нибудь определение функции и использовать переменную $ this внутри определения функции, затем присвоить определение функции переменной (другие дали примеры синтаксиса), а затем передать эту переменную объекту и вызывая его в контексте объекта, функция может затем обращаться к объекту и управлять им через $ this, как если бы это был просто еще один из его методов, хотя на самом деле он не определен в определении класса этого объекта, а где-то еще.
Если не очень понятно, то не волнуйтесь, станет ясно, как только вы начнете их использовать.
Честно говоря, мне это вообще непонятно даже мне, автору. В основном я говорю: чтобы узнать, что такое замыкание, проверьте их в JavaScript, но имейте в виду, что область видимости переменных в JavaScript и PHP различается.
Вот примеры закрытий в php
// Author: [email protected]
// Publish on: 2017-08-28
class users
{
private $users = null;
private $i = 5;
function __construct(){
// Get users from database
$this->users = array('a', 'b', 'c', 'd', 'e', 'f');
}
function displayUsers($callback){
for($n=0; $n<=$this->i; $n++){
echo $callback($this->users[$n], $n);
}
}
function showUsers($callback){
return $callback($this->users);
}
function getUserByID($id, $callback){
$user = isset($this->users[$id]) ? $this->users[$id] : null;
return $callback($user);
}
}
$u = new users();
$u->displayUsers(function($username, $userID){
echo "$userID -> $username<br>";
});
$u->showUsers(function($users){
foreach($users as $user){
echo strtoupper($user).' ';
}
});
$x = $u->getUserByID(2, function($user){
return "<h1>$user</h1>";
});
echo ($x);
Выход:
0 -> a
1 -> b
2 -> c
3 -> d
4 -> e
5 -> f
A B C D E F
c
По сути, Closure - это внутренние функции, которые имеют доступ к внешним переменным и используются как функция обратного вызова для анонимной функции (функции, у которых нет имени).
<?php
$param='ironman';
function sayhello(){
$param='captain';
$func=function () use ($param){
$param='spiderman';
};
$func();
echo $param;
}
sayhello();
?>
//output captain
//and if we pass variable as a reference as(&$param) then output would be spider man;
$param='captain' в функции sayhello() - это локальная переменная функции sayhello(). $param='ironman' выше sayhello() - глобальная переменная. Если вы хотите сделать в своем скрипте только одну переменную $ param, вы должны вызвать: global $param; в функции sayhello().
Закрытие:
У MDN есть лучшее объяснение IMO:
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function.
т.е. закрытие - это функция с доступом к переменным, которые находятся в родительской области. Замыкание позволяет нам удобно создавать функции на лету, поскольку в некоторых ситуациях функции нужны только в одном месте (обратные вызовы, вызываемые аргументы).
Пример:
$arr = [1,2,3,3];
$outersScopeNr = 2;
// The second arg in array_filter is a closure
// It would be inconvenient to have this function in global namespace
// The use keyword lets us access a variable in an outer scope
$newArr = array_filter($arr, function ($el) use ($outersScopeNr) {
return $el === 3 || $el === $outersScopeNr;
});
var_dump($newArr);
// array (size=3)
// 1 => int 2
// 2 => int 3
// 3 => int 3
Я не могу просто скопировать этот пример и запустить его. Предпочитаю пример, который я могу просто запустить.