1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233:
<?php
namespace esperecyan\url;
use esperecyan\webidl\TypeHinter;
use esperecyan\webidl\TypeError;
/**
* The URLSearchParams class defines utility methods to work with the query string of a URL.
* @link https://url.spec.whatwg.org/#interface-urlsearchparams URL Standard
* @link https://developer.mozilla.org/docs/Web/API/URLSearchParams URLSearchParams - Web API Interfaces | MDN
*/
class URLSearchParams implements \IteratorAggregate
{
/**
* @var string[][] List of name-value pairs.
* @link https://url.spec.whatwg.org/#concept-urlsearchparams-list URL Standard
*/
private $list = [];
/**
* @var URL|null
* @link https://url.spec.whatwg.org/#concept-urlsearchparams-url-object URL Standard
*/
private $urlObject = null;
/**
* @link https://url.spec.whatwg.org/#concept-urlsearchparams-new URL Standard
* @param string[][]|string[]|string|URLSearchParams $init
* An array of two-element arrays with the first element the name and the second the value,
* associative array, USVString, or URLSearchParams.
*/
public function __construct($init = '')
{
$initValue = TypeHinter::to(
// The URL Standard expects an interface having a pair iterator to match “sequence<sequence<V>>”,
// but it matches “record<V, V>” on the API of esperecyan/webidl.
// So “ or esperecyan\url\URLSearchParams”is appended here.
'(sequence<sequence<USVString>> or record<USVString, USVString> or USVString'
. ' or esperecyan\url\URLSearchParams)',
$init
);
static::createNewURLSearchParamsObject(
$this,
is_string($initValue) ? preg_replace('/^\\?/u', '', $initValue) : $initValue
);
}
/**
* Creates a new URLSearchParams object.
* @link https://url.spec.whatwg.org/#concept-urlsearchparams-new URL Standard
* @param $query self|null
* @param $init string[][]|string[]|string|URLSearchParams|null
* @throws TypeError
* @return self
*/
private static function createNewURLSearchParamsObject($query, $init)
{
if (!$query) {
$query = new static();
}
if ($init instanceof URLSearchParams) {
$query->list = $init->list;
} elseif (is_array($init)) {
foreach ($init as $pair) {
if (count($pair) !== 2) {
throw new TypeError(
'URLSearchParams require name/value tuples when being initialized by a sequence.'
);
}
}
$query->list = $init;
} elseif ($init instanceof \esperecyan\webidl\Record) {
foreach ($init as $name => $value) {
$query->list[] = [$name, $value];
}
} else {
$query->list = lib\URLencoding::parseURLencodedString($init);
}
return $query;
}
/**
* A URLSearchParams object’s update steps.
* @link https://url.spec.whatwg.org/#concept-urlsearchparams-update URL Standard
*/
private function update()
{
if ($this->urlObject) {
\Closure::bind(function ($urlObject, $query) {
$urlObject->url->query = $query === '' ? null : $query;
}, null, $this->urlObject)->__invoke($this->urlObject, lib\URLencoding::serializeURLencoded($this->list));
}
}
/**
* Appends a new name-value pair whose name is name and value is value, to the list of name-value pairs.
* @link https://url.spec.whatwg.org/#dom-urlsearchparams-appendname-value URL Standard
* @param string $name A USVString.
* @param string $value A USVString.
*/
public function append($name, $value)
{
$this->list[] = [TypeHinter::to('USVString', $name, 0), TypeHinter::to('USVString', $value, 1)];
$this->update();
}
/**
* Removes all name-value pairs whose name is name.
* @link https://url.spec.whatwg.org/#dom-urlsearchparams-deletename URL Standard
* @param string $name A USVString.
*/
public function delete($name)
{
$nameString = TypeHinter::to('USVString', $name);
array_splice($this->list, 0, count($this->list), array_filter($this->list, function ($pair) use ($nameString) {
return $pair[0] !== $nameString;
}));
$this->update();
}
/**
* Returns the value of the first name-value pair whose name is name, and null if there is no such pair.
* @link https://url.spec.whatwg.org/#dom-urlsearchparams-getname URL Standard
* @param string $name
* @return string|null A USVString or null.
*/
public function get($name)
{
$nameString = TypeHinter::to('USVString', $name);
$value = null;
foreach ($this->list as $pair) {
if ($pair[0] === $nameString) {
$value = $pair[1];
break;
}
}
return $value;
}
/**
* Returns the values of all name-value pairs whose name is name, in list order, and the empty sequence otherwise.
* @link https://url.spec.whatwg.org/#dom-urlsearchparams-getallname URL Standard
* @param string $name A USVString.
* @return string[] An array of a USVString.
*/
public function getAll($name)
{
$nameString = TypeHinter::to('USVString', $name);
$values = [];
foreach ($this->list as $pair) {
if ($pair[0] === $nameString) {
$values[] = $pair[1];
}
}
return $values;
}
/**
* Returns true if there is a name-value pair whose name is name, and false otherwise.
* @link https://url.spec.whatwg.org/#dom-urlsearchparams-hasname URL Standard
* @param string $name A USVString.
* @return bool
*/
public function has($name)
{
return !is_null($this->get(TypeHinter::to('USVString', $name)));
}
/**
* If there are any name-value pairs whose name is name, set the value of the first such name-value pair to value and remove the others.
* Otherwise, append a new name-value pair whose name is name and value is value, to the list of name-value pairs.
* @link https://url.spec.whatwg.org/#dom-urlsearchparams-setname-value URL Standard
* @param string $name A USVString.
* @param string $value A USVString.
*/
public function set($name, $value)
{
$nameString = TypeHinter::to('USVString', $name, 0);
$valueString = TypeHinter::to('USVString', $value, 1);
$already = false;
foreach ($this->list as $key => &$pair) {
if ($pair[0] === $nameString) {
if ($already) {
unset($this->list[$key]);
} else {
$pair[1] = $valueString;
$already = true;
}
}
}
unset($pair);
if ($already) {
$this->list = array_values($this->list);
array_splice($this->list, 0, count($this->list), $this->list);
} else {
$this->list[] = [$nameString, $valueString];
}
$this->update();
}
/**
* Sorts all name-value pair by their names and comparing JavaScript strings (UTF-16).
* @link https://url.spec.whatwg.org/#dom-urlsearchparams-sort URL Standard
* @param string $name A USVString.
* @param string $value A USVString.
*/
public function sort()
{
array_multisort(array_map(function ($pair) {
return mb_convert_encoding($pair[0], 'UTF-16BE', 'UTF-8');
}, $this->list), SORT_STRING, range(1, count($this->list)), $this->list);
}
/**
* @uses lib\URLSearchParamsIterator
*/
public function getIterator()
{
return new lib\URLSearchParamsIterator($this->list, $this);
}
/**
* Returns the serialization of the URLSearchParams object's associated list of name-value pairs.
* @link https://url.spec.whatwg.org/#stringification-behavior URL Standard
* @return string A USVString.
*/
public function __toString()
{
return lib\URLencoding::serializeURLencoded($this->list);
}
}