create_function

(PHP 4 >= 4.0.1, PHP 5, PHP 7)

create_function通过评估代码字符串动态创建函数

警告

此函数自 PHP 7.2.0 起已 被弃用,自 PHP 8.0.0 起已 移除。强烈建议不要依赖此函数。

说明

create_function(string $args, string $code): string

根据传递的参数动态创建一个函数,并返回一个唯一的函数名。

注意

此函数在内部执行了 eval(),因此它与 eval() 具有相同的安全问题。它还具有糟糕的性能和内存使用特性,因为创建的函数是全局的,无法释放。

应使用原生 匿名函数 代替。

参数

通常建议将这些参数作为 单引号 字符串传递。如果使用 双引号 字符串,则代码中的变量名需要小心转义,例如 \$somevar

args

函数参数,作为一个逗号分隔的字符串。

code

函数代码。

返回值

返回一个唯一的函数名作为字符串,或者在失败时返回 false。请注意,该名称包含一个不可打印的字符 ("\0"),因此在打印名称或将其合并到任何其他字符串中时应谨慎。

示例

示例 #1 使用 create_function() 或匿名函数动态创建函数

您可以使用动态创建的函数来(例如)从运行时收集的信息中创建函数。首先,使用 create_function()

<?php
$newfunc
= create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
echo
$newfunc(2, M_E) . "\n";
?>

现在是相同的代码,使用 匿名函数;请注意,代码和参数不再包含在字符串中

<?php
$newfunc
= function($a,$b) { return "ln($a) + ln($b) = " . log($a * $b); };
echo
$newfunc(2, M_E) . "\n";
?>

上面的示例将输出

ln(2) + ln(2.718281828459) = 1.6931471805599

示例 #2 使用 create_function() 或匿名函数创建通用处理函数

另一个用途可能是拥有一个通用的处理程序函数,它可以对一组参数应用一组操作

<?php
function process($var1, $var2, $farr)
{
foreach (
$farr as $f) {
echo
$f($var1, $var2) . "\n";
}
}

// 创建一组数学函数
$farr = array(
create_function('$x,$y', 'return "some trig: ".(sin($x) + $x*cos($y));'),
create_function('$x,$y', 'return "a hypotenuse: ".sqrt($x*$x + $y*$y);'),
create_function('$a,$b', 'if ($a >=0) {return "b*a^2 = ".$b*sqrt($a);} else {return false;}'),
create_function('$a,$b', "return \"min(b^2+a, a^2,b) = \".min(\$a*\$a+\$b,\$b*\$b+\$a);"),
create_function('$a,$b', 'if ($a > 0 && $b != 0) {return "ln(a)/b = ".log($a)/$b; } else { return false; }')
);

echo
"\nUsing the first array of dynamic functions\n";
echo
"parameters: 2.3445, M_PI\n";
process(2.3445, M_PI, $farr);

// 现在创建一组字符串处理函数
$garr = array(
create_function('$b,$a', 'if (strncmp($a, $b, 3) == 0) return "** \"$a\" '.
'and \"$b\"\n** Look the same to me! (looking at the first 3 chars)";'),
create_function('$a,$b', 'return "CRCs: " . crc32($a) . ", ".crc32($b);'),
create_function('$a,$b', 'return "similar(a,b) = " . similar_text($a, $b, $p) . "($p%)";')
);
echo
"\nUsing the second array of dynamic functions\n";
process("Twas brilling and the slithy toves", "Twas the night", $garr);
?>

同样,这里使用 匿名函数 的相同代码。请注意,代码中的变量名不再需要转义,因为它们没有包含在字符串中。

<?php
function process($var1, $var2, $farr)
{
foreach (
$farr as $f) {
echo
$f($var1, $var2) . "\n";
}
}

