【问题标题】:PHP http_build_query with two array keys that are samePHP http_build_query 具有两个相同的数组键
【发布时间】:2020-09-25 18:43:07
【问题描述】:

我想使用 http_build_query 构建这个 url:

https://www.googleapis.com/freebase/v1/topic/m/0d6lp?filter=/common/topic/notable_for&filter=/common/topic/alias

请注意,“filter=”参数在 url 中出现两次,用于指定两个过滤器。

我尝试过这样做,但遇到了问题:

$service_url = 'https://www.googleapis.com/freebase/v1/topic';
$mid = '/m/0d6lp';
$params = array('filter' => '/common/topic/notable_for', 'filter' =>   '/common/topic/alias');
$url = $service_url . $mid . '?' . http_build_query($params);

问题在于“过滤器”数组键重复两次,只有最后一个参数出现在 http_build_query 中。如何用两个过滤器构造原始 url?

【问题讨论】:

标签: php


【解决方案1】:

这里的问题当然是 PHP 数组(哈希)中的每个键只能有一个值。从本质上讲,PHP 哈希并不是查询字符串的良好表示,因为查询字符串具有顺序并且对键的唯一性没有限制。

为了解决这个问题,您需要一个可以处理重复键的特殊查询字符串构建器:

class QueryString {
    private $parts = array();

    public function add($key, $value) {
        $this->parts[] = array(
            'key'   => $key,
            'value' => $value
        );
    }

    public function build($separator = '&', $equals = '=') {
        $queryString = array();

        foreach($this->parts as $part) {
            $queryString[] = urlencode($part['key']) . $equals . urlencode($part['value']);
        }

        return implode($separator, $queryString);
    }

    public function __toString() {
        return $this->build();
    }
}

使用示例(Codepad Demo):

$qs = new QueryString();
$qs->add('filter', '1');
$qs->add('filter', '2');
var_dump($qs->build()); // filter=1&filter=2

【讨论】:

  • 但是当通过$_GET['filter']访问它时,它将返回2而不是1 & 2。您必须在这里使用 2 个不同的密钥。
  • @YogeshSuthar 那是因为 $_GET 是一个散列(并且不能准确地表示一个查询字符串)。当 PHP 解析 QS 时,它会覆盖重复项。如果您检查具有重复键的查询字符串(php://input 用于 POST 或$_SERVER['QUERY_STRING'] 用于 GET),您可以提取这两个值。还可以考虑像filter[] 这样的键,PHP 会将其聚合到一个数组中。查询字符串允许多次设置键。
  • @YogeshSuthar 还要检查Java handles it 它如何为每个键返回一个值数组。 This SO question 还确认这是一个已建立的(尽管没有记录)标准,允许为键分配多个值。
【解决方案2】:

我已经扩展了@Bailey Parker 的回答,以处理他回答的第一条评论中提出的问题。此代码将创建查询字符串,其中一个键具有多个值,并且两个值都将使用 []

保留
class QueryString
{
        private $parts = array();

        public function add($key, $value) {
                if(empty($value))return;
                if(is_array($value))
                {
                        foreach ($value as $v)
                                $this->add($key,$v);
                }
                else
                {
                        $this->parts[$key][] = $value;
                }

        }

        public function build($separator = '&', $equals = '=') {
                $queryString = array();

                $parts = array();
                foreach($this->parts as $key=>$value)
                {
                        if(count($value) > 1)
                                $parts[$key] = $value;
                        else
                                $parts[$key] = $value[0];
                }
                $query = http_build_query($parts);
                return preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '[]=', $query);;

        }

        public function __toString() {
                return $this->build();
        }

}

此代码还可以接受特定键的值数组

$qs = new QueryString();
$qs->add('trialId', array('1','2'));
$qs->add('packageId', '12');
$qs->add('frequencyId', '4');
var_dump($qs->build());

这个类会产生普通的查询字符串,key有多个值加上[]号,输出是这样的

?frequencyId=4&packageId=12&trialId[]=1&trialId[]=2

【讨论】:

    【解决方案3】:
    1. 您不能在 accoc 数组中存储两个或多个具有一个索引的值。
    2. Api 不采用 filter[] 之类的值

    所以,我建议您创建自己的函数来构建该查询。

    【讨论】:

    • API 确实支持多个过滤器。您可以运行我在浏览器中提供的 url 并查看两个过滤器是否适用。 API文档中也提到过。
    • @Ninja,已从答案中删除。
    【解决方案4】:

    不是最好的解决方案,但我有一个解决方法:

    $item = [
       'key1' => array('value1','value2'),
       'key2' => array('value3','value4')
    ];
    
    $array=[
        'aaa' => 'aaa',
        'null' => 'null&'.preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', http_build_query($item,null,'&')),
        'zzz' => 'zzz'
    ];
    
    echo urldecode(http_build_query($array));
    

    输出:

    aaa=aaa&null=null&key1=value1&key1=value2&key2=value3&key2=value4&zzz=zzz

    警告:这可能行不通,因为 url 将是:

    aaa=aaa&null=null%26key1%3Dvalue1%26key1%3Dvalue2%26key2%3Dvalue3%26key2%3Dvalue4&zzz=zzz

    编辑: 其他选项将在将结果分配给 url 之前将结果预置:

    preg_replace('/%5B(?:[0-9]|[1-9][0-9]+)%5D=/', '=', http_build_query($item))
    

    输出:

    key1=value1&key1=value2&key2=value3&key2=value4

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-12
      • 1970-01-01
      • 2022-01-17
      相关资源
      最近更新 更多