使用create_function()建立"匿名"函数

#1.使用create_function()建立"匿名"函数php

前面提到PHP5.3中才才开始正式支持匿名函数,说到这里可能会有细心读者有意见了,由于有个函数是能够生成匿名函数的: create_function函数, 在手册里能够查到这个函数在PHP4.1和PHP5中就有了,这个函数一般也能做为匿名回调函数使用, 例如以下数组

<?php
$array = array(1, 2, 3, 4);
array_walk($array, create_function('$value', 'echo $value'));
?>

这段代码只是将数组中的值依次输出,固然也能作更多的事情。闭包

2.为何create_function不算真正的匿名函数

那为何这不算真正的匿名函数呢, 咱们先看看这个函数的返回值,这个函数返回一个字符串, 一般咱们能够像下面这样调用一个函数函数

函数的调用ui

<?php
function a() {
    echo 'function a';
}
 
$a = 'a';  //把一个字符串赋值给一个变量
$a(); //用变量来调用函数
?

咱们在实现回调函数的时候也能够采用这样的方式,例如debug

回调函数的实现code

<?php
function do_something($callback) {//传一个变量而后调用变量函数
    // doing
    # ...
 
    // done
    $callback();
}
?>

这样就能实如今函数do_something()执行完成以后调用$callback指定的函数。字符串

注解:也就是说利用PHP这个变量特性咱们本身也能够完成匿名函数的调用回调函数


#3. create_function是返回函数名称的string

回到create_function函数的返回值: 函数返回一个惟一的字符串函数名, 出现错误的话则返回FALSE。

这么说这个函数也只是动态的建立了一个函数,而这个函数是有函数名的,也就是说,其实这并非匿名的。 只是建立了一个全局惟一的函数而已。

<?php
$func = create_function('', 'echo "Function created dynamic";');
echo $func; // lambda_1
 
$func();    // Function created dynamic
 
$my_func = 'lambda_1';
$my_func(); // 不存在这个函数
lambda_1(); // 不存在这个函数
?>

上面这段代码的前面很好理解,create_function就是这么用的,后面指定函数名调用却失败了,这就有些很差理解了,

php是怎么保证这个函数是全局惟一的?

lambda_1看起来也是一个很普通的函数明,若是咱们先定义一个叫作lambda_1的函数呢? 这里函数的返回字符串会是lambda_2,它在建立函数的时候会检查是否这个函数是否存在直到找到合适的函数名, 但若是咱们在create_function以后定义一个叫作lambda_1的函数会怎么样呢?

这样就出现函数重复定义的问题了, 这样的实现恐怕不是最好的方法,实际上若是你真的定义了名为lambda_1的函数也是不会出现我所说的问题的。这到底是怎么回事呢? 上面代码的倒数2两行也说明了这个问题,实际上并无定义名为lambda_1的函数。

也就是说咱们的lambda_1和create_function返回的lambda_1并非同样的!? 怎么会这样呢? 那只能说明咱们没有看到实质, 只看到了表面,表面是咱们在echo的时候输出了lambda_1,而咱们的lambda_1是咱们本身敲入的. 咱们仍是使用debug_zval_dump函数来看看吧。

<?php
$func = create_function('', 'echo "Hello";');
 
$my_func_name = 'lambda_1';
debug_zval_dump($func);         // string(9) "lambda_1" refcount(2)
debug_zval_dump($my_func_name); // string(8) "lambda_1" refcount(2)
?>

看出来了吧,他们的长度竟然不同,长度不同,因此咱们调用的函数固然是不存在的, 咱们仍是直接看看create_function函数到底都作了些什么吧。 该实现见: $PHP_SRC/Zend/zend_builtin_functions.c

#define LAMBDA_TEMP_FUNCNAME    "__lambda_func"
 
ZEND_FUNCTION(create_function)
{
    // ... 省去无关代码
    function_name = (char *) emalloc(sizeof("0lambda_")+MAX_LENGTH_OF_LONG);
    function_name[0] = '\0';  // <--- 这里
    do {
        function_name_length = 1 + sprintf(function_name + 1, "lambda_%d", ++EG(lambda_count));
    } while (zend_hash_add(EG(function_table), function_name, function_name_length+1, &new_function, sizeof(zend_function), NULL)==FAILURE);
    zend_hash_del(EG(function_table), LAMBDA_TEMP_FUNCNAME, sizeof(LAMBDA_TEMP_FUNCNAME));
    RETURN_STRINGL(function_name, function_name_length, 0);
}

该函数在定义了一个函数以后,给函数起了个名字,它将函数名的第一个字符变为了'\0'也就是空字符,而后在函数表中查找是否已经定义了这个函数, 若是已经有了则生成新的函数名, 第一个字符为空字符的定义方式比较特殊, 这样在用户代码中就没法定义出这样的函数了, 这样也就不存在命名冲突的问题了, 这也算是种取巧的作法了, 在了解到这个特殊的函数以后,咱们其实仍是能够调用到这个函数的, 只要咱们在函数名前加一个空字符就能够了, chr()函数能够帮咱们生成这样的字符串, 例如前面建立的函数能够经过以下的方式访问到:

<?php
$my_func = chr(0) . "lambda_1";
$my_func(); // Hello
?>

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

  1. 函数的定义是经过字符串动态eval的, 这就没法进行基本的语法检查;

  2. 这类函数和普通函数没有本质区别, 没法实现闭包的效果。

相关文章
相关标签/搜索