首页 > 解决方案 > 创建没有 eval 的动态 if 语句

问题描述

有一个系统可以抓取其他网站,用户可以获得有关抓取内容的信息。用户应该能够根据抓取的内容(假设它的整数)创建过滤器:如果内容小于/大于/等于特定用户所需的数字等,则跳过结果。所以 PHP 应该能够检查这些东西,换句话说:创建一个语句,检查抓取的结果是否有效,也可以选择过滤器。

例子:

在这里我们可以看到一个带有随机数的 Excel 表(就像一个抓取的网站): 在此处输入图像描述

用户通过某些过滤器创建和过滤排除数字(相同的示例 excel 过滤器): 在此处输入图像描述

系统获取过滤器并过滤结果:

在此处输入图像描述

现在我需要用 PHP 做同样的事情。我在考虑 eval 但它在我的 PHP 上被禁用。

PHP 示例:

// the content which was scraped
$scrapedResults = [1, 10, 11, 1216, 15, 55, 556, 123, 225, -15,];

// the user created filters
$filters = [
    ['filter' => ['more' => 0], 'operator' => null,],
    ['filter' => ['less' => 15], 'operator' => 'and'],
    ['filter' => ['equal' => 1216], 'operator' => 'or'],
];

// the filter checker
function isValid(string $action, int $ruleArgument, int $target): bool
{
    switch ($action)
    {
        case 'more': return $ruleArgument > $target;
        case 'less': return $ruleArgument < $target;
        case 'equal': return $ruleArgument === $target;

        default: throw new InvalidArgumentException('unknown action given');
    }
}

$result = [];

foreach ($scrapedResults as $scrapedResult) {
    foreach ($filters as $filter) {
        // how to check with all of these filter clauses?
    }

    // by static this function shoud look like this:
    // if($target > 0 && $target < 15 || $tager === 1216) { return true; } else { return false; }
    // but how I should it created dynamically??
}

另一个 PHP 示例:

// the filter which user created:
$filters = [
    ['filter' => ['more' => 1], 'operator' => null,],
    ['filter' => ['less' => 15], 'operator' => 'and'],
    ['filter' => ['equal' => 1216], 'operator' => 'or'],
    ['filter' => ['equal' => 225], 'operator' => 'or'],
];

// the if clause which my code should generate:
// if($target > 1 && $target < 15 || $target === 1216 || $target === 225) {return true;} else {return false;}

这项任务有一个转折点。在 excel 过滤器中限制为 4 个附加过滤器。系统没有任何限制,所以我可以从 1 到 100000。

简而言之:我需要找到一种在没有 EVAL() 的情况下在 PHP 中创建 if 动态语句的方法

也欢迎任何有关此任务逻辑改进的建议!

标签: php

解决方案


我已经使用闭包实现了这一点,有一组数组定义了用于过滤器类型和运算符的回调函数。

每次应用过滤器时,它首先检查过滤器是否正常,然后使用array_filter()查找表中的相应回调来检查每个项目与过滤的值。一旦过滤了整个数字列表,它就会计算出如何将这些结果与以前的结果结合起来。回调再次知道这部分的逻辑......

// the content which was scraped
$scrapedResults = [1, 10, 11, 1216, 15, 55, 556, 123, 225, -15,];

// the user created filters
$filters = [
    ['filter' => ['more' => 0], 'operator' => null,],
    ['filter' => ['less' => 15], 'operator' => 'and'],
    ['filter' => ['equal' => 1216], 'operator' => 'or'],
];

// Implementation of the filters
$filterType = ['more' => function ($a, $b) { return $a > $b; },
    'less' => function ($a, $b) { return $a < $b; },
    'equal' => function ($a, $b) { return $a == $b; }];

// Implementation of the operators
$operators = ['and' => function ($old, $result ) {
                            return array_intersect($old, $result);
                        },
                'or' => function ($old, $result ) {
                            return array_merge($old, $result);
                        }];

$output = [];
foreach ( $filters as $filter ) {
    $currentType = array_keys($filter['filter'])[0];
    if ( !isset($filterType[$currentType]) ) {
        throw new InvalidArgumentException('unknown action given');
    }
    $filterValue = $filter['filter'][$currentType];
    $callback = $filterType[$currentType];
    $filterRes = array_filter($scrapedResults, function ($a) 
                            use ($callback, $filterValue) {
                    return $callback($a, $filterValue);
                });
    
    if ( $filter['operator'] == null )  {
        $output = $filterRes;
    }
    else if ( isset($operators[$filter['operator']]) )   {
        $output = $operators[$filter['operator']]($output, $filterRes);
    }
}
echo "output->";
print_r($output);

这输出...

output->Array
(
    [0] => 1
    [1] => 10
    [2] => 11
    [3] => 1216
)

推荐阅读