// 创建一堆数学函数
$farr = array(
function(
$x,$y) { return "some trig: ".(sin($x) + $x*cos($y)); },
function(
$x,$y) { return "a hypotenuse: ".sqrt($x*$x + $y*$y); },
function(
$a,$b) { if ($a >=0) {return "b*a^2 = ".$b*sqrt($a);} else {return false;} },
function(
$a,$b) { return "min(b^2+a, a^2,b) = " . min($a*$a+$b, $b*$b+$a); },
function(
$a,$b) { if ($a > 0 && $b != 0) {return "ln(a)/b = ".log($a)/$b; } else { return false; } }
);

echo
"\nUsing the first array of dynamic functions\n";
echo
"parameters: 2.3445, M_PI\n";
process(2.3445, M_PI, $farr);

// 现在创建一堆字符串处理函数
$garr = array(
function(
$b,$a) { if (strncmp($a, $b, 3) == 0) return "** \"$a\" " .
"and \"$b\"\n** Look the same to me! (looking at the first 3 chars)"; },
function(
$a,$b) { return "CRCs: " . crc32($a) . ", ".crc32($b); },
function(
$a,$b) { return "similar(a,b) = " . similar_text($a, $b, $p) . "($p%)"; }
);
echo
"\nUsing the second array of dynamic functions\n";
process("Twas brilling and the slithy toves", "Twas the night", $garr);
?>

上面的示例将输出

Using the first array of dynamic functions
parameters: 2.3445, M_PI
some trig: -1.6291725057799
a hypotenuse: 3.9199852871011
b*a^2 = 4.8103313314525
min(b^2+a, a^2,b) = 8.6382729035898
ln(a)/b = 0.27122299212594

Using the second array of dynamic functions
** "Twas the night" and "Twas brilling and the slithy toves"
** Look the same to me! (looking at the first 3 chars)
CRCs: 3569586014, 342550513
similar(a,b) = 11(45.833333333333%)

示例 #3 使用动态函数作为回调函数

也许动态函数最常见的用途是将它们作为回调函数传递,例如使用 array_walk()usort()

<?php
$av
= array("the ", "a ", "that ", "this ");
array_walk($av, create_function('&$v,$k', '$v = $v . "mango";'));
print_r($av);
?>

转换为 匿名函数

<?php
$av
= array("the ", "a ", "that ", "this ");
array_walk($av, function(&$v,$k) { $v = $v . "mango"; });
print_r($av);
?>

上面的示例将输出

Array
(
  [0] => the mango
  [1] => a mango
  [2] => that mango
  [3] => this mango
)

使用 create_function() 将字符串从最长到最短排序

<?php
$sv
= array("small", "a big string", "larger", "it is a string thing");
echo
"Original:\n";
print_r($sv);
echo
"Sorted:\n";
usort($sv, create_function('$a,$b','return strlen($b) - strlen($a);'));
print_r($sv);
?>

转换为 匿名函数

<?php
$sv
= array("small", "a big string", "larger", "it is a string thing");
echo
"Original:\n";
print_r($sv);
echo
"Sorted:\n";
usort($sv, function($a,$b) { return strlen($b) - strlen($a); });
print_r($sv);
?>

上面的示例将输出

Original:
Array
(
  [0] => small
  [1] => a big string
  [2] => larger
  [3] => it is a string thing
)
Sorted:
Array
(
  [0] => it is a string thing
  [1] => a big string
  [2] => larger
  [3] => small
)

参见

添加注释

用户贡献的注释 22 则注释

tamagochi_man
5 年前
虽然在 11 年前是正确的,但 Dan D 的说法现在已经不那么正确了。匿名函数现在是 Closure 类的对象,并且可以被垃圾回收器安全地回收。
Dan D
17 年前
在 PHP 中使用匿名函数时要小心,就像在 Python、Ruby、Lisp 或 Javascript 等语言中一样。如前所述,分配的内存永远不会释放;它们在 PHP 中不是对象——它们只是动态命名的全局函数——因此它们没有作用域,也不受垃圾回收的影响。

