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

PHP中的匿名函数内容不起作用,php,Php,我有一个网站运行在一个非常旧的PHP5.6.38安装上。。。现在,我冒险将其迁移到最新的XAMPP版本(使用PHP8.0.3) 不出所料,这里有一些必要的更改,但我似乎无法排序的是与不推荐使用的“create_function”函数相关的更改。我使用它来允许我按一个或多个键名对关联数组进行动态排序。。。例如: usort($myarray, create_function('$a,$b', get_usort_function('field2 ASC, field5 ASC'))); 现在,我

我有一个网站运行在一个非常旧的PHP5.6.38安装上。。。现在,我冒险将其迁移到最新的XAMPP版本(使用PHP8.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'); } );
<?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);

?>
get_usort_函数用于创建比较所需的文本-因此对于上面的示例,它将返回类似于

$field2=compare_ints($a['field2'], $b['field2']); if($field2==0){return compare_ints($a['field5'], $b['field5']);}else{return $field2;}
现在,在PHP8版本中,匿名函数不起作用-但是如果我硬编码get_usort_函数返回的字符串,那么它就起作用了。我错过什么了吗

一个简单的例子如下

usort($myarray, function($a,$b) { get_usort_function('field2 ASC, field5 ASC'); } );
<?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);

?>
我真的很想解决这个问题,因为我的网站充分利用了这个usort功能!所以,很明显,最不需要返工的就是梦想

提前感谢,, 达伦

