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

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

Ответ 1

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:)

Ответ 2

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

Например, если вы читаете конфигурационный файл, и один из параметров говорит вам, что hash_method для вашего алгоритма multiply, а не square, вы можете создать закрытие, которое будет использоваться везде, где вам нужно хеш-то.

Закрытие может быть создано (например) config_parser(); он создает функцию под названием do_hash_method(), используя переменные local to 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 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 not in the local scope anymore
    $val = do_hash_method($val)
}

Ответ 3

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

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

Ответ 4

Мне нравится контекст, предоставленный сообщением troelskn. Когда я хочу сделать что-то вроде примера Dan Udey на 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

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

Ответ 5

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

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

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

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

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

Ответ 6

Вот примеры закрытия в 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