PHP의 클로저… 정확히 무엇이며 언제 사용해야합니까?


82

그래서 저는 멋진 최신 객체 지향 방식으로 프로그래밍하고 있습니다. PHP가 구현하는 OOP의 다양한 측면을 정기적으로 사용하지만 언제 클로저를 사용해야할지 궁금합니다. 클로저를 구현하는 것이 유용 할 때를 밝힐 수있는 전문가가 있습니까?

답변:


82

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) 사람들이 3 년 동안 다른 함수 내에서만 사용되는 전역 적으로 정의 된 함수가 있는지 궁금해합니다.

그것은 일을 체계적으로 유지합니다. 함수 자체에 이름이 없는지 확인하십시오. 단순히에 대한 참조로 정의되고 할당됩니다 $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
David J Eddy

4
클로저를 사용 하는 이유에 대한 설명을 즐겼습니다 . 대부분의 사람들은 그것을 잘 이해하지 못합니다. +1
Carrie Kendall

10
이것은 클로저에 대한 설명이 아니라 익명 함수에 대한 설명입니다. 익명 함수는 말했듯이 전역이 아니라는 점을 제외하면 명명 된 함수와 같습니다. 반면 클로저는 어휘 범위의 자유 변수 ( "use"로 선언 됨)를 포함하는 함수입니다. 즉. 다른 모든 항목이 가비지 수집 된 후에도 선언 된 범위에서 값을 복사하고 참조 할 수 있습니다.
Warbo 2014-06-28

@Warbo 이것은 사실입니다. 당시에는 익명 함수와 클로저의 차이를 전혀 이해하지 못했습니다. 클로저는 익명 함수를 익힌 후에 만 ​​의미가 있지만 오늘날 까지도 클로저의 범위 측면을 설명하지 않는 클로저가 무엇인지에 대한 "설명"이 있습니다 (예 : 7 년 전 ;-)).
dirtside

그렇기 때문에 클로저가 많이 사용되는 JavaScript를 확인하십시오.하지만 PHP에서는 변수 범위 규칙이 다릅니다.
Rolf

17

현재 결정한 작업을 수행하는 기능이 미래에 필요할 때.

예를 들어, 구성 파일을 읽고 매개 변수 중 하나가 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)
}

이 예제를 복사하여 실행할 수는 없습니다. 간단히 실행할 수있는 예를 선호했습니다.
Kim Stacks

3
이 대답은 좋지 않습니다. 이것은 무의미한 말입니다. "앞으로 지금 결정한 작업을 수행하는 기능이 필요할 때."
키프로스에서 휴식

15

기술적 인 세부 사항 외에도 클로저는 함수 지향 프로그래밍으로 알려진 프로그래밍 스타일의 기본 전제 조건입니다. 클로저는 대략 객체 지향 프로그래밍에서 객체를 사용하는 것과 동일한 용도로 사용됩니다. 데이터 (변수)를 일부 코드 (함수)와 함께 바인딩 한 다음 다른 곳으로 전달할 수 있습니다. 따라서 프로그램 작성 방식에 영향을 주거나 프로그램 작성 방식을 변경하지 않으면 전혀 영향을주지 않습니다.

PHP의 맥락에서 PHP는 이미 클래스 기반, 객체 지향 패러다임 및 이전 절차 적 패러다임에 무겁기 때문에 약간 이상합니다. 일반적으로 클로저가있는 언어에는 전체 어휘 범위가 있습니다. 이전 버전과의 호환성을 유지하기 위해 PHP는 이것을 얻지 않을 것입니다. 따라서 여기서 클로저가 다른 언어와는 조금 다를 것입니다. 나는 그들이 어떻게 사용 될지 아직 정확히 보지 못했다고 생각합니다.


10

나는 troelskn의 게시물에서 제공하는 컨텍스트를 좋아합니다. PHP에서 Dan Udey의 예제와 같은 작업을하고 싶을 때는 OO 전략 패턴을 사용합니다. 제 생각에는 런타임에 동작이 결정되는 새로운 전역 함수를 도입하는 것보다 훨씬 낫습니다.

http://en.wikipedia.org/wiki/Strategy_pattern

PHP에서 메소드 이름을 포함하는 변수를 사용하여 함수와 메소드를 호출 할 수도 있습니다. 따라서 Dan의 또 다른 예는 다음과 같습니다.

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

클로저는 기본적으로 한 컨텍스트에서 정의를 작성하지만 다른 컨텍스트에서 실행하는 함수입니다. Javascript는 모든 곳에서 JavaScript에서 사용되기 때문에 이러한 것들을 이해하는 데 많은 도움이되었습니다.

PHP에서는 함수 내에서 "전역"(또는 "외부") 변수의 범위와 접근성이 다르기 때문에 JavaScript보다 덜 효과적입니다. 그러나 PHP 5.4부터 클로저는 객체 내에서 실행될 때 $ this 객체에 액세스 할 수 있으므로 훨씬 더 효과적입니다.

이것이 클로저에 관한 것이며 위에 쓰여진 내용을 이해하는 것으로 충분해야합니다.

즉, 어딘가에 함수 정의를 작성하고 함수 정의 내에서 $ this 변수를 사용한 다음 함수 정의를 변수에 할당 한 다음 (다른 사용자는 구문 예제를 제공함)이 변수를 객체에 전달할 수 있어야합니다. 객체 컨텍스트에서 호출하면 함수는 $ this를 통해 객체를 액세스하고 조작 할 수 있습니다. 마치 해당 객체의 클래스 정의에 정의되어 있지 않고 다른 곳에서 정의 된 메서드 중 하나 인 것처럼.

명확하지 않더라도 걱정하지 마세요. 사용하기 시작하면 명확 해집니다.


솔직히 나는 이것이 저자 인 나에게도 전혀 명확하지 않습니다. 기본적으로 나는 말하고있다 : 어떤 클로저가 자바 스크립트에서 체크 아웃되는지 배우기 위해 자바 스크립트와 PHP간에 변수 범위가 다르다는 것을 명심하십시오.
Rolf

1

기본적으로 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'in func sayhello()는 func 에 대한 지역 변수 sayhello()입니다. $param='ironman'sayhello()는 전역 변수입니다. 당신이 당신의 스크립트에 하나의 $의 PARAM 변수를 확인하려면 당신은 호출해야합니다 : global $param;sayhello()FUNC
vlakov

0

다음은 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

0

폐쇄 :

MDN은 IMO를 가장 잘 설명합니다.

클로저는 주변 상태 (어휘 환경)에 대한 참조와 함께 번들로 묶인 (포함 된) 함수의 조합입니다. 즉, 클로저는 내부 함수에서 외부 함수의 범위에 대한 액세스를 제공합니다.

즉, 클로저는 부모 범위에있는 변수에 접근 할 수있는 함수입니다. 어떤 상황에서는 함수가 한곳에서만 필요하기 때문에 클로저를 사용하면 즉석에서 편리하게 함수를 만들 수 있습니다 (콜백, 호출 가능한 인수).

예:

$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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.