Замыкания в PHP ... что это такое и когда вам нужно их использовать? - PullRequest
77 голосов
/ 29 сентября 2008

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

Ответы [ 7 ]

75 голосов
/ 30 сентября 2008

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 лямбды и замыкания

15 голосов
/ 29 сентября 2008

Помимо технических деталей, замыкания являются фундаментальной предпосылкой для стиля программирования, известного как функционально-ориентированное программирование. Замыкание примерно используется для того же, что и объект, в котором вы используете объектно-ориентированное программирование; Он связывает данные (переменные) вместе с некоторым кодом (функцией), который затем можно передать куда-то еще. Таким образом, они влияют на то, как вы пишете программы, или - если вы не меняете способ написания своих программ - они вообще не оказывают никакого влияния.

В контексте PHP они немного странные, так как PHP уже сильно зависит от объектно-ориентированной парадигмы, основанной на классе, а также от более старой процедурной. Обычно языки, которые имеют замыкания, имеют полную лексическую область. Чтобы поддерживать обратную совместимость, PHP не получит этого, так что это означает, что замыкания здесь будут немного отличаться от других языков. Я думаю, нам еще предстоит точно определить, как они будут использоваться.

15 голосов
/ 29 сентября 2008

Когда вам понадобится функция в будущем, которая выполняет задание, которое вы определили сейчас.

Например, если вы читаете файл конфигурации и один из параметров говорит вам, что 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)
}
10 голосов
/ 29 сентября 2008

Мне нравится контекст, предоставленный постом 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

конечно, если вы хотите, чтобы он был доступен везде, вы можете просто сделать все статичным ...

2 голосов
/ 25 августа 2014

Закрытие - это, в основном, функция, для которой вы пишете определение в одном контексте, но выполняете в другом контексте. Javascript очень помог мне с их пониманием, потому что они повсеместно используются в JavaScript.

В PHP они менее эффективны, чем в JavaScript, из-за различий в области действия и доступности «глобальных» (или «внешних») переменных изнутри функций. Тем не менее, начиная с PHP 5.4, замыкания могут обращаться к объекту $ this при запуске внутри объекта, что делает их намного более эффективными.

Это и есть замыкания, и этого должно быть достаточно, чтобы понять, что написано выше.

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

Если это не очень понятно, не волнуйтесь, это станет ясно, как только вы начнете их использовать.

1 голос
/ 04 марта 2019

В сущности, 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;
0 голосов
/ 28 августа 2017

Вот примеры для замыканий в php

// Author: HishamDalal@gamil.com
// 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...