除了我想要的元素,如何删除所有数组元素?

58
我有一个控制器,它从HTML表单中获取POST参数,然后将它们发送到模型,模型将把数组插入到Cassandra数据库中。
它是防止SQL注入的,因为它是NoSQL,但是我担心用户可能会模拟100k个POST参数或者添加一些我不需要的参数,这些参数都会被插入到数据库中。如何确保只有我需要的值才留在我的数组中。
例如:
$post = ['parent_id', 'type', 'title', 'body', 'tags']; // Good
$post = ['parent_id', 'type', 'title', 'body', 'tags', 'one', 'two', 'three'] // Bad

我该如何确保我的数组会取消设置所有不在示例中的元素?

8个回答

103

通过列入白名单,您可以得到预期的条目。

<?php
$post = array( 
    'parent_id' => 1,
    'type' => 'foo', 
    'title' => 'bar', 
    'body' => 'foo bar', 
    'tags' => 'foo, bar', 
    'one' => 'foo',
    'two' => 'bar',
    'three' => 'qux'
);

$whitelist = array(
    'parent_id',
    'type',
    'title',
    'body',
    'tags'
);

$filtered = array_intersect_key( $post, array_flip( $whitelist ) );

var_dump( $filtered );

无论如何,使用Cassandra作为数据存储并不意味着你不需要对收到的数据进行验证。


50

您正在寻找array_intersect函数:

$good = ['parent_id', 'type', 'title', 'body', 'tags'];
$post = ['parent_id', 'type', 'title', 'body', 'tags', 'one', 'two', 'three'];

print_r(array_intersect($good, $post));

在此查看演示.

当然,这个具体的例子并没有太多意义,因为它是基于数组的工作的,但还有一个array_intersect_key,它基于键执行相同的操作。


2
@Qmal:$good 作为 具有良好的名称,而 $post 则将它们作为 。最简单的解决方法是传入 array_flip($good) 而不是 $good,这样两个输入都具有良好的名称作为键。 - Jon
答案和评论中的codepad链接已恢复为默认的“hello world”脚本。 :-( - TecBrat

3

多维数组怎么办?我花了几个小时研究这个问题,但没有找到最优解。所以我自己写了一个。

function allow_keys($arr, $keys)
    {
        $saved = [];

        foreach ($keys as $key => $value) {
            if (is_int($key) || is_int($value)) {
                $keysKey = $value;
            } else {
                $keysKey = $key;
            }
            if (isset($arr[$keysKey])) {

                $saved[$keysKey] = $arr[$keysKey];
                if (is_array($value)) {

                    $saved[$keysKey] = allow_keys($saved[$keysKey], $keys[$keysKey]);
                }
            }
        }
        return $saved;
    }

使用示例: example

$array = [
        'key1' => 'kw',
        'loaa'=> ['looo'],
        'k'    => [
            'prope' => [
                'prop'  => ['proo', 'prot', 'loolooo', 'de'],
                'prop2' => ['hun' => 'lu'],
            ],
            'prop1' => [

            ],
        ],
    ];

调用:示例

allow_keys($array, ['key1', 'k' => ['prope' => ['prop' => [0, 1], 'prop2']]])

输出:

Array ( [key1] => kw [k] => Array ( [prope] => Array ( [prop] => Array ( [0] => proo [1] => prot ) [prop2] => Array ( [hun] => lu ) ) ) ) 

所以,您可以从多维数组中获取所需的键。它不仅限于“多维”,您可以通过传递一个数组来使用它,如下所示:

['key1', 'loaa']

您得到的输出:

Array ( [key1] => kw [loaa] => Array ( [0] => looo ) )

干杯!


2
值得记住的是虽然array_intersectarray_intersect_key很好,但它们可能过于复杂。在我的情况下,我只想要1个元素留下,因此最简单的选择就是根据我需要的键/值重建我想要的数组。因此,我想知道在什么时候array_intersect 不再值得使用,你只需要使用$new = array('whatI'=>'want');就可以了。我相信在OP中这是值得的,但在较小的情况下可能过于复杂。

另外,作为对原始问题的回答,仅仅使用unset可能是一种更便宜的选择 - unset($post['one'],$post['two'],$post['three'])。同样,这关系到此时何时变得太低效,而array_intersect函数更好。


真不敢相信我没想到那个哈哈哈哈哈 - Constant
问题解决了,你真是救了我的一天,点赞! - Constant

1
如果你正在处理关联数组并且由于某种原因不想使用array_intersect_key(),那么你也可以采用更简单的方法,手动构建一个新数组,使用旧数组中所需的值。
$post = array(
    'parent_id' => 1,
    'type' => "post",
    'title' => "Post title",
    'body' => "Post body",
    'tags' => "Post tags",
    'malicious' => "Robert'); DROP TABLE students;--"
);
$good = array(
    'parent_id' => $post['parent_id'],
    'type' => $post['type'],
    'title' => $post['title'],
    'body' => $post['body'],
    'tags' => $post['tags']
);

1
这被称为白名单,你的例子有误导性,因为$_POST是一个关联数组。
$post = [
    'parent_id' => 'val',
    'type' => 'val',
    'title' => 'val',
    'body' => 'val',
    'tags' => 'val',
    'one' => 'val',
    'two' => 'val',
    'three'=>'val',
];

$whitelist = ['parent_id', 'type', 'title', 'body', 'tags'];

$sanitized_post = array_whitelist_assoc($post, $whitelist);

这是我为关联数组创建的白名单功能。

if(!function_exists('array_whitelist_assoc')){

    /**
     * Returns an associative array containing all the entries of array1 which have keys that are present in all the arguments when using their values as keys.
     *
     * @param array $array The array with master keys to check.
     * @param array $array2 An array to compare keys against its values.
     * @return array $array2,... A variable list of arrays to compare.
     * 
     */

    function array_whitelist_assoc(Array $array1, Array $array2) {

        if(func_num_args() > 2){
            $args = func_get_args();
            array_shift($args);
            $array2 = call_user_func_array('array_merge', $args);
        } 
        return array_intersect_key($array1, array_flip($array2)); 
    }
}

1

这将输出与 $post_allowed 相同的内容。它所做的就是只允许 $post_input 中存在于 $post_allow 中的值。

$post_allowed = ['parent_id', 'type', 'title', 'body', 'tags'];
$post_input   = ['parent_id', 'type', 'title', 'body', 'tags', 'one', 'two', 'three'];
$post = array_intersect($post_input, $post_allowed);

0

1
由于这个答案只有一个链接,与其他完整的答案相比显然缺乏信息,因此它更适合作为评论。 - mickmackusa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接