Skip to content

Commit 3aa6b1a

Browse files
committed
🛀 Uri constructor now also accepts arrays (parse_url), eliminate UriExtended, move methods to message_helpers
1 parent b6169a1 commit 3aa6b1a

File tree

8 files changed

+239
-295
lines changed

8 files changed

+239
-295
lines changed

src/Psr17/factory_helpers.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
namespace chillerlan\HTTP\Psr17;
1010

1111
use Psr\Http\Message\ServerRequestInterface;
12-
use chillerlan\HTTP\Psr7\{ServerRequest, Stream, UriExtended};
12+
use chillerlan\HTTP\Psr7\{ServerRequest, Stream, Uri};
1313
use InvalidArgumentException;
1414
use Psr\Http\Message\StreamInterface;
1515

@@ -77,7 +77,7 @@ function_exists('getallheaders') ? getallheaders() : [],
7777
/**
7878
* Get a Uri populated with values from $_SERVER.
7979
*/
80-
function create_uri_from_globals():UriExtended{
80+
function create_uri_from_globals():Uri{
8181
$parts = [];
8282
$hasPort = false;
8383
$hasQuery = false;
@@ -118,8 +118,7 @@ function create_uri_from_globals():UriExtended{
118118
$parts['query'] = $_SERVER['QUERY_STRING'];
119119
}
120120

121-
/** @noinspection PhpIncompatibleReturnTypeInspection */
122-
return UriExtended::fromParts($parts);
121+
return new Uri($parts);
123122
}
124123

125124
/**

src/Psr7/Uri.php

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
use InvalidArgumentException;
1414
use Psr\Http\Message\UriInterface;
1515

16-
use function call_user_func_array, explode, filter_var, is_string, ltrim, parse_url,
16+
use function call_user_func_array, explode, filter_var, is_array, is_string, ltrim, parse_url,
1717
preg_replace_callback, rawurlencode, strpos, strtolower, ucfirst;
1818

1919
use const FILTER_FLAG_IPV6, FILTER_VALIDATE_IP;
@@ -53,18 +53,24 @@ class Uri implements UriInterface{
5353
/**
5454
* Uri constructor.
5555
*
56+
* @param string|array $uri
57+
*
5658
* @throws \InvalidArgumentException
5759
*/
58-
public function __construct(string $uri = null){
60+
public function __construct($uri = null){
61+
62+
if($uri !== null){
5963

60-
if($uri !== null && $uri !== ''){
61-
$parts = parse_url($uri);
64+
if(is_string($uri)){
65+
$uri = parse_url($uri);
66+
}
6267

63-
if($parts === false){
68+
if(!is_array($uri)){
6469
throw new InvalidArgumentException('invalid URI: "'.$uri.'"');
6570
}
6671

67-
$this->parseUriParts($parts);
72+
$this->parseUriParts($uri);
73+
$this->validateState();
6874
}
6975

7076
}

src/Psr7/UriExtended.php

Lines changed: 0 additions & 159 deletions
This file was deleted.

src/Psr7/message_helpers.php

Lines changed: 119 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
namespace chillerlan\HTTP\Psr7;
1010

1111
use InvalidArgumentException, TypeError;
12-
use Psr\Http\Message\{MessageInterface, RequestInterface, ResponseInterface, UploadedFileInterface};
12+
use Psr\Http\Message\{MessageInterface, RequestInterface, ResponseInterface, UploadedFileInterface, UriInterface};
1313

14-
use function array_combine, array_keys, array_map, array_merge, array_values, call_user_func_array, count, explode,
14+
use function array_combine, array_filter, array_keys, array_map, array_merge, array_values, call_user_func_array, count, explode,
1515
gzdecode, gzinflate, gzuncompress, implode, is_array, is_bool, is_iterable, is_numeric, is_scalar, is_string,
16-
json_decode, json_encode, parse_str, parse_url, rawurlencode, simplexml_load_string, sort, strtolower, trim,
16+
json_decode, json_encode, parse_str, parse_url, rawurldecode, rawurlencode, simplexml_load_string, sort, strtolower, trim,
1717
ucfirst, uksort;
1818

1919
use const PHP_URL_QUERY, SORT_STRING;
@@ -499,3 +499,119 @@ function decompress_content(MessageInterface $message):string{
499499
}
500500

501501
}
502+
503+
/**
504+
* Whether the URI is absolute, i.e. it has a scheme.
505+
*
506+
* An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true
507+
* if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative
508+
* to another URI, the base URI. Relative references can be divided into several forms:
509+
* - network-path references, e.g. '//example.com/path'
510+
* - absolute-path references, e.g. '/path'
511+
* - relative-path references, e.g. 'subpath'
512+
*
513+
* @see Uri::isNetworkPathReference
514+
* @see Uri::isAbsolutePathReference
515+
* @see Uri::isRelativePathReference
516+
* @link https://tools.ietf.org/html/rfc3986#section-4
517+
*/
518+
function uriIsAbsolute(UriInterface $uri):bool{
519+
return $uri->getScheme() !== '';
520+
}
521+
522+
/**
523+
* Whether the URI is a network-path reference.
524+
*
525+
* A relative reference that begins with two slash characters is termed an network-path reference.
526+
*
527+
* @link https://tools.ietf.org/html/rfc3986#section-4.2
528+
*/
529+
function uriIsNetworkPathReference(UriInterface $uri):bool{
530+
return $uri->getScheme() === '' && $uri->getAuthority() !== '';
531+
}
532+
533+
/**
534+
* Whether the URI is a absolute-path reference.
535+
*
536+
* A relative reference that begins with a single slash character is termed an absolute-path reference.
537+
*
538+
* @link https://tools.ietf.org/html/rfc3986#section-4.2
539+
*/
540+
function uriIsAbsolutePathReference(UriInterface $uri):bool{
541+
return $uri->getScheme() === '' && $uri->getAuthority() === '' && isset($uri->getPath()[0]) && $uri->getPath()[0] === '/';
542+
}
543+
544+
/**
545+
* Whether the URI is a relative-path reference.
546+
*
547+
* A relative reference that does not begin with a slash character is termed a relative-path reference.
548+
*
549+
* @return bool
550+
* @link https://tools.ietf.org/html/rfc3986#section-4.2
551+
*/
552+
function uriIsRelativePathReference(UriInterface $uri):bool{
553+
return $uri->getScheme() === '' && $uri->getAuthority() === '' && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/');
554+
}
555+
556+
/**
557+
* removes a specific query string value.
558+
*
559+
* Any existing query string values that exactly match the provided key are
560+
* removed.
561+
*
562+
* @param string $key Query string key to remove.
563+
*/
564+
function uriWithoutQueryValue(UriInterface $uri, string $key):UriInterface{
565+
$current = $uri->getQuery();
566+
567+
if($current === ''){
568+
return $uri;
569+
}
570+
571+
$decodedKey = rawurldecode($key);
572+
573+
$result = array_filter(explode('&', $current), function($part) use ($decodedKey){
574+
return rawurldecode(explode('=', $part)[0]) !== $decodedKey;
575+
});
576+
577+
return $uri->withQuery(implode('&', $result));
578+
}
579+
580+
/**
581+
* adds a specific query string value.
582+
*
583+
* Any existing query string values that exactly match the provided key are
584+
* removed and replaced with the given key value pair.
585+
*
586+
* A value of null will set the query string key without a value, e.g. "key"
587+
* instead of "key=value".
588+
*
589+
* @param string $key Key to set.
590+
* @param string|null $value Value to set
591+
*/
592+
function uriWithQueryValue(UriInterface $uri, string $key, string $value = null):UriInterface{
593+
$current = $uri->getQuery();
594+
595+
if($current === ''){
596+
$result = [];
597+
}
598+
else{
599+
$decodedKey = rawurldecode($key);
600+
$result = array_filter(explode('&', $current), function($part) use ($decodedKey){
601+
return rawurldecode(explode('=', $part)[0]) !== $decodedKey;
602+
});
603+
}
604+
605+
// Query string separators ("=", "&") within the key or value need to be encoded
606+
// (while preventing double-encoding) before setting the query string. All other
607+
// chars that need percent-encoding will be encoded by withQuery().
608+
$replaceQuery = ['=' => '%3D', '&' => '%26'];
609+
$key = strtr($key, $replaceQuery);
610+
611+
$result[] = $value !== null
612+
? $key.'='.strtr($value, $replaceQuery)
613+
: $key;
614+
615+
return $uri->withQuery(implode('&', $result));
616+
}
617+

tests/Psr17/FactoryHelpersTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
namespace chillerlan\HTTPTest\Psr17;
1212

13-
use chillerlan\HTTP\Psr7\{UploadedFile, UriExtended};
13+
use chillerlan\HTTP\Psr7\{UploadedFile, Uri};
1414
use InvalidArgumentException, stdClass;
1515
use PHPUnit\Framework\TestCase;
1616
use Psr\Http\Message\StreamInterface;
@@ -98,7 +98,7 @@ public function dataGetUriFromGlobals():array{
9898
public function testCreateUriFromGlobals(string $expected, array $serverParams){
9999
$_SERVER = $serverParams;
100100

101-
$this::assertEquals(new UriExtended($expected), create_uri_from_globals());
101+
$this::assertEquals(new Uri($expected), create_uri_from_globals());
102102
}
103103

104104
public function testCreateServerRequestFromGlobals():void{
@@ -157,7 +157,7 @@ public function testCreateServerRequestFromGlobals():void{
157157
$this::assertSame($_GET, $server->getQueryParams());
158158

159159
$this::assertEquals(
160-
new UriExtended('https://www.example.org/blog/article.php?id=10&user=foo'),
160+
new Uri('https://www.example.org/blog/article.php?id=10&user=foo'),
161161
$server->getUri()
162162
);
163163

0 commit comments

Comments
 (0)