>  기사  >  백엔드 개발  >  PHP 익명 함수 및 클로저

PHP 익명 함수 및 클로저

WBOY
WBOY원래의
2016-08-08 09:26:101125검색

[iefreer]는 PHP 클로저 구문을 심도있게 설명하는 기사를 다시 게시했으며 이러한 새로운 구문을 능숙하게 적용하는 방법에 대한 기사도 다시 게시할 예정입니다.

익명 함수는 프로그래밍 언어에서 비교적 초기에 등장했는데, Lisp 언어에서 처음 등장했고 이후 많은 프로그래밍 언어에 이 기능이 생기기 시작했습니다.

현재는 Javascript가 널리 사용되고 있습니다. C#과 PHP는 5.3까지 익명 함수를 실제로 지원하지 않았으며 새로운 C++ 표준 C++0x에서도 이를 지원하기 시작했습니다.

익명 함수는 식별자를 지정하지 않고 호출할 수 있는 함수 또는 서브루틴 유형입니다. 익명 함수는 다른 함수에 매개변수로 편리하게 전달할 수 있습니다.

클로저

익명 함수의 경우 클로저를 언급해야 합니다. 클로저는 어휘 클로저(Lexical Closures)의 약자이며 자유 변수 함수(Free Variable Function)를 지칭하며 이 함수에도 적용된 자유 변수가 존재합니다. 클로저가 생성된 환경을 벗어나면 클로저는 함수와 관련 참조로 구성된 엔터티로 간주될 수도 있습니다. 일부 언어에서는 함수 내에서 다른 함수를 정의할 때 내부 함수가 외부 함수의 변수를 참조하면 클로저가 발생할 수 있습니다. 외부 함수를 실행하면 클로저가 형성됩니다.

이라는 단어는 익명 함수와 혼동되기 쉽습니다. 실제로는 두 가지 다른 개념입니다. 이는 많은 언어에서 익명 함수를 구현할 때 클로저 형성을 허용하기 때문일 수 있습니다.

"익명" 함수를 생성하려면 create_function()을 사용하세요

앞서 언급했듯이 익명 함수는 PHP5.3에서만 공식적으로 지원됩니다. 함수는 익명 함수를 생성할 수 있습니다: create_function 함수 이 함수는 설명서의 PHP4.1 및 PHP5에서 찾을 수 있습니다. 예를 들어 다음과 같이 일반적으로 익명 콜백 함수로 사용할 수도 있습니다. 🎜 >

