1<?php
2
3namespace GuzzleHttp\Psr7;
4
5final class Query
6{
7    /**
8     * Parse a query string into an associative array.
9     *
10     * If multiple values are found for the same key, the value of that key
11     * value pair will become an array. This function does not parse nested
12     * PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
13     * will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
14     *
15     * @param string   $str         Query string to parse
16     * @param int|bool $urlEncoding How the query string is encoded
17     *
18     * @return array
19     */
20    public static function parse($str, $urlEncoding = true)
21    {
22        $result = [];
23
24        if ($str === '') {
25            return $result;
26        }
27
28        if ($urlEncoding === true) {
29            $decoder = function ($value) {
30                return rawurldecode(str_replace('+', ' ', $value));
31            };
32        } elseif ($urlEncoding === PHP_QUERY_RFC3986) {
33            $decoder = 'rawurldecode';
34        } elseif ($urlEncoding === PHP_QUERY_RFC1738) {
35            $decoder = 'urldecode';
36        } else {
37            $decoder = function ($str) {
38                return $str;
39            };
40        }
41
42        foreach (explode('&', $str) as $kvp) {
43            $parts = explode('=', $kvp, 2);
44            $key = $decoder($parts[0]);
45            $value = isset($parts[1]) ? $decoder($parts[1]) : null;
46            if (!isset($result[$key])) {
47                $result[$key] = $value;
48            } else {
49                if (!is_array($result[$key])) {
50                    $result[$key] = [$result[$key]];
51                }
52                $result[$key][] = $value;
53            }
54        }
55
56        return $result;
57    }
58
59    /**
60     * Build a query string from an array of key value pairs.
61     *
62     * This function can use the return value of `parse()` to build a query
63     * string. This function does not modify the provided keys when an array is
64     * encountered (like `http_build_query()` would).
65     *
66     * @param array     $params   Query string parameters.
67     * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
68     *                            to encode using RFC3986, or PHP_QUERY_RFC1738
69     *                            to encode using RFC1738.
70     *
71     * @return string
72     */
73    public static function build(array $params, $encoding = PHP_QUERY_RFC3986)
74    {
75        if (!$params) {
76            return '';
77        }
78
79        if ($encoding === false) {
80            $encoder = function ($str) {
81                return $str;
82            };
83        } elseif ($encoding === PHP_QUERY_RFC3986) {
84            $encoder = 'rawurlencode';
85        } elseif ($encoding === PHP_QUERY_RFC1738) {
86            $encoder = 'urlencode';
87        } else {
88            throw new \InvalidArgumentException('Invalid type');
89        }
90
91        $qs = '';
92        foreach ($params as $k => $v) {
93            $k = $encoder($k);
94            if (!is_array($v)) {
95                $qs .= $k;
96                if ($v !== null) {
97                    $qs .= '=' . $encoder($v);
98                }
99                $qs .= '&';
100            } else {
101                foreach ($v as $vv) {
102                    $qs .= $k;
103                    if ($vv !== null) {
104                        $qs .= '=' . $encoder($vv);
105                    }
106                    $qs .= '&';
107                }
108            }
109        }
110
111        return $qs ? (string) substr($qs, 0, -1) : '';
112    }
113}
114