>bRZ慰'r9B#̙Qu@~~W189@O#Ms=vlCF9 CTcE+1u#J ~^͌'^oσ$1|+gNEB{}ݺbbՏ,wG[d]kNkPI|7 X3ݡiDB>E #o3݋9 JY "IM ,U Xs~|m4Y ~P2n0KD{-e>FH y@AY[ic[kW70~uYx.#~ԡ; ghǣib3j)(y}$DpTF~5ZCZO@q:/ݷ6ڟF͉RKPCu.lM (P:wY=B34EmFq݌c߾qFCeWMCoѬCa7ڰ|^$pO.a x bYlbt`3z*Y ~ׂnU|M Y򡆘jkQ˯VK~z(ԟsk`ʓw=tpܰ2Se/RWsz 8 ga >e¶ᵰR+ ۸q&^?P'08wBsKAIm%à,wfdZ3ul4qL.:_jqcT?y-왌!&߃0o9%t90&8ܵsye{}t5wZ.B !3 %S.V1y\fskXj)_8s-Xc A|Fd -YZcd/:@[~1 ?@v7Fe&_柚ѵÈ5u9=4ž:̗*.}Q67]1@r?qo$ysGa_ \ͻ3 WjP± ©Mw,%lxTVFX$u_ c6q`r4l[;Og̷#F#e K|=³> #z'?DC l3*"PTٷh z Ѯ6T̐W1! x ÏM( bSx:ιPb|1]kr_ yŅ-̧;gWҶf'4hR g[53X kܰwRR8sm5dS~04GrS I),tUJNSз@16G0J.Ӟ g,EhbGQu.@![jG Rbb5僖Q|4a:6kF16STXwiL,'$âC:H!Ѝ[E__'gߎ>BN<:kCeU.6yũBG2qv6EoH]嫯yw\WGv xR,n*GBqζ|RM#f/{\B:Ω% j# !z5=Q,G>?TW¦kW Gہ\o/}Lۢc'qtlARLNV֊Cfl|ܼBAq*M~`5 .bx6m5:;{2&pM9j[ P%oI9~Z~UR/|p0T Z^nOa\nO?(^_ 臱"$nbF jE +$s5ND/Q:1?3%'kvlk-u IQЭmfqwJ@,zdQEuA@WW(/bAK8`G %9K- )LT3E͉ןHGKY:rbav#DOk&u EuُV(6GjM!vGLp,I. N~p4,\o-*)MHlpII@&Hkesς8XGn@H62X)Iu` <%E_;K_GŠ+D)P D]Lr~vOԍHI8Ow˼,0_;ߚ=y>lZ Faw?s.ϴ'1 <@1ʑ sp2peCHk=LXބ *iGR(;M y ԡ8#ay"G (ۧWZqf=wl#ds,8<. 9/}8,.}Ӻ'Dwv8iZe顀]4D;jR `oQ7bjZѤq΅.dqHٰY\G^q^ؗ|_fJơdj`K'.%`Xq9ĔMDhYϮI`*4HX,mb^R|Z&硍4l[;Cw d:H^4f68jy,Ŀȭ'@WT"}2jv55>tXbFBx{Ķ* Q 1wb08ɄJ\DPD)۪hJ(ތVYX]Y o(H8W3,I݈4P-Lo3 )[c6w>Η`̋p 庝2ZtRO/&f:^EiFցhf fdxrԩg^QQySh2.T|~o$قg~InHXfBslpqp @"YGЖg ҥM>a'5Y`h% bZ [9Ib!:..YRoFg 9`#u/YQї%#OU %آ^LoU{%ԨZ_hjfvUC'X߶o٫KȜ1wչk2j^=Ļ֬ڧ$Q}jZ+ ww:/ that can be configured with the $flags parameter. * * PSR-7 UriInterface cannot distinguish between an empty component and a missing component as * getQuery(), getFragment() etc. always return a string. This means the URIs "/?#" and "/" are * treated equivalent which is not necessarily true according to RFC 3986. But that difference * is highly uncommon in reality. So this potential normalization is implied in PSR-7 as well. * * @param UriInterface $uri The URI to normalize * @param int $flags A bitmask of normalizations to apply, see constants * * @see https://tools.ietf.org/html/rfc3986#section-6.2 */ public static function normalize(UriInterface $uri, int $flags = self::PRESERVING_NORMALIZATIONS): UriInterface { if ($flags & self::CAPITALIZE_PERCENT_ENCODING) { $uri = self::capitalizePercentEncoding($uri); } if ($flags & self::DECODE_UNRESERVED_CHARACTERS) { $uri = self::decodeUnreservedCharacters($uri); } if ($flags & self::CONVERT_EMPTY_PATH && $uri->getPath() === '' && ($uri->getScheme() === 'http' || $uri->getScheme() === 'https') ) { $uri = $uri->withPath('/'); } if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') { $uri = $uri->withHost(''); } if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && Uri::isDefaultPort($uri)) { $uri = $uri->withPort(null); } if ($flags & self::REMOVE_DOT_SEGMENTS && !Uri::isRelativePathReference($uri)) { $uri = $uri->withPath(UriResolver::removeDotSegments($uri->getPath())); } if ($flags & self::REMOVE_DUPLICATE_SLASHES) { $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath())); } if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') { $queryKeyValues = explode('&', $uri->getQuery()); sort($queryKeyValues); $uri = $uri->withQuery(implode('&', $queryKeyValues)); } return $uri; } /** * Whether two URIs can be considered equivalent. * * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be * resolved against the same base URI. If this is not the case, determination of equivalence or difference of * relative references does not mean anything. * * @param UriInterface $uri1 An URI to compare * @param UriInterface $uri2 An URI to compare * @param int $normalizations A bitmask of normalizations to apply, see constants * * @see https://tools.ietf.org/html/rfc3986#section-6.1 */ public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, int $normalizations = self::PRESERVING_NORMALIZATIONS): bool { return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations); } private static function capitalizePercentEncoding(UriInterface $uri): UriInterface { $regex = '/(?:%[A-Fa-f0-9]{2})++/'; $callback = function (array $match) { return strtoupper($match[0]); }; return $uri->withPath( preg_replace_callback($regex, $callback, $uri->getPath()) )->withQuery( preg_replace_callback($regex, $callback, $uri->getQuery()) ); } private static function decodeUnreservedCharacters(UriInterface $uri): UriInterface { $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i'; $callback = function (array $match) { return rawurldecode($match[0]); }; return $uri->withPath( preg_replace_callback($regex, $callback, $uri->getPath()) )->withQuery( preg_replace_callback($regex, $callback, $uri->getQuery()) ); } private function __construct() { // cannot be instantiated } }