gpt4 book ai didi

php - PHP 中的匿名函数内容不起作用

转载 作者:行者123 更新时间:2023-12-04 17:19:40 25 4
gpt4 key购买 nike

我有一个网站运行在一个相当老的 PHP 5.6.38 安装上...现在我已经冒险将它移动到最新的 XAMPP 版本(使用 PHP 8.0.3)。

不出所料,需要进行一些更改,但我似乎无法整理的是与已弃用的“create_function”函数相关的更改。我用它来允许我通过一个或多个键名动态排序关联数组......例如:

usort($myarray, create_function('$a,$b', get_usort_function('field2 ASC, field5 ASC')));

现在,我读到我应该使用匿名函数,所以将代码更改为如下...

usort($myarray, function($a,$b) { get_usort_function('field2 ASC, field5 ASC'); } );

get_usort_function 用于创建比较所需的文本 - 因此对于上面的示例,它会返回类似于...

$field2=compare_ints($a['field2'], $b['field2']); if($field2==0){return compare_ints($a['field5'], $b['field5']);}else{return $field2;}

现在,在 PHP8 版本中,匿名函数不起作用 - 但如果我对 get_usort_function 返回的字符串进行硬编码,那么它就起作用了。我错过了什么吗?

一个简单的实际操作示例如下...

<?php

function compare_ints($val1, $val2)
{
return $val1 <=> $val2;
}

function dynamic_create_usort_function()
{
$str='return compare_ints($a[' . "'" . 'id' . "'" . '], $b[' . "'" . 'id' . "'" . ']);';

return $str;
}

$a1 = array( 'id' => 9, 'name' => 'Andy');
$a2 = array( 'id' => 5, 'name' => 'Bob');
$a = array($a1, $a2);

$s = dynamic_create_usort_function();

print "\n\n***$s***\n\n";

print_r($a);

usort($a, function($a,$b) { dynamic_create_usort_function(); } );

print_r($a);

usort($a, function($a,$b) { return compare_ints($a['id'], $b['id']); } );

print_r($a);

?>

上面的例子给出了...的输出

***return compare_ints($a['id'], $b['id']);***

Array
(
[0] => Array
(
[id] => 9
[name] => Andy
)

[1] => Array
(
[id] => 5
[name] => Bob
)

)
Array
(
[0] => Array
(
[id] => 9
[name] => Andy
)

[1] => Array
(
[id] => 5
[name] => Bob
)

)
Array
(
[0] => Array
(
[id] => 5
[name] => Bob
)

[1] => Array
(
[id] => 9
[name] => Andy
)

)

我真的很想解决这个问题,因为我的网站大量使用了这个 usort 函数!所以,很明显,需要最少的返工就是梦想......

提前致谢,达伦

最佳答案

问题

create_function接受两个字符串,即要创建的函数的参数和主体。在内部,它使用 eval创建可调用的东西,这通常是不受欢迎的,因为它增加了攻击面。

根据您的描述,get_usort_function 函数返回一个字符串;如您所述,如果像这样调用:

get_usort_function('field2 ASC, field5 ASC')

它会返回如下内容:

$field2=compare_ints($a['field2'], $b['field2']); if($field2==0){return compare_ints($a['field5'], $b['field5']);}else{return $field2;}

您已经注意到,对传递给 usort 的可调用对象中的字符串进行硬编码是可行的,我想这类似于:

usort($myarray, function($a,$b) { $field2=compare_ints($a['field2'], $b['field2']); if($field2==0){return compare_ints($a['field5'], $b['field5']);}else{return $field2;} } );

但是根据上面对 get_usort_function 工作原理的描述,更准确的硬编码应该是:

usort($myarray, function($a,$b) { "$field2=compare_ints($a['field2'], $b['field2']); if($field2==0){return compare_ints($a['field5'], $b['field5']);}else{return $field2;}" } );

当像这样写出来时,很明显,将 usort 调用从 create_function 更改为使用可调用的,如您在上面指出的那样不会返回任何东西(所以 usort 将保留所有元素的现有顺序)。这可能是您对它的工作原理的理解不清楚的部分。

解决方案

您可以执行如下操作(这可能类似于现有 get_usort_function 内部逻辑的简化版本):

<?php

function print_people($people) {
foreach($people as ['id' => $id, 'name' => $name]) {
print("{$id}: {$name}\n");
}
print("\n");
}

function get_usort_callable(...$fields) {
return function ($a, $b) use ($fields) {
foreach ($fields as $field) {
$result = $a[$field] <=> $b[$field];
if ($result != 0) { return $result; }
}
return 0;
};
}

$a1 = ['id' => 9, 'name' => 'Andy'];
$a2 = ['id' => 6, 'name' => 'Carol'];
$a3 = ['id' => 5, 'name' => 'Bob'];
$a = [$a1, $a2, $a3];

print_people($a);

usort($a, get_usort_callable('id', 'name'));

print_people($a);

usort($a, get_usort_callable('name', 'id'));

print_people($a);

给出以下输出:

9: Andy
6: Carol
5: Bob

5: Bob
6: Carol
9: Andy

9: Andy
5: Bob
6: Carol

这里的主要内容是在 get_usort_callable 返回的匿名函数上使用 use 关键字来使 $fields 可用。如果您想匹配现有 get_usort_function 的功能,您可以重写它以采用 string,并将其拆分。

最少的工作

鉴于您将不得不从 create_function 转移到使用 PHP 8,您可以做的最少工作就是重写 get_usort_function 以返回一个可调用(与上面类似)并替换调用,如:

usort($myarray, create_function('$a,$b', get_usort_function(...)));

与:

usort($myarray, get_usort_function(...));

鉴于您可以访问 get_usort_function 的内部逻辑,它应该不会困难,幸运的是这只是在一个地方。调用站点的重构也非常机械化,几乎可以用任何 IDE 进行查找和替换。

展望 future (并取决于您的偏好),您可能希望将 SQL ORDER BY 样式字符串替换为结构化数组,例如:

'field2 ASC, field5 ASC'

变成:

[
[
'field' => 'field2',
'direction' => 'asc'
],
[
'field' => 'field5',
'direction' => 'asc'
]
]

为了避免需要在 get_usort_function 中进行额外处理的空格等可能出现的问题。

create_function 与匿名函数

从使用 create_function 到使用匿名函数时,您不再需要将函数的参数和主体作为字符串传递。例如,以下两个可调用对象是等效的:

create_function('$a,$b', 'return $a["id"] <=> $b["id"];')
function($a, $b) { return $a["id"] <=> $b["id"]; }

并且可以互换使用。

通过使用以下函数添加一级间接(正如您在问题中所拥有的):

function sort_by_function_body($field) {
return "return \$a[\"{$field}\"] <=> \$b[\"{$field}\"];";
}

function sort_by_callable($field) {
return function($a, $b) use ($field) { return $a[$field] <=> $b[$field]; };
}

这两个也是等价的:

create_function('$a,$b', sort_by_function_body('id'))
sort_by_callable('id')

主要的收获是 sort_by_callable 函数本身返回一个专门的匿名函数,它将对传入的字段进行排序,而不是包含执行相同逻辑的代码的字符串。

关于php - PHP 中的匿名函数内容不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66960268/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com