[php] 일반 사본 보기

  1. >
  2. $array =
  3. 배열(1, 2, 3, 4) array_walk (
  4. $array, create_function('$value', 'echo $value')) 할 일이 더 많습니다. 그렇다면 이것이 진정한 익명 함수가 아닌 이유는 무엇입니까? 먼저 이 함수의 반환 값을 살펴보겠습니다. 일반적으로 다음과 같은 함수를 호출할 수 있습니다. [php] 일반 사본 보기

  5. >

    기능

    a() {
    1. 에코
    2. '함수 a'
    3. } $a =
    4. 'a'; $a();
    5. 콜백 함수를 구현할 때도 이 방법을 사용할 수 있습니다. 예:
    6. [php] 일반 사본 보기
    7. >기능 do_something($callback
    8. ) {


    //

     

    1.                             🎜>// 완료
    2. $callback
    3. ( ); }
    4. 이렇게 하면 $callback에 지정된 함수가 do_something 함수 다음에 호출될 수 있습니다. ()가 실행됩니다. create_function 함수의 반환 값으로 돌아가기: 함수는 고유한 문자열 함수 이름을 반환하거나 오류가 발생하면 FALSE를 반환합니다. 따라서 이 함수는 동적으로 함수를 생성할 뿐이며 이 함수에는 함수 이름이 있으므로 실제로는 익명이 아닙니다. 단지 전역적으로 고유한 기능을 생성하는 것뿐입니다. [php] 일반 사본 보기
      1. $func = create_function('', 'echo "동적으로 생성된 함수";')
      2. echo $func; //lambda_1
      3. $func() // 동적으로 생성된 함수
      4. $my_func = 'lambda_1' ;
      5. $my_func() // 이 함수는 존재하지 않습니다 🎜 >
      6. lambda_1() // 이 함수는 존재하지 않습니다


      위 코드의 첫 번째 부분은 create_function이 사용되는 방식이지만 나중에 함수 이름을 통해 호출하면 실패하므로 PHP가 이 함수를 어떻게 보장하는지 이해하기 어렵습니다. 전역적으로 고유합니까? Lambda_1을 참조하십시오. 매우 일반적인 함수 이름처럼 들립니다. 여기서 함수의 반환 문자열은 함수가 생성될 때까지 함수가 존재하는지 확인합니다. 그러나 create_function 다음에 Lambda_1이라는 함수를 정의하면 어떻게 될까요? 이렇게 하면 함수를 반복적으로 정의하는 문제가 발생할 수 있습니다. 사실 실제로는 Lambda_1이라는 함수를 정의하는 것이 최선의 방법은 아닐 것입니다. 제가 언급한 문제는 발생하지 않습니다. 무슨 일이 일어나고 있는 걸까요? 위 코드의 마지막 두 줄도 이 문제를 보여줍니다. 실제로 정의된 Lambda_1이라는 함수가 없습니다. <… echo하면 Lambda_1이 출력되고, 우리가 직접 입력한 것은 debug_zval_dump 함수입니다.

      [php] 일반 사본 보기

        $func
      1. = create_function('', 'echo "안녕하세요";')  
      2. $my_func_name
      3. = 'lambda_1' debug_zval_dump(
      4. $func
      5. ) 🎜>debug_zval_dump($my_func_name); // string(8) "lambda_1" refcount(2)
      6. 보세요, 길이가 실제로 다릅니다. 길이가 다르다는 것은 동일한 함수가 아니라는 것을 의미하므로, 물론 우리가 호출한 함수는 존재하지 않습니다. create_function 함수가 무엇을 하는지 살펴보겠습니다. 구현 보기: $PHP_SRC/Zend/zend_builtin_functions.c
      7. [php] 일반 사본 보기

      1. #define LAMBDA_TEMP_FUNCNAME    "__lambda_func"  
      2.    
      3. ZEND_FUNCTION(create_function)  
      4. {  
      5.     // ... 省去无关代码  
      6.     function_name = (char *) emalloc(sizeof("0lambda_")+MAX_LENGTH_OF_LONG);  
      7.     함수_이름[0] = ' ';  // <--- 这里  
      8.     하세요 {  
      9.         function_name_length = 1 + sprintf(function_name + 1, "lambda_%d", ++EG(lambda_count));  
      10.     } 동안 (zend_hash_add(EG(function_table), function_name, function_name_length+1, &new_function, sizeof(zend_function), NULL)= =실패);  
      11.     zend_hash_del(EG(function_table), LAMBDA_TEMP_FUNCNAME, sizeof(LAMBDA_TEMP_FUNCNAME));  
      12.     RETURN_STRINGL(function_name, function_name_length, 0);  
      13. }  


      该函数后,给函数起了个name字,它将函数이름이 붙은 이름은 ''也就是공자 이름입니다. 이름, 第一个字符为空字符의 결정 방법 式比较특히, 因为에서는 户代码中无法을 결정하는 것이 까다롭습니다.到这个特殊的函数之后,我们其实还是可以调用到这个函数的, 只要我们에서 函数name前加一个空字符就可以了, chr()函数可以帮我们生成这样的字, 例如前side创建的函数可以过如下的方式访问到:

      [php] 일반 사본 보기

      1.    
      2. $my_func = 문자(0) . "lambda_1";  
      3. $my_func(); // 안녕하세요  


      这种创建"匿name函数"的方式有一些缺点:

      1. 函数의 결정은 这就无법에 따라 평가됩니다.
      2. 质区别, 无法实现闭包的效果.

      PHP5.3에서 가장 확실한 이름은

      에서 PHP5.3에 대한 더 많은 정보를 제공합니다. __호출 魔幻机是: 当一个对象当做函数调用时候,对象定义了__invoke魔幻方法则这个函数会被调用,这和C++中的操作符重载有些类似, 例如可以image下face这样使用:

      [php] 일반 사본 보기

    5. 수업
    6.  Callme {      공개
    7.  기능 __invoke($phone_num) {          에코
    8.  "안녕하세요: $phone_num" ;      }  
    9. }  
    10.    
    11. $call
    12.  = new Callme();  $call
    13. (13810688888); // "안녕하세요: 13810688888  
    14. 匿name函数实现

    앞면介绍了将对象작동법, 聪明的你可能想到재PHP实现匿name函数象, PHP中의 匿name函数就的确是过这种式实现的。我们先来验证一下:

    [php] 일반 사본 보기

    1. $func = 함수 () {
    2. echo "안녕하세요, 익명 함수";
    3. }
    4. gettype($func); // 객체 echo get_class(
    5. $func) // 클로저 익명 함수는 그냥 평범한 클래스인 것으로 밝혀졌습니다. Javascript에 익숙한 학생들은 익명 함수의 사용에 매우 익숙합니다. PHP도 Javascript와 유사한 구문을 사용하여 정의합니다. 익명 함수는 실제로 클래스 인스턴스이므로 쉽게 할당할 수 있습니다. , Javascript에서는 객체의 속성에 익명 함수를 할당할 수 있습니다. 예:

    [php] 일반 복사 보기

    var

    a = {};
    1. a.call = 함수() {alert(
    2. "전화함");} a .call(); // 경고 호출
    3. 이는 Javascript에서는 매우 일반적이지만 PHP에서는 객체의 속성을 복사하는 것은 불가능합니다. 이렇게 하면 클래스가 클래스에 정의된 메서드를 검색하게 됩니다. 이는 PHP 클래스 모델에 의해 결정됩니다. 물론, PHP는 이 점에서 개선될 수 있다고 결정되었으며, 후속 버전에서는 이러한 호출을 허용하여 일부 기능을 더 쉽게 유연하게 구현할 수 있을 것입니다. 현재 이 효과를 얻을 수 있는 방법이 있습니다. 또 다른 마법 메서드인 __call()을 사용하는 것입니다. 이를 달성하는 방법은 독자들에게 연습으로 남겨두겠습니다.
    4. 클로저의 사용

    PHP는 익명 함수를 구현하기 위해 클로저(Closure)를 사용합니다. 익명 함수의 가장 강력한 기능은 익명 함수가 제공하는 동적 특성과 클로저 효과입니다. 익명 함수를 정의할 때 범위 외부의 변수를 사용하려면 다음 구문을 사용해야 합니다:

    [php] 일반 사본 보기

    1. $name =
    2. 'TIPI 팀'; >기능() 사용($name
    3. ) {  echo "안녕하세요, $name"}
    4. $func (); // TIPI팀 안녕하세요
    5. 이 사용 설명은 꽤 어색해 보입니다. 특히 Javascript와 비교하면 PHP-Core이어야 합니다. 사용되는 구문은 포괄적인 고려 사항을 기반으로 합니다. PHP의 범위는 Javascript의 범위와 다르기 때문에 PHP 함수에 정의된 변수는 기본적으로 로컬 변수이지만 Javascript에서는 로컬 변수입니다. 명시적인 정의를 제외하고는 지역변수이며, PHP가 변이되면 그 변수가 지역변수인지 상위 범위의 변수인지 판단하는 것은 불가능하다. 하지만 이는 언어의 효율성과 복잡성에 큰 영향을 미칩니다.
    6. 이 구문은 비교적 간단합니다. 상위 범위의 변수에 액세스해야 하는 경우 use 문을 사용하여 선언해야 합니다. 이 구문도 간단하고 읽기 쉽습니다. global 문과 비슷한 효과를 얻으려면 use를 사용하세요. 익명 함수는 실행될 때마다 상위 범위의 변수에 액세스할 수 있습니다. 이러한 변수는 다음 예와 같이 익명 함수가 소멸되기 전에 항상 자체 상태를 저장합니다. [php] 일반 사본 보기
      1. 함수 getCounter() {
      2. $i = 0;
      3. 반환 기능() 사용($i) { // 여기에 변수를 전달하기 위해 참조를 사용하는 경우: use(&$i)
      4.  echo ++$i;
      5. $counter
      6. = getCounter() ;
      7. $counter
      8. () >(); 🎜>
      9. 은 Javascript와 다릅니다. 여기에는 두 가지가 있습니다. 이 함수 호출은 기본적으로 $i 변수를 증가시키지 않습니다. 레이어 변수를 복사하여 익명 함수에 추가합니다. 상위 레이어 변수의 값을 변경해야 하는 경우 참조로 전달해야 합니다. 따라서 위 코드는 이 아닌 을 출력합니다. 클로저 구현앞서 언급했듯이 익명 함수는 클로저를 통해 구현됩니다. 이제 클로저(클래스)가 어떻게 구현되는지 살펴보겠습니다. 클로저 구현 코드는 $PHP_SRC/Zend/zend_closure.c에 있습니다. 익명 함수의 '객관화' 문제는 Closure를 통해 구현되었습니다. 익명 함수 생성 시 변수에 어떻게 접근하나요?예를 들어 다음 코드는
      10. [php] 일반 사본 보기

      $i

      = 100; >사용1, 21,1(

      $i

      ) {

      debug_zval_dump(

      $i

      )
      1. }
      2. ();
      3. VLD를 사용하여 이 인코딩이 어떤 종류의 opcode로 컴파일되었는지 확인하세요 [php] 일반 사본 보기
        1. $ php -dvld.active=1 closure.php  
        2.    
        3. 변수:  !0 = $i, !1 = $counter  
        4. # *  op                         가져오기          ext  return  피연산자  
        5. ------ ------------------------------------- --  
        6. 0  >   지정 > >
        7. 3      INIT_FCALL_BY_NAME                                   !1  
        8. 4      DO_FCALL_BY_NAME                             0            
        9. 5    > 반환                                               1  
        10.    
        11. 기능 이름:  {클로저}  
        12. 작업 수:  5  
        13. 컴파일된 변수:  !0 = $i  
        14. 라인     # *  op                        가져오기          ext  return  피연산자  
        15. ---- ------------------------------------- --------------------------  
        16.   3     0  >   FETCH_R                    정적              $0      'i'  
        17. > 🎜>  4     2      SEND_VAR                                         !0          3      DO_FCALL                                   1         'debug_zval_dump'
        18.   
        19.   5     4    > 반환                                                 null  
        20. 상면根据情况去掉了一些无关的输流, 从上到下, 第1开始将100赋值给!0也就是变weight$i,随后执行ZEND_DECLARE_LAMBDA_FUNCTION,우리는 opcode를 执行函数中看看这里是怎么执行的으로 사용하며 $PHP_SRC/Zend/zend_vm_execute .h中:
        21. [ PHP] 일반 사본 보기

        정적

         int ZEND_FASTCALL  ZEND_DECLARE_LAMBDA_FUNCTION_SPEC_CONST_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)  

        1. {      zend_op *opline = EX(opline);  
        2.     zend_function *op_array;  
        3.    
        4.     if
        5.  (zend_hash_quick_find(EG(function_table), Z_STRVAL(opline->op1. u.constant), Z_STRLEN(opline->op1.u.constant), Z_LVAL(opline->op2.u.u.constant), (void *) &op_arra  
        6. y) == 실패 ||           op_array->type != ZEND_USER_FUNCTION) {  
        7.         zend_error_noreturn(E_ERROR, " 폐쇄를 위한 기본 람다 함수를 찾을 수 없음"
        8. );  
        9.     }     
        10.     zend_create_closure(&EX_T(opline->result. u.var
        11. ).tmp_var, op_array TSRMLS_CC);  
        12.        ZEND_VM_NEXT_OPCODE();  
        13. }  

        14. 이 함수는 zend_create_closure() 함수를 호출하여 클로저 객체를 생성하므로, $PHP_SRC/Zend/zend_closures.c에 있는 zend_create_closure() 함수가 무엇을 하는지 계속해서 살펴보겠습니다.

          [php] 일반 사본 보기

          1. ZEND_API void zend_create_closure(zval *res, zend_function *func TSRMLS_DC)
          2. {
          3. zend_closure *클로저
          4. object_init_ex(res, zend_ce_closure); >
          5. 클로저 = (zend_closure *)zend_object_store_get_object(res TSRMLS_CC)
          6. 클로저->func = *func;
          7. if
          8. ( 클로저-> func.type == ZEND_USER_FUNCTION) { // 사용자 정의 익명 함수인 경우  
          9. if
          10. (클로저->func.op_array.static_variables) { HashTable *static_variables = 클로저->func.op_array.static_variables;
          11.  
          12.                                                            >
          13. ALLOC_HASHTABLE(클로저->func.op_array.static_variables) zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables ), NULL, ZVAL_PTR_DTOR, 0)
          14. >
          15. zend_hash_apply_with_arguments(static_variables TSRMLS_CC, (apply_func_args_t)zval_copy_static_var, 1, closure->func.op_array.static_variables)
          16. }                                                                                        >
          17. 폐쇄->func.common.scope = NULL; 🎜>
          18. }
          19. 위 코드의 주석에서 언급했듯이 zval_copy_static_var() 함수의 구현을 계속 살펴보세요.
          20. [php] 일반 사본 보기
            1. static int zval_copy_static_var(zval **p TSRMLS_DC, int num_args, va_list args, zend_hash_key *key)
            2. {
            3. HashTable *target = va_arg(args, HashTable*)
            4. zend_bool is_ref; 🎜>
            5. // use 문 유형의 정적 변수에 대해서만 값 연산을 수행합니다. 그렇지 않으면 익명 함수 본문의 정적 변수도 범위에 영향을 미칩니다. 외부 변수
            6. if
            7. (Z_TYPE_PP(p) & (IS_LEXICAL_VAR|IS_LEXICAL_REF)) { is_ref = Z_TYPE_PP(p) & IS_LEXICAL_REF; (!EG(active_symbol_table)) {
            8. zend_rebuild_symbol_table(TSRMLS_C) }
            9.  //없는 경우 현재 범위의 해당 변수
            10.   if
            11. (zend_hash_quick_find(EG(active_symbol_table), key->arKey , key->nKeyLength, key->h, (void **) &p) == 실패) {
            12.                                                                                   🎜>
            13. // 참조변수인 경우 익명함수 정의 후 변수 연산을 하면서 임시변수를 생성
            14.                ALLOC_INIT_ZVAL(tmp);                                      🎜> zend_hash_quick_add( EG(active_symbol_table), 키 ->arKey, 키->nKeyLength, 키->h, &tmp, sizeof (zval*), (void**)&p)
            15. {
            16.                              > p = &EG(uninitialized_zval_ptr) ; 🎜>
            17. 🎜>, key-> ;arKey)
            18. }
            19. } 그 외 {
            20.                                                   ~ ~                          
            21. }else if (Z_ISREF_PP(p)) {
            22. SEPARATE_ZVAL(p);
            23.                                                                              >
            24. if
            25. (zend_hash_quick_add(target, key->arKey, key->nKeyLength, key->h, p , sizeof(zval*), NULL) == 성공) {
            26. Z_ADDREF_PP(p)
            27. } >
            28. ZEND_HASH_APPLY_KEEP;
            29. } >함수, 해시 테이블의 값을 읽을 때마다 다음과 같이 처리됩니다. 함수이며, 이 함수는 모든 use 문에서 정의한 변수 값을 이 익명 함수의 정적 변수에 할당하여 익명 함수가 use 의 변수에 액세스할 수 있도록 합니다. 원본 링크:
            30. http://www.php-internals.com/book/?p=chapt04/04-04-anonymous-function
            31. 참고 자료:http://php.net/manual/zh/functions.anonymous.php 위 내용은 내용의 측면을 포함하여 PHP 익명 함수 및 클로저를 소개하고 있으며, PHP 튜토리얼에 관심이 있는 친구들에게 도움이 되기를 바랍니다.
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.