因此,如果您正在开发任何可重用的东西(OO 或其他),我会像瘟疫一样避开它们。它们速度慢、效率低,而且无法确定您的实现是否最终会进入一个大的循环。我的实现最终在一个大约 100 万条记录的迭代中,很快耗尽了我的每进程 500 MB 的限制。
Josh J
17 年前
关于 info at adaniels dot nl 的递归问题

通过在正确作用域中引用函数变量来实现匿名函数递归。
<?php
$fn2
= create_function('$a', 'echo $a; if ($a < 10) call_user_func($GLOBALS["fn2"], ++$a);');
$fn2(1);
?>
kak dot serpom dot po dot yaitsam at gmail dot com
12 年前
尝试这样做以提高脚本的性能(增加 maxCacheSize)

<?php
runkit_function_copy
('create_function', 'create_function_native');
runkit_function_redefine('create_function', '$arg,$body', 'return __create_function($arg,$body);');

function
__create_function($arg, $body) {
static
$cache = array();
static
$maxCacheSize = 64;
static
$sorter;

if (
$sorter === NULL) {
$sorter = function($a, $b) {
if (
$a->hits == $b->hits) {
return
0;
}

return (
$a->hits < $b->hits) ? 1 : -1;
};
}

$crc = crc32($arg . "\\x00" . $body);

if (isset(
$cache[$crc])) {
++
$cache[$crc][1];
return
$cache[$crc][0];
}

if (
sizeof($cache) >= $maxCacheSize) {
uasort($cache, $sorter);
array_pop($cache);
}

$cache[$crc] = array($cb = eval('return function('.$arg.'){'.$body.'};'), 0);
return
$cb;
}
?>
kkaiser at revolution-records dot net
17 年前
在将 PHP4 代码库迁移到 PHP5 的过程中,我遇到一个奇怪的问题。在库中,每个类都继承自一个名为“class_container”的通用类。“class_container”包含一个名为 runtime_functions 的数组和一个名为 class_function 的方法,该方法如下:

<?php
function class_function($name,$params,$code) {

$this->runtime_functions[$name] = create_function($params,$code);

}
?>

在 class_container 的子类中,有一个函数使用 class_function() 来存储一些自定义的 lambda 函数,这些函数是自引用的。

<?php
function myfunc($name,$code) {

$this->class_function($name,'$theobj','$this=&$theobj;'.$code);

}
?>

在 PHP4 中,这可以正常工作。想法是在子类级别编写代码块,例如“echo $this->id;”,然后简单地使用 $MYOBJ->myfunc(“go”,“echo $this->id;”);,然后像 $MYOBJ->runtime_functions[“go”](); 一样调用它。

它本质上与将匿名函数绑定到 Javascript 中的对象完全相同。

请注意,为了使 $code 块正常工作,必须手动重新定义“$this”关键字。

然而,在 PHP5 中,你不能在没有出现致命错误的情况下重新声明 $this,因此代码必须更新为

<?php
function myfunc($name,$code) {

$this->class_function($name,'$this',$code);

}
?>

显然 create_function() 允许你通过函数参数设置 $this,让你能够将匿名函数绑定到实例化的对象。我认为这可能对某些人有用。
info at adaniels dot nl
18 年前
请注意,在匿名函数中使用 __FUNCTION__ 将始终导致“__lambda_func”。

<?php
$fn
= create_function('', 'echo __FUNCTION__;');
$fn();
// 结果: __lambda_func
echo $fn;
// 结果: ºlambda_2 (实际的第一个字符无法显示)
?>

这意味着匿名函数不能递归使用。以下代码(递归计数到 10)会导致错误
<?php
$fn2
= create_function('$a', 'echo $a; if ($a < 10) call_user_func(__FUNCTION__, $a++);');
$fn2(1);
// 警告: call_user_func(__lambda_func) [function.call-user-func]: 第一个参数应为 T:/test/test.php(21) 中有效的回调: 第 1 行运行时创建的函数
?>
DB on music_ml at yahoo dot com dot ar
21 年前
[由 danbrown AT php DOT net 编辑:将用户修正后的帖子与之前的(不正确的)帖子合并在一起。]

