README.md
1# PSR-7 Message Implementation
2
3This repository contains a full [PSR-7](https://www.php-fig.org/psr/psr-7/)
4message implementation, several stream decorators, and some helpful
5functionality like query string parsing.
6
7
8
9
10
11## Features
12
13This package comes with a number of stream implementations and stream
14decorators.
15
16
17## Installation
18
19```shell
20composer require guzzlehttp/psr7
21```
22
23## Version Guidance
24
25| Version | Status | PHP Version |
26|---------|---------------------|--------------|
27| 1.x | Security fixes only | >=5.4,<8.1 |
28| 2.x | Latest | >=7.2.5,<8.4 |
29
30
31## AppendStream
32
33`GuzzleHttp\Psr7\AppendStream`
34
35Reads from multiple streams, one after the other.
36
37```php
38use GuzzleHttp\Psr7;
39
40$a = Psr7\Utils::streamFor('abc, ');
41$b = Psr7\Utils::streamFor('123.');
42$composed = new Psr7\AppendStream([$a, $b]);
43
44$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));
45
46echo $composed; // abc, 123. Above all listen to me.
47```
48
49
50## BufferStream
51
52`GuzzleHttp\Psr7\BufferStream`
53
54Provides a buffer stream that can be written to fill a buffer, and read
55from to remove bytes from the buffer.
56
57This stream returns a "hwm" metadata value that tells upstream consumers
58what the configured high water mark of the stream is, or the maximum
59preferred size of the buffer.
60
61```php
62use GuzzleHttp\Psr7;
63
64// When more than 1024 bytes are in the buffer, it will begin returning
65// false to writes. This is an indication that writers should slow down.
66$buffer = new Psr7\BufferStream(1024);
67```
68
69
70## CachingStream
71
72The CachingStream is used to allow seeking over previously read bytes on
73non-seekable streams. This can be useful when transferring a non-seekable
74entity body fails due to needing to rewind the stream (for example, resulting
75from a redirect). Data that is read from the remote stream will be buffered in
76a PHP temp stream so that previously read bytes are cached first in memory,
77then on disk.
78
79```php
80use GuzzleHttp\Psr7;
81
82$original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
83$stream = new Psr7\CachingStream($original);
84
85$stream->read(1024);
86echo $stream->tell();
87// 1024
88
89$stream->seek(0);
90echo $stream->tell();
91// 0
92```
93
94
95## DroppingStream
96
97`GuzzleHttp\Psr7\DroppingStream`
98
99Stream decorator that begins dropping data once the size of the underlying
100stream becomes too full.
101
102```php
103use GuzzleHttp\Psr7;
104
105// Create an empty stream
106$stream = Psr7\Utils::streamFor();
107
108// Start dropping data when the stream has more than 10 bytes
109$dropping = new Psr7\DroppingStream($stream, 10);
110
111$dropping->write('01234567890123456789');
112echo $stream; // 0123456789
113```
114
115
116## FnStream
117
118`GuzzleHttp\Psr7\FnStream`
119
120Compose stream implementations based on a hash of functions.
121
122Allows for easy testing and extension of a provided stream without needing
123to create a concrete class for a simple extension point.
124
125```php
126
127use GuzzleHttp\Psr7;
128
129$stream = Psr7\Utils::streamFor('hi');
130$fnStream = Psr7\FnStream::decorate($stream, [
131 'rewind' => function () use ($stream) {
132 echo 'About to rewind - ';
133 $stream->rewind();
134 echo 'rewound!';
135 }
136]);
137
138$fnStream->rewind();
139// Outputs: About to rewind - rewound!
140```
141
142
143## InflateStream
144
145`GuzzleHttp\Psr7\InflateStream`
146
147Uses PHP's zlib.inflate filter to inflate zlib (HTTP deflate, RFC1950) or gzipped (RFC1952) content.
148
149This stream decorator converts the provided stream to a PHP stream resource,
150then appends the zlib.inflate filter. The stream is then converted back
151to a Guzzle stream resource to be used as a Guzzle stream.
152
153
154## LazyOpenStream
155
156`GuzzleHttp\Psr7\LazyOpenStream`
157
158Lazily reads or writes to a file that is opened only after an IO operation
159take place on the stream.
160
161```php
162use GuzzleHttp\Psr7;
163
164$stream = new Psr7\LazyOpenStream('/path/to/file', 'r');
165// The file has not yet been opened...
166
167echo $stream->read(10);
168// The file is opened and read from only when needed.
169```
170
171
172## LimitStream
173
174`GuzzleHttp\Psr7\LimitStream`
175
176LimitStream can be used to read a subset or slice of an existing stream object.
177This can be useful for breaking a large file into smaller pieces to be sent in
178chunks (e.g. Amazon S3's multipart upload API).
179
180```php
181use GuzzleHttp\Psr7;
182
183$original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
184echo $original->getSize();
185// >>> 1048576
186
187// Limit the size of the body to 1024 bytes and start reading from byte 2048
188$stream = new Psr7\LimitStream($original, 1024, 2048);
189echo $stream->getSize();
190// >>> 1024
191echo $stream->tell();
192// >>> 0
193```
194
195
196## MultipartStream
197
198`GuzzleHttp\Psr7\MultipartStream`
199
200Stream that when read returns bytes for a streaming multipart or
201multipart/form-data stream.
202
203
204## NoSeekStream
205
206`GuzzleHttp\Psr7\NoSeekStream`
207
208NoSeekStream wraps a stream and does not allow seeking.
209
210```php
211use GuzzleHttp\Psr7;
212
213$original = Psr7\Utils::streamFor('foo');
214$noSeek = new Psr7\NoSeekStream($original);
215
216echo $noSeek->read(3);
217// foo
218var_export($noSeek->isSeekable());
219// false
220$noSeek->seek(0);
221var_export($noSeek->read(3));
222// NULL
223```
224
225
226## PumpStream
227
228`GuzzleHttp\Psr7\PumpStream`
229
230Provides a read only stream that pumps data from a PHP callable.
231
232When invoking the provided callable, the PumpStream will pass the amount of
233data requested to read to the callable. The callable can choose to ignore
234this value and return fewer or more bytes than requested. Any extra data
235returned by the provided callable is buffered internally until drained using
236the read() function of the PumpStream. The provided callable MUST return
237false when there is no more data to read.
238
239
240## Implementing stream decorators
241
242Creating a stream decorator is very easy thanks to the
243`GuzzleHttp\Psr7\StreamDecoratorTrait`. This trait provides methods that
244implement `Psr\Http\Message\StreamInterface` by proxying to an underlying
245stream. Just `use` the `StreamDecoratorTrait` and implement your custom
246methods.
247
248For example, let's say we wanted to call a specific function each time the last
249byte is read from a stream. This could be implemented by overriding the
250`read()` method.
251
252```php
253use Psr\Http\Message\StreamInterface;
254use GuzzleHttp\Psr7\StreamDecoratorTrait;
255
256class EofCallbackStream implements StreamInterface
257{
258 use StreamDecoratorTrait;
259
260 private $callback;
261
262 private $stream;
263
264 public function __construct(StreamInterface $stream, callable $cb)
265 {
266 $this->stream = $stream;
267 $this->callback = $cb;
268 }
269
270 public function read($length)
271 {
272 $result = $this->stream->read($length);
273
274 // Invoke the callback when EOF is hit.
275 if ($this->eof()) {
276 ($this->callback)();
277 }
278
279 return $result;
280 }
281}
282```
283
284This decorator could be added to any existing stream and used like so:
285
286```php
287use GuzzleHttp\Psr7;
288
289$original = Psr7\Utils::streamFor('foo');
290
291$eofStream = new EofCallbackStream($original, function () {
292 echo 'EOF!';
293});
294
295$eofStream->read(2);
296$eofStream->read(1);
297// echoes "EOF!"
298$eofStream->seek(0);
299$eofStream->read(3);
300// echoes "EOF!"
301```
302
303
304## PHP StreamWrapper
305
306You can use the `GuzzleHttp\Psr7\StreamWrapper` class if you need to use a
307PSR-7 stream as a PHP stream resource.
308
309Use the `GuzzleHttp\Psr7\StreamWrapper::getResource()` method to create a PHP
310stream from a PSR-7 stream.
311
312```php
313use GuzzleHttp\Psr7\StreamWrapper;
314
315$stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
316$resource = StreamWrapper::getResource($stream);
317echo fread($resource, 6); // outputs hello!
318```
319
320
321# Static API
322
323There are various static methods available under the `GuzzleHttp\Psr7` namespace.
324
325
326## `GuzzleHttp\Psr7\Message::toString`
327
328`public static function toString(MessageInterface $message): string`
329
330Returns the string representation of an HTTP message.
331
332```php
333$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
334echo GuzzleHttp\Psr7\Message::toString($request);
335```
336
337
338## `GuzzleHttp\Psr7\Message::bodySummary`
339
340`public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null`
341
342Get a short summary of the message body.
343
344Will return `null` if the response is not printable.
345
346
347## `GuzzleHttp\Psr7\Message::rewindBody`
348
349`public static function rewindBody(MessageInterface $message): void`
350
351Attempts to rewind a message body and throws an exception on failure.
352
353The body of the message will only be rewound if a call to `tell()`
354returns a value other than `0`.
355
356
357## `GuzzleHttp\Psr7\Message::parseMessage`
358
359`public static function parseMessage(string $message): array`
360
361Parses an HTTP message into an associative array.
362
363The array contains the "start-line" key containing the start line of
364the message, "headers" key containing an associative array of header
365array values, and a "body" key containing the body of the message.
366
367
368## `GuzzleHttp\Psr7\Message::parseRequestUri`
369
370`public static function parseRequestUri(string $path, array $headers): string`
371
372Constructs a URI for an HTTP request message.
373
374
375## `GuzzleHttp\Psr7\Message::parseRequest`
376
377`public static function parseRequest(string $message): Request`
378
379Parses a request message string into a request object.
380
381
382## `GuzzleHttp\Psr7\Message::parseResponse`
383
384`public static function parseResponse(string $message): Response`
385
386Parses a response message string into a response object.
387
388
389## `GuzzleHttp\Psr7\Header::parse`
390
391`public static function parse(string|array $header): array`
392
393Parse an array of header values containing ";" separated data into an
394array of associative arrays representing the header key value pair data
395of the header. When a parameter does not contain a value, but just
396contains a key, this function will inject a key with a '' string value.
397
398
399## `GuzzleHttp\Psr7\Header::splitList`
400
401`public static function splitList(string|string[] $header): string[]`
402
403Splits a HTTP header defined to contain a comma-separated list into
404each individual value:
405
406```
407$knownEtags = Header::splitList($request->getHeader('if-none-match'));
408```
409
410Example headers include `accept`, `cache-control` and `if-none-match`.
411
412
413## `GuzzleHttp\Psr7\Header::normalize` (deprecated)
414
415`public static function normalize(string|array $header): array`
416
417`Header::normalize()` is deprecated in favor of [`Header::splitList()`](README.md#guzzlehttppsr7headersplitlist)
418which performs the same operation with a cleaned up API and improved
419documentation.
420
421Converts an array of header values that may contain comma separated
422headers into an array of headers with no comma separated values.
423
424
425## `GuzzleHttp\Psr7\Query::parse`
426
427`public static function parse(string $str, int|bool $urlEncoding = true): array`
428
429Parse a query string into an associative array.
430
431If multiple values are found for the same key, the value of that key
432value pair will become an array. This function does not parse nested
433PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
434will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
435
436
437## `GuzzleHttp\Psr7\Query::build`
438
439`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string`
440
441Build a query string from an array of key value pairs.
442
443This function can use the return value of `parse()` to build a query
444string. This function does not modify the provided keys when an array is
445encountered (like `http_build_query()` would).
446
447
448## `GuzzleHttp\Psr7\Utils::caselessRemove`
449
450`public static function caselessRemove(iterable<string> $keys, $keys, array $data): array`
451
452Remove the items given by the keys, case insensitively from the data.
453
454
455## `GuzzleHttp\Psr7\Utils::copyToStream`
456
457`public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void`
458
459Copy the contents of a stream into another stream until the given number
460of bytes have been read.
461
462
463## `GuzzleHttp\Psr7\Utils::copyToString`
464
465`public static function copyToString(StreamInterface $stream, int $maxLen = -1): string`
466
467Copy the contents of a stream into a string until the given number of
468bytes have been read.
469
470
471## `GuzzleHttp\Psr7\Utils::hash`
472
473`public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string`
474
475Calculate a hash of a stream.
476
477This method reads the entire stream to calculate a rolling hash, based on
478PHP's `hash_init` functions.
479
480
481## `GuzzleHttp\Psr7\Utils::modifyRequest`
482
483`public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface`
484
485Clone and modify a request with the given changes.
486
487This method is useful for reducing the number of clones needed to mutate
488a message.
489
490- method: (string) Changes the HTTP method.
491- set_headers: (array) Sets the given headers.
492- remove_headers: (array) Remove the given headers.
493- body: (mixed) Sets the given body.
494- uri: (UriInterface) Set the URI.
495- query: (string) Set the query string value of the URI.
496- version: (string) Set the protocol version.
497
498
499## `GuzzleHttp\Psr7\Utils::readLine`
500
501`public static function readLine(StreamInterface $stream, int $maxLength = null): string`
502
503Read a line from the stream up to the maximum allowed buffer length.
504
505
506## `GuzzleHttp\Psr7\Utils::streamFor`
507
508`public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface`
509
510Create a new stream based on the input type.
511
512Options is an associative array that can contain the following keys:
513
514- metadata: Array of custom metadata.
515- size: Size of the stream.
516
517This method accepts the following `$resource` types:
518
519- `Psr\Http\Message\StreamInterface`: Returns the value as-is.
520- `string`: Creates a stream object that uses the given string as the contents.
521- `resource`: Creates a stream object that wraps the given PHP stream resource.
522- `Iterator`: If the provided value implements `Iterator`, then a read-only
523 stream object will be created that wraps the given iterable. Each time the
524 stream is read from, data from the iterator will fill a buffer and will be
525 continuously called until the buffer is equal to the requested read size.
526 Subsequent read calls will first read from the buffer and then call `next`
527 on the underlying iterator until it is exhausted.
528- `object` with `__toString()`: If the object has the `__toString()` method,
529 the object will be cast to a string and then a stream will be returned that
530 uses the string value.
531- `NULL`: When `null` is passed, an empty stream object is returned.
532- `callable` When a callable is passed, a read-only stream object will be
533 created that invokes the given callable. The callable is invoked with the
534 number of suggested bytes to read. The callable can return any number of
535 bytes, but MUST return `false` when there is no more data to return. The
536 stream object that wraps the callable will invoke the callable until the
537 number of requested bytes are available. Any additional bytes will be
538 buffered and used in subsequent reads.
539
540```php
541$stream = GuzzleHttp\Psr7\Utils::streamFor('foo');
542$stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r'));
543
544$generator = function ($bytes) {
545 for ($i = 0; $i < $bytes; $i++) {
546 yield ' ';
547 }
548}
549
550$stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));
551```
552
553
554## `GuzzleHttp\Psr7\Utils::tryFopen`
555
556`public static function tryFopen(string $filename, string $mode): resource`
557
558Safely opens a PHP stream resource using a filename.
559
560When fopen fails, PHP normally raises a warning. This function adds an
561error handler that checks for errors and throws an exception instead.
562
563
564## `GuzzleHttp\Psr7\Utils::tryGetContents`
565
566`public static function tryGetContents(resource $stream): string`
567
568Safely gets the contents of a given stream.
569
570When stream_get_contents fails, PHP normally raises a warning. This
571function adds an error handler that checks for errors and throws an
572exception instead.
573
574
575## `GuzzleHttp\Psr7\Utils::uriFor`
576
577`public static function uriFor(string|UriInterface $uri): UriInterface`
578
579Returns a UriInterface for the given value.
580
581This function accepts a string or UriInterface and returns a
582UriInterface for the given value. If the value is already a
583UriInterface, it is returned as-is.
584
585
586## `GuzzleHttp\Psr7\MimeType::fromFilename`
587
588`public static function fromFilename(string $filename): string|null`
589
590Determines the mimetype of a file by looking at its extension.
591
592
593## `GuzzleHttp\Psr7\MimeType::fromExtension`
594
595`public static function fromExtension(string $extension): string|null`
596
597Maps a file extensions to a mimetype.
598
599
600## Upgrading from Function API
601
602The static API was first introduced in 1.7.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API was removed in 2.0.0. A migration table has been provided here for your convenience:
603
604| Original Function | Replacement Method |
605|----------------|----------------|
606| `str` | `Message::toString` |
607| `uri_for` | `Utils::uriFor` |
608| `stream_for` | `Utils::streamFor` |
609| `parse_header` | `Header::parse` |
610| `normalize_header` | `Header::normalize` |
611| `modify_request` | `Utils::modifyRequest` |
612| `rewind_body` | `Message::rewindBody` |
613| `try_fopen` | `Utils::tryFopen` |
614| `copy_to_string` | `Utils::copyToString` |
615| `copy_to_stream` | `Utils::copyToStream` |
616| `hash` | `Utils::hash` |
617| `readline` | `Utils::readLine` |
618| `parse_request` | `Message::parseRequest` |
619| `parse_response` | `Message::parseResponse` |
620| `parse_query` | `Query::parse` |
621| `build_query` | `Query::build` |
622| `mimetype_from_filename` | `MimeType::fromFilename` |
623| `mimetype_from_extension` | `MimeType::fromExtension` |
624| `_parse_message` | `Message::parseMessage` |
625| `_parse_request_uri` | `Message::parseRequestUri` |
626| `get_message_body_summary` | `Message::bodySummary` |
627| `_caseless_remove` | `Utils::caselessRemove` |
628
629
630# Additional URI Methods
631
632Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,
633this library also provides additional functionality when working with URIs as static methods.
634
635## URI Types
636
637An instance of `Psr\Http\Message\UriInterface` can either be an absolute URI or a relative reference.
638An absolute URI has a scheme. A relative reference is used to express a URI relative to another URI,
639the base URI. Relative references can be divided into several forms according to
640[RFC 3986 Section 4.2](https://datatracker.ietf.org/doc/html/rfc3986#section-4.2):
641
642- network-path references, e.g. `//example.com/path`
643- absolute-path references, e.g. `/path`
644- relative-path references, e.g. `subpath`
645
646The following methods can be used to identify the type of the URI.
647
648### `GuzzleHttp\Psr7\Uri::isAbsolute`
649
650`public static function isAbsolute(UriInterface $uri): bool`
651
652Whether the URI is absolute, i.e. it has a scheme.
653
654### `GuzzleHttp\Psr7\Uri::isNetworkPathReference`
655
656`public static function isNetworkPathReference(UriInterface $uri): bool`
657
658Whether the URI is a network-path reference. A relative reference that begins with two slash characters is
659termed an network-path reference.
660
661### `GuzzleHttp\Psr7\Uri::isAbsolutePathReference`
662
663`public static function isAbsolutePathReference(UriInterface $uri): bool`
664
665Whether the URI is a absolute-path reference. A relative reference that begins with a single slash character is
666termed an absolute-path reference.
667
668### `GuzzleHttp\Psr7\Uri::isRelativePathReference`
669
670`public static function isRelativePathReference(UriInterface $uri): bool`
671
672Whether the URI is a relative-path reference. A relative reference that does not begin with a slash character is
673termed a relative-path reference.
674
675### `GuzzleHttp\Psr7\Uri::isSameDocumentReference`
676
677`public static function isSameDocumentReference(UriInterface $uri, UriInterface $base = null): bool`
678
679Whether the URI is a same-document reference. A same-document reference refers to a URI that is, aside from its
680fragment component, identical to the base URI. When no base URI is given, only an empty URI reference
681(apart from its fragment) is considered a same-document reference.
682
683## URI Components
684
685Additional methods to work with URI components.
686
687### `GuzzleHttp\Psr7\Uri::isDefaultPort`
688
689`public static function isDefaultPort(UriInterface $uri): bool`
690
691Whether the URI has the default port of the current scheme. `Psr\Http\Message\UriInterface::getPort` may return null
692or the standard port. This method can be used independently of the implementation.
693
694### `GuzzleHttp\Psr7\Uri::composeComponents`
695
696`public static function composeComponents($scheme, $authority, $path, $query, $fragment): string`
697
698Composes a URI reference string from its various components according to
699[RFC 3986 Section 5.3](https://datatracker.ietf.org/doc/html/rfc3986#section-5.3). Usually this method does not need
700to be called manually but instead is used indirectly via `Psr\Http\Message\UriInterface::__toString`.
701
702### `GuzzleHttp\Psr7\Uri::fromParts`
703
704`public static function fromParts(array $parts): UriInterface`
705
706Creates a URI from a hash of [`parse_url`](https://www.php.net/manual/en/function.parse-url.php) components.
707
708
709### `GuzzleHttp\Psr7\Uri::withQueryValue`
710
711`public static function withQueryValue(UriInterface $uri, $key, $value): UriInterface`
712
713Creates a new URI with a specific query string value. Any existing query string values that exactly match the
714provided key are removed and replaced with the given key value pair. A value of null will set the query string
715key without a value, e.g. "key" instead of "key=value".
716
717### `GuzzleHttp\Psr7\Uri::withQueryValues`
718
719`public static function withQueryValues(UriInterface $uri, array $keyValueArray): UriInterface`
720
721Creates a new URI with multiple query string values. It has the same behavior as `withQueryValue()` but for an
722associative array of key => value.
723
724### `GuzzleHttp\Psr7\Uri::withoutQueryValue`
725
726`public static function withoutQueryValue(UriInterface $uri, $key): UriInterface`
727
728Creates a new URI with a specific query string value removed. Any existing query string values that exactly match the
729provided key are removed.
730
731## Cross-Origin Detection
732
733`GuzzleHttp\Psr7\UriComparator` provides methods to determine if a modified URL should be considered cross-origin.
734
735### `GuzzleHttp\Psr7\UriComparator::isCrossOrigin`
736
737`public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool`
738
739Determines if a modified URL should be considered cross-origin with respect to an original URL.
740
741## Reference Resolution
742
743`GuzzleHttp\Psr7\UriResolver` provides methods to resolve a URI reference in the context of a base URI according
744to [RFC 3986 Section 5](https://datatracker.ietf.org/doc/html/rfc3986#section-5). This is for example also what web
745browsers do when resolving a link in a website based on the current request URI.
746
747### `GuzzleHttp\Psr7\UriResolver::resolve`
748
749`public static function resolve(UriInterface $base, UriInterface $rel): UriInterface`
750
751Converts the relative URI into a new URI that is resolved against the base URI.
752
753### `GuzzleHttp\Psr7\UriResolver::removeDotSegments`
754
755`public static function removeDotSegments(string $path): string`
756
757Removes dot segments from a path and returns the new path according to
758[RFC 3986 Section 5.2.4](https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4).
759
760### `GuzzleHttp\Psr7\UriResolver::relativize`
761
762`public static function relativize(UriInterface $base, UriInterface $target): UriInterface`
763
764Returns the target URI as a relative reference from the base URI. This method is the counterpart to resolve():
765
766```php
767(string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
768```
769
770One use-case is to use the current request URI as base URI and then generate relative links in your documents
771to reduce the document size or offer self-contained downloadable document archives.
772
773```php
774$base = new Uri('http://example.com/a/b/');
775echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
776echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
777echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
778echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
779```
780
781## Normalization and Comparison
782
783`GuzzleHttp\Psr7\UriNormalizer` provides methods to normalize and compare URIs according to
784[RFC 3986 Section 6](https://datatracker.ietf.org/doc/html/rfc3986#section-6).
785
786### `GuzzleHttp\Psr7\UriNormalizer::normalize`
787
788`public static function normalize(UriInterface $uri, $flags = self::PRESERVING_NORMALIZATIONS): UriInterface`
789
790Returns a normalized URI. The scheme and host component are already normalized to lowercase per PSR-7 UriInterface.
791This methods adds additional normalizations that can be configured with the `$flags` parameter which is a bitmask
792of normalizations to apply. The following normalizations are available:
793
794- `UriNormalizer::PRESERVING_NORMALIZATIONS`
795
796 Default normalizations which only include the ones that preserve semantics.
797
798- `UriNormalizer::CAPITALIZE_PERCENT_ENCODING`
799
800 All letters within a percent-encoding triplet (e.g., "%3A") are case-insensitive, and should be capitalized.
801
802 Example: `http://example.org/a%c2%b1b` → `http://example.org/a%C2%B1b`
803
804- `UriNormalizer::DECODE_UNRESERVED_CHARACTERS`
805
806 Decodes percent-encoded octets of unreserved characters. For consistency, percent-encoded octets in the ranges of
807 ALPHA (%41–%5A and %61–%7A), DIGIT (%30–%39), hyphen (%2D), period (%2E), underscore (%5F), or tilde (%7E) should
808 not be created by URI producers and, when found in a URI, should be decoded to their corresponding unreserved
809 characters by URI normalizers.
810
811 Example: `http://example.org/%7Eusern%61me/` → `http://example.org/~username/`
812
813- `UriNormalizer::CONVERT_EMPTY_PATH`
814
815 Converts the empty path to "/" for http and https URIs.
816
817 Example: `http://example.org` → `http://example.org/`
818
819- `UriNormalizer::REMOVE_DEFAULT_HOST`
820
821 Removes the default host of the given URI scheme from the URI. Only the "file" scheme defines the default host
822 "localhost". All of `file:/myfile`, `file:///myfile`, and `file://localhost/myfile` are equivalent according to
823 RFC 3986.
824
825 Example: `file://localhost/myfile` → `file:///myfile`
826
827- `UriNormalizer::REMOVE_DEFAULT_PORT`
828
829 Removes the default port of the given URI scheme from the URI.
830
831 Example: `http://example.org:80/` → `http://example.org/`
832
833- `UriNormalizer::REMOVE_DOT_SEGMENTS`
834
835 Removes unnecessary dot-segments. Dot-segments in relative-path references are not removed as it would
836 change the semantics of the URI reference.
837
838 Example: `http://example.org/../a/b/../c/./d.html` → `http://example.org/a/c/d.html`
839
840- `UriNormalizer::REMOVE_DUPLICATE_SLASHES`
841
842 Paths which include two or more adjacent slashes are converted to one. Webservers usually ignore duplicate slashes
843 and treat those URIs equivalent. But in theory those URIs do not need to be equivalent. So this normalization
844 may change the semantics. Encoded slashes (%2F) are not removed.
845
846 Example: `http://example.org//foo///bar.html` → `http://example.org/foo/bar.html`
847
848- `UriNormalizer::SORT_QUERY_PARAMETERS`
849
850 Sort query parameters with their values in alphabetical order. However, the order of parameters in a URI may be
851 significant (this is not defined by the standard). So this normalization is not safe and may change the semantics
852 of the URI.
853
854 Example: `?lang=en&article=fred` → `?article=fred&lang=en`
855
856### `GuzzleHttp\Psr7\UriNormalizer::isEquivalent`
857
858`public static function isEquivalent(UriInterface $uri1, UriInterface $uri2, $normalizations = self::PRESERVING_NORMALIZATIONS): bool`
859
860Whether two URIs can be considered equivalent. Both URIs are normalized automatically before comparison with the given
861`$normalizations` bitmask. The method also accepts relative URI references and returns true when they are equivalent.
862This of course assumes they will be resolved against the same base URI. If this is not the case, determination of
863equivalence or difference of relative references does not mean anything.
864
865
866## Security
867
868If you discover a security vulnerability within this package, please send an email to security@tidelift.com. All security vulnerabilities will be promptly addressed. Please do not disclose security-related issues publicly until a fix has been announced. Please see [Security Policy](https://github.com/guzzle/psr7/security/policy) for more information.
869
870
871## License
872
873Guzzle is made available under the MIT License (MIT). Please see [License File](LICENSE) for more information.
874
875
876## For Enterprise
877
878Available as part of the Tidelift Subscription
879
880The maintainers of Guzzle and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/packagist-guzzlehttp-psr7?utm_source=packagist-guzzlehttp-psr7&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
881