问题 接受两个字符串,参数和要创建的函数体。在内部,它用来创建一些可调用的东西,这通常是不赞成的,因为它增加了攻击表面积

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

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($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
的可调用函数中,对字符串进行硬编码是可行的,我认为这类似于:

$field2=compare_ints($a['field2'], $b['field2']); if($field2==0){return compare_ints($a['field5'], $b['field5']);}else{return $field2;}
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\u usort\u函数
的工作原理,更准确的硬编码是:

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\u函数
更改为使用可调用函数(如上所述)不会返回任何内容(因此
usort
将保留所有元素的现有顺序)。这可能是你不清楚它是如何工作的部分

解决方案 您可以执行以下操作(这可能类似于现有
get\u usort\u函数的内部逻辑的简化版本)

这里的主要技巧是在由
get\u usort\u callable
返回的匿名函数上使用
use
关键字,使
$fields
可用。如果您想匹配现有的
get\u usort\u函数的功能
,可以将其重写为一个
字符串
,然后将其拆分

最小工作量 如果要使用PHP8,您将不得不离开
create_函数
,那么您所能做的最少工作就是重写
get____函数
,以返回一个可调用函数(类似于上文),并替换如下调用:

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

考虑到您可以访问
get\u usort\u函数的内部逻辑
,这应该不会太难,谢天谢地,这只在一个位置。调用站点的重构也是相当机械的,几乎可以用任何IDE进行查找和替换

接下来(取决于您的偏好),您可能希望用结构化数组替换SQL
ORDER BY
样式字符串,例如:

'field2 ASC, field5 ASC'
变成:

[
  [
    'field' => 'field2',
    'direction' => 'asc'
  ],
  [
    'field' => 'field5',
    'direction' => 'asc'
  ]
]
避免可能出现的空格等问题,这些问题需要在
get\u usort\u函数中进行额外处理

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

create_function('$a,$b', 'return $a["id"] <=> $b["id"];')
create_function('$a,$b', sort_by_function_body('id'))
主要的好处是,
sort\u by\u callable
函数本身返回一个专门的匿名函数,它将根据传入的字段进行排序,而不是包含代码的字符串来执行相同的逻辑。

谢谢

我已经采纳了你所说的,并重新设计了一些东西,以得到我需要的有效解决方案

$a1 = array( 'id' => 7, 'name' => 'Andy', 'dob' => '01-02-2020');
$a2 = array( 'id' => 7, 'name' => 'Bob', 'dob' => '01-02-2022');
$a3 = array( 'id' => 9, 'name' => 'Guy', 'dob' => '01-02-2021');
$a4 = array( 'id' => 7, 'name' => 'Mick', 'dob' => '01-02-2023');
$a5 = array( 'id' => 3, 'name' => 'Daz', 'dob' => '01-02-2019');
$a6 = array( 'id' => 7, 'name' => 'Daz', 'dob' => '01-02-2021');

$a = array($a1, $a2, $a3, $a4, $a5, $a6);

function get_usort_function_callable($order_bys, $mode = 1)
{
    return function ($a, $b) use ($order_bys, $mode)
    {
        // We get the options in reverse order, so we can properly nest them.
        $order_by_options_r = array_reverse(explode(",", $order_bys));
        #print_r($order_by_options_r);
        #print "MODE = $mode";

        $first_element = TRUE;
        
        foreach ($order_by_options_r as $order_by)
        {
            if (( isset($retval) ) AND ( strlen($retval) > 0 ))
            {
                $inner_val = $retval;
            }

            $order_by = trim($order_by);
            $indexOfSpace = strpos($order_by, " ");

            if( $indexOfSpace !== FALSE )
            {
                $column = trim(substr($order_by,0,$indexOfSpace));
                $sortorder = trim(substr($order_by,$indexOfSpace));

                if ( strcasecmp($sortorder,"DESC") == 0 )
                {
                    switch ( $mode )
                    {
                        case 1:
                        case 2:
                            $comp = '$b[' . "'" . $column . "'" . '] <=> $a[' . "'" . $column . "'" . ']';
                        break;
                        case 3:
                            $comp = 'strtotime($b[' . "'" . $column . "'" . ']) <=> strtotime($a[' . "'" . $column . "'" . '])';
                        break;
                        case 4:
                            $comp = 'strnatcmp($b[' . "'" . $column . "'" . '], $a[' . "'" . $column . "'" . '])';
                        break;
                    }
                } else {
                    switch ( $mode )
                    {
                        case 1:
                        case 2:
                            $comp = '$a[' . "'" . $column . "'" . '] <=> $b[' . "'" . $column . "'" . ']';
                        break;
                        case 3:
                            $comp = 'strtotime($a[' . "'" . $column . "'" . ']) <=> strtotime($b[' . "'" . $column . "'" . '])';
                        break;
                        case 4:
                            $comp = 'strnatcmp($a[' . "'" . $column . "'" . '], $b[' . "'" . $column . "'" . '])';
                        break;
                    }
                }

                if($first_element)
                {
                    $retval = 'return ' . $comp . ';';
                    $first_element = FALSE;
                } else {
                    $retval = '$' . $column . '=' . $comp . '; if ( $' . $column . ' == 0 ) { ' . $inner_val . ' } else { return $' . $column . '; }';
                }
            }
        }

        #print "RET = $retval\n";

        return eval($retval);
    };
}

usort($a, get_usort_function_callable("dob ASC, id DESC, name DESC") );

print_r($a);
…变成这样的电话

usort($_wishlist_details, get_usort_function_callable('wishlistcountryname ASC, wishlistname ASC') );
最好对我的正则表达式技能进行分类,以便进行全局搜索/替换,而不是手动编辑每个文件


感谢您的帮助:-)