你不能从类方法内部的匿名函数中引用类变量使用 $this。匿名函数不会继承方法作用域。你需要这样做

<?php
class AnyClass {

var
$classVar = 'some regular expression pattern';

function
classMethod() {

$_anonymFunc = create_function( '$arg1, $arg2', 'if ( eregi($arg2, $arg1) ) { return true; } else { return false; } ' );

$willWork = $_anonymFunc('some string', $classVar);

}

}
?>
Dave H
13 年前
以下函数对于创建用户函数的别名非常有用。
对于内置函数,它不太有用,因为默认值不可用,因此内置函数的函数别名必须提供所有参数,无论它们是可选的还是必需的。

<?php
function create_function_alias($function_name, $alias_name)
{
if(
function_exists($alias_name))
return
false;
$rf = new ReflectionFunction($function_name);
$fproto = $alias_name.'(';
$fcall = $function_name.'(';
$need_comma = false;

foreach(
$rf->getParameters() as $param)
{
if(
$need_comma)
{
$fproto .= ',';
$fcall .= ',';
}

$fproto .= '$'.$param->getName();
$fcall .= '$'.$param->getName();

if(
$param->isOptional() && $param->isDefaultValueAvailable())
{
$val = $param->getDefaultValue();
if(
is_string($val))
$val = "'$val'";
$fproto .= ' = '.$val;
}
$need_comma = true;
}
$fproto .= ')';
$fcall .= ')';

$f = "function $fproto".PHP_EOL;
$f .= '{return '.$fcall.';}';

eval(
$f);
return
true;
}
?>
lombax85 at gmail dot com
3 年前
对于那些在 php8 上 *真正* 需要 create_function() 的人(因为遗留代码无法轻松更改),可以使用“composer require lombax85/create_function”。
CertaiN
10 年前
最佳包装器

<?php

function create_lambda($args, $code) {
static
$func;
if (!isset(
$func[$args][$code])) {
$func[$args][$code] = create_function($args, $code);
}
return
$func[$args][$code];
}
neo at nowhere dot com
15 年前
针对 kkaiser at revolution-records dot net 的笔记,即使 PHP 允许你使用
<?
$myfunc = create_function('$this', $code);
?>
你不能在匿名函数中使用对 “$this” 的引用,因为 PHP 会抱怨你在非对象上下文中使用对 “$this” 的引用。

目前,我还没有找到解决方法...
Alan FUNG
16 年前
$f = create_function('','echo "function defined by create_function";');
$f();

结果
function defined by create_function

你可以在使用 create_function 时在函数体中不定义返回值。
Rene Saarsoo
16 年前
这里有一些关于 “内存泄漏” create_function() 可能产生的讨论。

create_function() 实际上所做的,是创建一个名称为 chr(0).lambda_n 的普通函数,其中 n 是某个数字

<?php
$f
= create_function('', 'return 1;');

function
lambda_1() { return 2; }

$g = "lambda_1";
echo
$g(); // 输出: 2

$h = chr(0)."lambda_1";
echo
$h(); // 输出: 1
?>
TSE-WebDesign
16 年前
以下是如何从另一个运行时创建的函数调用运行时创建的函数
<?php
$get_func
= create_function('$func', 'return substr($func,1);');
$get_value = create_function('$index','return pow($index,$index);');
$another_func = create_function('$a', '$func="\x00"."'.$get_func($get_value).'";return $func($a);');
echo
$another_func(2); # 结果是 4
?>
Phlyst
17 年前
回复 info at adaniels dot nl

你可能无法在 lambda 中使用 __FUNCTION__(感谢你指出这一点;我现在也遇到了这个问题),但如果你将函数分配给变量,可以使用 $GLOBALS 来解决这个问题。我使用 PHP4 重新实现了 array_walk_recursive(),如下所示

