PHP
2 weeks ago
class-convertedurl.php
2 weeks ago
class-cssurlprocessor.php
2 weeks ago
class-nativeurlintextprocessorwrapper.php
2 weeks ago
class-urlintextprocessor.php
2 weeks ago
class-urlrewritecache.php
2 weeks ago
class-wpurl.php
2 weeks ago
class-wpwhatwgurl.php
2 weeks ago
class-wpwhatwgurlsearchparams.php
2 weeks ago
functions.php
2 weeks ago
public-suffix-list.php
2 weeks ago
class-wpwhatwgurlsearchparams.php
176 lines
| 1 | <?php |
| 2 | |
| 3 | namespace WordPress\DataLiberation\URL; |
| 4 | |
| 5 | /** |
| 6 | * Adapter that exposes the WHATWG URLSearchParams API on top of WPWhatwgUrl. |
| 7 | * |
| 8 | * The PHP 8.5 native Uri\WhatWg\Url parser does not expose a URLSearchParams |
| 9 | * counterpart, but the rest of the codebase (and Rowbot's URL implementation) |
| 10 | * relies on $url->searchParams->get()/has()/set()/delete()/append(). This |
| 11 | * class reads from and writes to the owning URL's query string so callers can |
| 12 | * keep using the familiar property-based API regardless of which parser |
| 13 | * backs WPURL::parse(). |
| 14 | * |
| 15 | * Pairs are serialized as application/x-www-form-urlencoded — that matches |
| 16 | * the WHATWG URLSearchParams stringifier and what Rowbot produces. |
| 17 | */ |
| 18 | class WPWhatwgUrlSearchParams implements \Countable, \IteratorAggregate { |
| 19 | |
| 20 | /** @var WPWhatwgUrl */ |
| 21 | private $owner; |
| 22 | |
| 23 | public function __construct( WPWhatwgUrl $owner ) { |
| 24 | $this->owner = $owner; |
| 25 | } |
| 26 | |
| 27 | public function __get( $name ) { |
| 28 | if ( 'size' === $name ) { |
| 29 | return count( $this->read_pairs() ); |
| 30 | } |
| 31 | return null; |
| 32 | } |
| 33 | |
| 34 | public function get( $name ) { |
| 35 | foreach ( $this->read_pairs() as $pair ) { |
| 36 | if ( $pair[0] === (string) $name ) { |
| 37 | return $pair[1]; |
| 38 | } |
| 39 | } |
| 40 | return null; |
| 41 | } |
| 42 | |
| 43 | public function getAll( $name ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid |
| 44 | $values = array(); |
| 45 | foreach ( $this->read_pairs() as $pair ) { |
| 46 | if ( $pair[0] === (string) $name ) { |
| 47 | $values[] = $pair[1]; |
| 48 | } |
| 49 | } |
| 50 | return $values; |
| 51 | } |
| 52 | |
| 53 | public function has( $name ) { |
| 54 | foreach ( $this->read_pairs() as $pair ) { |
| 55 | if ( $pair[0] === (string) $name ) { |
| 56 | return true; |
| 57 | } |
| 58 | } |
| 59 | return false; |
| 60 | } |
| 61 | |
| 62 | public function set( $name, $value ) { |
| 63 | $name = (string) $name; |
| 64 | $value = (string) $value; |
| 65 | $pairs = $this->read_pairs(); |
| 66 | $result = array(); |
| 67 | $found = false; |
| 68 | foreach ( $pairs as $pair ) { |
| 69 | if ( $pair[0] === $name ) { |
| 70 | if ( ! $found ) { |
| 71 | $result[] = array( $name, $value ); |
| 72 | $found = true; |
| 73 | } |
| 74 | continue; |
| 75 | } |
| 76 | $result[] = $pair; |
| 77 | } |
| 78 | if ( ! $found ) { |
| 79 | $result[] = array( $name, $value ); |
| 80 | } |
| 81 | $this->write_pairs( $result ); |
| 82 | } |
| 83 | |
| 84 | public function append( $name, $value ) { |
| 85 | $pairs = $this->read_pairs(); |
| 86 | $pairs[] = array( (string) $name, (string) $value ); |
| 87 | $this->write_pairs( $pairs ); |
| 88 | } |
| 89 | |
| 90 | public function delete( $name ) { |
| 91 | $name = (string) $name; |
| 92 | $result = array(); |
| 93 | foreach ( $this->read_pairs() as $pair ) { |
| 94 | if ( $pair[0] === $name ) { |
| 95 | continue; |
| 96 | } |
| 97 | $result[] = $pair; |
| 98 | } |
| 99 | $this->write_pairs( $result ); |
| 100 | } |
| 101 | |
| 102 | public function count(): int { |
| 103 | return count( $this->read_pairs() ); |
| 104 | } |
| 105 | |
| 106 | public function getIterator(): \Iterator { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid |
| 107 | return new \ArrayIterator( $this->read_pairs() ); |
| 108 | } |
| 109 | |
| 110 | // Method name mirrors the WHATWG URLSearchParams API. |
| 111 | // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid |
| 112 | public function toString(): string { |
| 113 | return $this->serialize( $this->read_pairs() ); |
| 114 | } |
| 115 | |
| 116 | public function __toString(): string { |
| 117 | return $this->toString(); |
| 118 | } |
| 119 | |
| 120 | /** |
| 121 | * @return array<int, array{0: string, 1: string}> |
| 122 | */ |
| 123 | private function read_pairs() { |
| 124 | $search = $this->owner->__get( 'search' ); |
| 125 | if ( '' === $search || '?' === $search ) { |
| 126 | return array(); |
| 127 | } |
| 128 | $query = '?' === $search[0] ? substr( $search, 1 ) : $search; |
| 129 | $pairs = array(); |
| 130 | foreach ( explode( '&', $query ) as $segment ) { |
| 131 | if ( '' === $segment ) { |
| 132 | continue; |
| 133 | } |
| 134 | $eq = strpos( $segment, '=' ); |
| 135 | if ( false === $eq ) { |
| 136 | $pairs[] = array( self::decode( $segment ), '' ); |
| 137 | } else { |
| 138 | $pairs[] = array( |
| 139 | self::decode( substr( $segment, 0, $eq ) ), |
| 140 | self::decode( substr( $segment, $eq + 1 ) ), |
| 141 | ); |
| 142 | } |
| 143 | } |
| 144 | return $pairs; |
| 145 | } |
| 146 | |
| 147 | private function write_pairs( array $pairs ) { |
| 148 | if ( empty( $pairs ) ) { |
| 149 | $this->owner->__set( 'search', '' ); |
| 150 | return; |
| 151 | } |
| 152 | $this->owner->__set( 'search', '?' . $this->serialize( $pairs ) ); |
| 153 | } |
| 154 | |
| 155 | private function serialize( array $pairs ) { |
| 156 | $parts = array(); |
| 157 | foreach ( $pairs as $pair ) { |
| 158 | $parts[] = self::encode( $pair[0] ) . '=' . self::encode( $pair[1] ); |
| 159 | } |
| 160 | return implode( '&', $parts ); |
| 161 | } |
| 162 | |
| 163 | private static function encode( $value ) { |
| 164 | // urlencode() produces application/x-www-form-urlencoded output — |
| 165 | // spaces become "+" and the rest is percent-encoded — matching the |
| 166 | // WHATWG URLSearchParams stringifier. rawurlencode() would percent- |
| 167 | // encode spaces as %20 instead, which is wrong here. |
| 168 | // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.urlencode_urlencode |
| 169 | return urlencode( $value ); |
| 170 | } |
| 171 | |
| 172 | private static function decode( $value ) { |
| 173 | return urldecode( $value ); |
| 174 | } |
| 175 | } |
| 176 |