如果您想动态创建代码,您需要使用
eval()
。我在答案中添加了更多信息,以保持更清晰,但在当前的
get\u usort\u function\u callable
函数中需要更改的主要内容是,它不应该返回返回要执行的PHP代码字符串的函数,而应该返回一个就是该代码的函数,因为缺少更好的术语。另外,
usort
和类似函数的操作方式是运行callable来比较数组中的两个元素,并将其用于排序,因此对于给定的数组,它将被多次调用。我刚刚用最终的工作解决方案更新了我的答案(实际上是一个回复)。谢谢你的帮助!一切都好。作为重构的一部分,它似乎已经被清理了一点,当您触摸代码时,这总是您想要的;要使它比找到它时稍微好一点:)您应该在某个时候删除
eval
,但这可能是以后的工作。您在这里的使用还不错,因为它所计算的字符串仅基于固定输入(即用户没有提供任何内容),但您也可能会从直接代码解决方案中发现更好的性能。
sort_by_callable('id')
$a1 = array( 'id' => 7, 'name' => 'Andy', 'dob' => '01-02-2020');
$a2 = array( 'id' => 7, 'name' => 'Bob', 'dob' => '01-02-2022');
$a3 = array( 'id' => 9, 'name' => 'Guy', 'dob' => '01-02-2021');
$a4 = array( 'id' => 7, 'name' => 'Mick', 'dob' => '01-02-2023');
$a5 = array( 'id' => 3, 'name' => 'Daz', 'dob' => '01-02-2019');
$a6 = array( 'id' => 7, 'name' => 'Daz', 'dob' => '01-02-2021');

$a = array($a1, $a2, $a3, $a4, $a5, $a6);

function get_usort_function_callable($order_bys, $mode = 1)
{
    return function ($a, $b) use ($order_bys, $mode)
    {
        // We get the options in reverse order, so we can properly nest them.
        $order_by_options_r = array_reverse(explode(",", $order_bys));
        #print_r($order_by_options_r);
        #print "MODE = $mode";

        $first_element = TRUE;
        
        foreach ($order_by_options_r as $order_by)
        {
            if (( isset($retval) ) AND ( strlen($retval) > 0 ))
            {
                $inner_val = $retval;
            }

            $order_by = trim($order_by);
            $indexOfSpace = strpos($order_by, " ");

            if( $indexOfSpace !== FALSE )
            {
                $column = trim(substr($order_by,0,$indexOfSpace));
                $sortorder = trim(substr($order_by,$indexOfSpace));

                if ( strcasecmp($sortorder,"DESC") == 0 )
                {
                    switch ( $mode )
                    {
                        case 1:
                        case 2:
                            $comp = '$b[' . "'" . $column . "'" . '] <=> $a[' . "'" . $column . "'" . ']';
                        break;
                        case 3:
                            $comp = 'strtotime($b[' . "'" . $column . "'" . ']) <=> strtotime($a[' . "'" . $column . "'" . '])';
                        break;
                        case 4:
                            $comp = 'strnatcmp($b[' . "'" . $column . "'" . '], $a[' . "'" . $column . "'" . '])';
                        break;
                    }
                } else {
                    switch ( $mode )
                    {
                        case 1:
                        case 2:
                            $comp = '$a[' . "'" . $column . "'" . '] <=> $b[' . "'" . $column . "'" . ']';
                        break;
                        case 3:
                            $comp = 'strtotime($a[' . "'" . $column . "'" . ']) <=> strtotime($b[' . "'" . $column . "'" . '])';
                        break;
                        case 4:
                            $comp = 'strnatcmp($a[' . "'" . $column . "'" . '], $b[' . "'" . $column . "'" . '])';
                        break;
                    }
                }

                if($first_element)
                {
                    $retval = 'return ' . $comp . ';';
                    $first_element = FALSE;
                } else {
                    $retval = '$' . $column . '=' . $comp . '; if ( $' . $column . ' == 0 ) { ' . $inner_val . ' } else { return $' . $column . '; }';
                }
            }
        }

        #print "RET = $retval\n";

        return eval($retval);
    };
}

usort($a, get_usort_function_callable("dob ASC, id DESC, name DESC") );

print_r($a);
usort($wishlist_details, create_function('$a,$b', get_usort_function('wishlistcountryname ASC, wishlistname ASC')));
usort($_wishlist_details, get_usort_function_callable('wishlistcountryname ASC, wishlistname ASC') );