1<?php 2 3declare(strict_types=1); 4 5namespace GuzzleHttp\Psr7; 6 7final class Query 8{ 9 /** 10 * Parse a query string into an associative array. 11 * 12 * If multiple values are found for the same key, the value of that key 13 * value pair will become an array. This function does not parse nested 14 * PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2` 15 * will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`. 16 * 17 * @param string $str Query string to parse 18 * @param int|bool $urlEncoding How the query string is encoded 19 */ 20 public static function parse(string $str, $urlEncoding = true): array 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('+', ' ', (string) $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 (!array_key_exists($key, $result)) { 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 public static function build(array $params, $encoding = PHP_QUERY_RFC3986): string 72 { 73 if (!$params) { 74 return ''; 75 } 76 77 if ($encoding === false) { 78 $encoder = function (string $str): string { 79 return $str; 80 }; 81 } elseif ($encoding === PHP_QUERY_RFC3986) { 82 $encoder = 'rawurlencode'; 83 } elseif ($encoding === PHP_QUERY_RFC1738) { 84 $encoder = 'urlencode'; 85 } else { 86 throw new \InvalidArgumentException('Invalid type'); 87 } 88 89 $qs = ''; 90 foreach ($params as $k => $v) { 91 $k = $encoder((string) $k); 92 if (!is_array($v)) { 93 $qs .= $k; 94 $v = is_bool($v) ? (int) $v : $v; 95 if ($v !== null) { 96 $qs .= '='.$encoder((string) $v); 97 } 98 $qs .= '&'; 99 } else { 100 foreach ($v as $vv) { 101 $qs .= $k; 102 $vv = is_bool($vv) ? (int) $vv : $vv; 103 if ($vv !== null) { 104 $qs .= '='.$encoder((string) $vv); 105 } 106 $qs .= '&'; 107 } 108 } 109 } 110 111 return $qs ? (string) substr($qs, 0, -1) : ''; 112 } 113} 114