<?php
$array_walk_recursive
= create_function('&$array, $callback',
'foreach($array as $element) {
if(is_array($element)) {
$funky = $GLOBALS["array_walk_recursive"];
$funky($element, $callback);
}
else {
$callback($element);
}
}'
);
?>
josh at janrain dot com
18 年前
注意!这只是一个便利函数,它为常规函数生成一个唯一名称。它*不是*闭包,甚至不是匿名函数。它只是一个为你命名的普通函数。
Joshua E Cook
18 年前
由 create_function() 创建的函数不能通过引用返回值。下面的函数创建一个可以返回引用的函数。参数与 create_function() 相同。注意,这些参数被未修改地传递给 eval(),因此请确保传入的数据已消毒。

<?php
/**
* create_ref_function
* 创建一个匿名(lambda 风格)函数
* 它返回一个引用
* 参见 https://php.net/create_function
*/
function
create_ref_function( $args, $code )
{
static
$n = 0;

$functionName = sprintf('ref_lambda_%d',++$n);

$declaration = sprintf('function &%s(%s) {%s}',$functionName,$args,$body);

eval(
$declaration);

return
$functionName;
}
?>
boards at gmail dot com
18 年前
如果你正在检查函数是否创建正确,这将是更好的检查方法

<?php
$fnc
= @create_function('$arg1,$arg2,$arg3', 'return true;');
# 将该函数设置为任何你想要的
if (empty($fnc)) {
die(
'Could not create function $fnc.');
}

# 尽管如此,以下将不起作用
if (empty(create_function('$arg', 'return $arg;'))) {
die(
'Could not create anonymous function.');
}
# 你会得到一个关于无法在可写上下文中使用返回值的错误(即返回值在 C 中是一个常量,而 empty() 函数不使用 const void* 参数
?>
MagicalTux at FF.ST
20 年前
neo at gothic-chat d0t de 写道
注意内存泄漏,垃圾回收似乎“忽视”了动态创建的函数!

不完全是...

事实上,PHP 无法 “取消分配” 函数。因此,如果你创建了一个函数,它不会在脚本结束之前被删除,即使你取消了包含其名称的变量。

如果你需要在每次运行循环时更改函数的一部分,请考虑如何创建一个更通用的函数,或者尝试使用 eval :)(函数是为了重复使用而创建的。如果你只需要运行自己的代码一次,eval 会更好)。
edgar at goodforall dot eu
15 年前
我想到一个小玩具,我想分享一下。它创建一个匿名函数,允许你将类用作函数。

在 php 5.3 中,支持真正的函子(通过 __invoke)

<?php
function createFunctor($className){
$content = "
static \$class;
if(!\$class){
\$class = new
$className;
}
return \$class->run(\$args);
"
;
$f = create_function('$args', $content);
return
$f;

}
class
test {
public function
run($args){
print
$args;
}
}
$test = createFunctor('test');
$test('hello world');
?>
neo at gothic-chat d0t de
20 年前
注意内存泄漏,垃圾回收似乎“忽视”了动态创建的函数!

我使用了一个这样的函数来将链接中的特殊字符替换为它们的 htmlentities
<?php
$text
= preg_replace_callback (
"/(<(frame src|a href|form action)=\")([^\"]+)(\"[^>]*>)/i",
create_function (
'$matches',
'return $matches[1] . htmlentities ($matches[3]) . $matches[4];'
),
$text);
?>

在调用 1000 次后,该进程使用了比之前大约多 5MB 的内存。在我的情况下,这将一个 PHP 进程的内存大小提升到了超过 100MB!

在这种情况下,最好将函数存储在全局变量中。
x-empt[a_t]ispep.cx
23 年前
Create_function 使得能够改变函数的作用域。你可能有一个类,它需要定义一个全局函数。这是可能的,比如

<?php
class blah {
function
blah() {
$z=create_function('$arg1string','return "function-z-".$arg1string;');
$GLOBALS['z']=$z;
}
}
$blah_object=new blah;

$result=$GLOBALS['z']('Argument 1 String');
echo
$result;
?>

使函数从其定义的作用域中逃脱在许多情况下都很有用。
To Top