mirror of
https://github.com/itflow-org/itflow
synced 2026-09-22 22:51:17 +00:00
Update imapengine dependancies too
This commit is contained in:
886
libs/vendor/guzzlehttp/psr7/UPGRADING.md
vendored
886
libs/vendor/guzzlehttp/psr7/UPGRADING.md
vendored
@@ -1,6 +1,892 @@
|
||||
Guzzle PSR-7 Upgrade Guide
|
||||
==========================
|
||||
|
||||
2.x to 3.0
|
||||
----------
|
||||
|
||||
Guzzle PSR-7 3.0 is a major release that raises the minimum PHP version,
|
||||
updates to the PSR-7 v2 interfaces, validates header values more strictly,
|
||||
preserves explicit request method casing, and rejects several invalid URI,
|
||||
request, response, upload, query, stream, and multipart values that 2.x
|
||||
previously accepted or cast.
|
||||
|
||||
#### PHP Version and Dependencies
|
||||
|
||||
Guzzle PSR-7 3.0 requires PHP `^7.4 || ^8.0`. Guzzle PSR-7 2.x supported PHP
|
||||
`^7.2.5 || ^8.0`.
|
||||
|
||||
If your application still supports PHP 7.2 or 7.3, continue using Guzzle PSR-7
|
||||
2.x until your minimum PHP version is raised.
|
||||
|
||||
Guzzle PSR-7 3.0 requires `psr/http-message:^2.0` and
|
||||
`psr/http-factory:^1.1`. Guzzle PSR-7 2.x supported
|
||||
`psr/http-message:^1.1 || ^2.0` and `psr/http-factory:^1.0`. If your dependency
|
||||
constraints pin `psr/http-message` to v1, update them before upgrading.
|
||||
|
||||
Guzzle PSR-7 no longer depends on `ralouphie/getallheaders` and no longer
|
||||
provides a transitive global `getallheaders()` polyfill.
|
||||
`ServerRequest::fromGlobals()` continues to collect request headers internally.
|
||||
Applications that call `getallheaders()` directly on SAPIs where PHP does not
|
||||
provide it should require their own polyfill.
|
||||
|
||||
#### Header Values
|
||||
|
||||
Header values must now be strings or non-empty arrays of strings. Empty strings
|
||||
remain valid explicit header values, but empty arrays, `null`, `false`, integers,
|
||||
floats, and other non-string values are no longer cast or accepted.
|
||||
|
||||
```php
|
||||
// 2.x, no longer accepted in 3.0
|
||||
$response = $response->withHeader('Api-Version', 1);
|
||||
$response = $response->withHeader('Empty-List', []);
|
||||
|
||||
// 3.0
|
||||
$response = $response->withHeader('Api-Version', '1');
|
||||
$response = $response->withHeader('Empty-Value', '');
|
||||
```
|
||||
|
||||
Use `withoutHeader()` to remove a header.
|
||||
|
||||
#### Request Method Casing
|
||||
|
||||
Request methods passed explicitly to `Request`, `ServerRequest`, `withMethod()`,
|
||||
`Message::parseRequest()`, and the PSR-17 factories are no longer uppercased.
|
||||
PSR-7 treats method names as case-sensitive, so these APIs now preserve the
|
||||
method exactly as provided. If your application requires uppercase methods,
|
||||
normalize methods before constructing or modifying requests.
|
||||
|
||||
`ServerRequest::fromGlobals()` is the compatibility-oriented exception. It
|
||||
continues to uppercase string `REQUEST_METHOD` values read from PHP server
|
||||
globals, matching Guzzle PSR-7 2.x and common server request behavior. This
|
||||
normalization only applies when hydrating from globals; it does not apply to
|
||||
methods passed explicitly to constructors, factories, or `withMethod()`.
|
||||
|
||||
```php
|
||||
// 2.x
|
||||
$request = new Request('get', '/');
|
||||
$request->getMethod(); // GET
|
||||
|
||||
// 3.0
|
||||
$request = new Request('get', '/');
|
||||
$request->getMethod(); // get
|
||||
|
||||
// 3.0, server globals
|
||||
$_SERVER['REQUEST_METHOD'] = 'post';
|
||||
$request = ServerRequest::fromGlobals();
|
||||
$request->getMethod(); // POST
|
||||
```
|
||||
|
||||
#### Native PSR-7 Parameter Types
|
||||
|
||||
Guzzle PSR-7 3.0 requires the argument types documented by PSR-7 more strictly.
|
||||
It adds the native parameter types from `psr/http-message` v2. Code passing
|
||||
invalid argument types may now receive PHP `TypeError` exceptions instead of
|
||||
package-specific `InvalidArgumentException` exceptions or implicit casts.
|
||||
|
||||
Native parameter type changes include:
|
||||
|
||||
- `MessageInterface::withProtocolVersion()` now requires `string`.
|
||||
- Message header names now require `string`.
|
||||
- `RequestInterface::withRequestTarget()` and `withMethod()` now require `string`.
|
||||
- `RequestInterface::withUri()` now requires `bool` for `$preserveHost`.
|
||||
- `ResponseInterface::withStatus()` now requires `int` status codes and `string` reason phrases.
|
||||
- Server request attribute names now require `string`.
|
||||
- `UriInterface::withPort()` now requires `int|null`.
|
||||
- URI scheme, user info, host, path, query, and fragment mutators now require strings.
|
||||
- Stream `seek()`, `read()`, `write()`, and `getMetadata()` now require their PSR-7 v2 parameter types.
|
||||
- `UploadedFileInterface::moveTo()` now requires a string target path.
|
||||
|
||||
Update callers to pass values of the documented type before calling these
|
||||
methods:
|
||||
|
||||
```php
|
||||
// 2.x, no longer supported in 3.0
|
||||
$response = $response->withStatus('201');
|
||||
$uri = $uri->withPort('8080');
|
||||
|
||||
// 3.0
|
||||
$response = $response->withStatus(201);
|
||||
$uri = $uri->withPort(8080);
|
||||
```
|
||||
|
||||
#### Request Modification Changes
|
||||
|
||||
`Utils::modifyRequest()` now validates recognized change values before applying
|
||||
request modifications. Unknown change keys are still ignored. Explicit `null`
|
||||
values are no longer treated as omitted recognized changes; omit the key instead.
|
||||
|
||||
Recognized change values must use the documented types:
|
||||
|
||||
- `method`: `string`
|
||||
- `uri`: `UriInterface`
|
||||
- `query`: `string`
|
||||
- `version`: `string`
|
||||
- `body`: `resource|string|StreamInterface|callable|\Iterator|\Stringable`
|
||||
- `set_headers`: `array<array-key, string|non-empty-array<array-key, string>>`
|
||||
- `remove_headers`: `array<array-key, string|int>`
|
||||
|
||||
When a `uri` change contains a host, the synthesized `Host` header now
|
||||
includes any non-default URI port, matching the `Request` constructor. 2.x
|
||||
omitted port zero and every port on schemes other than HTTP and HTTPS.
|
||||
|
||||
#### Uploaded Files
|
||||
|
||||
`ServerRequestInterface::withUploadedFiles()` now rejects invalid nested upload
|
||||
trees. Every leaf must be an `UploadedFileInterface` instance.
|
||||
|
||||
`ServerRequest::normalizeFiles()` and `ServerRequest::fromGlobals()` now reject
|
||||
malformed `$_FILES` specifications earlier. Single-file specifications must
|
||||
contain non-null `tmp_name`, `size`, and `error` values. Single-file and nested
|
||||
file `size` values and `error` values must be non-negative PHP integers;
|
||||
numeric strings are no longer cast. If PHP supplies an upload size as a string
|
||||
because the byte count cannot fit in `PHP_INT_MAX`, it is rejected rather than
|
||||
truncated or cast. Nested specifications must provide `tmp_name`, `size`, and
|
||||
`error` as arrays. Every key in `tmp_name` must also exist in `size` and
|
||||
`error`; additional metadata entries without a matching `tmp_name` entry are
|
||||
ignored. When nested `name` or `type` metadata is provided, it must also be an
|
||||
array.
|
||||
|
||||
If your tests or adapters build `$_FILES` arrays manually, populate the full
|
||||
shape or create `UploadedFile` instances directly.
|
||||
|
||||
```php
|
||||
// 2.x, no longer accepted in 3.0
|
||||
$files = ['file' => ['tmp_name' => '/tmp/php123', 'error' => '0']];
|
||||
|
||||
// 3.0
|
||||
$files = ['file' => ['tmp_name' => '/tmp/php123', 'size' => 123, 'error' => UPLOAD_ERR_OK]];
|
||||
```
|
||||
|
||||
For stream-backed uploads, `UploadedFile::moveTo()` now rewinds seekable streams
|
||||
before copying them. If application code reads from a seekable uploaded stream
|
||||
before calling `moveTo()`, 3.0 writes the full stream contents to the target
|
||||
instead of only the unread suffix. Non-seekable stream-backed uploads continue to
|
||||
copy from their current position because consumed bytes cannot be replayed.
|
||||
|
||||
#### Parsed Body Values
|
||||
|
||||
`ServerRequestInterface::withParsedBody()` now rejects values other than
|
||||
`array`, `object`, or `null`.
|
||||
|
||||
```php
|
||||
// 2.x, no longer accepted in 3.0
|
||||
$request = $request->withParsedBody('name=value');
|
||||
|
||||
// 3.0
|
||||
$request = $request->withParsedBody(['name' => 'value']);
|
||||
```
|
||||
|
||||
#### URI Host and Scheme Validation
|
||||
|
||||
URI hosts containing URI delimiters, backslashes, embedded ports passed to
|
||||
`withHost()`, malformed IP-literal brackets, unbracketed IPv6, or other
|
||||
malformed host forms are no longer accepted. URI schemes containing whitespace
|
||||
or control characters are also no longer accepted.
|
||||
|
||||
If you previously passed a host and port together to `withHost()`, split them
|
||||
between `withHost()` and `withPort()`:
|
||||
|
||||
```php
|
||||
// 2.x, no longer accepted in 3.0
|
||||
$uri = $uri->withHost('example.com:8080');
|
||||
|
||||
// 3.0
|
||||
$uri = $uri->withHost('example.com')->withPort(8080);
|
||||
```
|
||||
|
||||
Normal URI strings with ports are still supported:
|
||||
|
||||
```php
|
||||
$uri = new Uri('https://example.com:8080/path');
|
||||
```
|
||||
|
||||
URI parsing now accepts bracketed IPv6 and IPvFuture hosts consistently with
|
||||
`withHost()` for userinfo and network-path authorities such as
|
||||
`http://user@[::1]/`, `//[::1]`, and `http://[v7.a:b]/`. Invalid
|
||||
delimiter-free bracketed literals, such as `[gggg::1]`, are also reported with
|
||||
the intact host. Bracketed literals containing authority/path delimiters, such
|
||||
as `[a@b]` or `[v1.a/b]`, still reject after fallback parsing and may report the
|
||||
mangled parsed host.
|
||||
|
||||
Userinfo before a bracketed IP-literal host is now percent-encoded ahead of
|
||||
parsing, so raw control bytes yield encoded userinfo, such as `us%01er`,
|
||||
instead of a silently mutated value, and raw DEL bytes in bracketed hosts are
|
||||
rejected instead of parsed as a mutated host. Consistent with registered-name
|
||||
authorities, such userinfo containing invalid UTF-8 is now rejected,
|
||||
percent-sequences such as `u%41` are preserved rather than decoded, and a
|
||||
literal `+` is preserved rather than decoded to a space.
|
||||
|
||||
Only an optional numeric port (which may be empty) and a path, query, or
|
||||
fragment may follow a bracketed IP-literal host. Trailing bytes that are
|
||||
neither, such as `http://[::1]:80@evil/` or `http://[::1]:80x/`, are now
|
||||
rejected instead of being reparsed into a different host.
|
||||
|
||||
Parsing still URL-decodes bracketed IP-literal hosts before validation
|
||||
(registered-name hosts round-trip unchanged), so a literal `+` in a bracketed
|
||||
IP-literal decodes to a space and is rejected: `withHost('[v1.fe80::a+en1]')`
|
||||
accepts the literal while parsing `http://[v1.fe80::a+en1]/` rejects it.
|
||||
Percent-encoding inside a bracketed IP-literal is now rejected during parsing as
|
||||
well, since RFC 3986 IP-literals contain no percent-encoding, so
|
||||
`http://[%3A%3A1]/` no longer decodes to `[::1]`; this matches `withHost()` and
|
||||
`Rfc3986::isValidHost()`.
|
||||
|
||||
Percent-encoded octets in a registered-name host are normalized to uppercase
|
||||
hex, so a host such as `a%c3%a9b` is represented as `a%C3%A9b`. Malformed
|
||||
percent sequences and percent-encoded octets that decode to a byte forbidden in
|
||||
a host, such as `ex%zz` and `%2fhost`, are rejected.
|
||||
|
||||
`Uri::fromParts()` accepts integer and decimal digit string ports, but floats and
|
||||
other port values are no longer cast.
|
||||
|
||||
Common host forms such as `localhost`, single-label hosts, underscores, Unicode
|
||||
hosts, valid IPv6 literals, and normal host and port URI strings remain
|
||||
supported.
|
||||
|
||||
URI schemes must now match RFC 3986 syntax and begin with a letter.
|
||||
|
||||
```php
|
||||
// 2.x, no longer accepted in 3.0
|
||||
$uri = (new Uri())->withScheme('0');
|
||||
|
||||
// 3.0
|
||||
$uri = (new Uri())->withScheme('https');
|
||||
```
|
||||
|
||||
The stricter validation also applies when a request is created or modified from
|
||||
a custom `UriInterface` implementation and its host is used to generate or
|
||||
update a `Host` header.
|
||||
|
||||
`ServerRequest::getUriFromGlobals()` now falls back to `SERVER_NAME`, then
|
||||
`SERVER_ADDR`, then the existing default host behavior for malformed
|
||||
`HTTP_HOST` values. It also rejects zero-port `HTTP_HOST` authorities and
|
||||
malformed `SERVER_PORT` values when fallback authority reconstruction needs the
|
||||
server port. When `REQUEST_URI` is absolute-form or CONNECT authority-form and
|
||||
supplies a valid authority, that authority is used before fallback `SERVER_PORT`
|
||||
validation. Origin-form, asterisk-form, missing `REQUEST_URI`, and fallback
|
||||
reconstruction paths still reject malformed `SERVER_PORT` values when fallback
|
||||
authority reconstruction needs the server port.
|
||||
|
||||
Absolute-form `REQUEST_URI` userinfo is removed when reconstructing the URI and
|
||||
request target from globals, including empty userinfo such as
|
||||
`http://@example.com/`. The host after the last raw `@` remains the URI host.
|
||||
|
||||
`Message::parseRequest()` now applies 3.0 authority rules when deriving a URI
|
||||
from an origin-form or asterisk-form request target. Host ports with leading
|
||||
zeroes are normalized for URI reconstruction, and port zero is rejected.
|
||||
It also rejects duplicate `Host` field lines, including case-insensitive
|
||||
duplicates. Any present raw `Host` field is validated before returning a parsed
|
||||
request, even when the request target supplies the URI authority, such as
|
||||
absolute-form and CONNECT requests. Valid `Host` values may still differ from
|
||||
the absolute-form or CONNECT request-target authority.
|
||||
|
||||
For server globals, applications that need to reject malformed inbound `Host`
|
||||
headers should validate the original server parameters before calling
|
||||
`getUriFromGlobals()` or inspect them afterward.
|
||||
|
||||
`Message::parseRequest()` now applies the same HTTP authority validation to
|
||||
absolute-form request targets. A zero or padded-zero port
|
||||
(`http://example.com:0/admin`) is rejected instead of producing a request
|
||||
whose synthesized `Host` header the same parser rejects elsewhere.
|
||||
Absolute-form targets with no URI host, such as `file:///etc/passwd`, are
|
||||
also rejected instead of producing a hostless request URI.
|
||||
|
||||
Absolute-form targets whose authority contains userinfo are also rejected,
|
||||
including empty userinfo such as `http://@example.com/`. RFC 9110 deprecates
|
||||
userinfo in http(s) target URIs and directs recipients to treat its presence
|
||||
as an error; `Host` headers and CONNECT targets already reject it. These
|
||||
rules apply to absolute-form targets of every scheme.
|
||||
`ServerRequest::fromGlobals()` is unchanged and continues to strip
|
||||
`REQUEST_URI` userinfo.
|
||||
|
||||
URI hosts now validate percent-encoding. Malformed sequences such as `ex%zz`,
|
||||
and percent-encoded octets that decode to bytes the raw host grammar already
|
||||
rejects (controls, space, DEL, `/`, `?`, `#`, `@`, `\`, `:`, `[`, `]`, and `%`
|
||||
itself) throw `MalformedUriException` from URI parsing and
|
||||
`InvalidArgumentException` from `Uri::withHost()`, and are rejected wherever
|
||||
hosts are validated, including `Host` headers and request targets in
|
||||
`Message::parseRequest()`. WHATWG-conformant browsers reject all of these hosts;
|
||||
curl rejects them too, except encoded DEL (`%7F`), which it decodes and forwards
|
||||
to name resolution. Other percent-encoded octets, including UTF-8 data such as
|
||||
`a%C3%A9b`, remain accepted and are normalized to uppercase hex.
|
||||
|
||||
IPv6 hosts are now canonicalized to their RFC 5952 form when a URI is
|
||||
constructed, so `getHost()`, `getAuthority()`, and `(string) $uri` return the
|
||||
canonical spelling and synthesized `Host` headers use it. Leading zeros are
|
||||
suppressed, hexadecimal fields are lowercase, and the longest run of two or
|
||||
more zero fields is collapsed with `::`. Embedded dotted-decimal notation
|
||||
follows the rendering policy of BIND-derived `inet_ntop()` implementations and
|
||||
curl 8.11 and newer: exactly the IPv4-mapped (`::ffff:0:0/96`) and deprecated
|
||||
IPv4-compatible (`::/96`) layouts use it, while other embedded-IPv4 forms,
|
||||
including translated (NAT64) well-known prefixes such as `64:ff9b::/96`
|
||||
(RFC 6052), serialize in pure hexadecimal fields.
|
||||
|
||||
```php
|
||||
// 2.x preserved the spelling as given
|
||||
(string) new Uri('http://[0:0:0:0:0:0:0:1]/'); // http://[0:0:0:0:0:0:0:1]/
|
||||
|
||||
// 3.0
|
||||
(string) new Uri('http://[0:0:0:0:0:0:0:1]/'); // http://[::1]/
|
||||
(string) new Uri('http://[::FFFF:7F00:1]/'); // http://[::ffff:127.0.0.1]/
|
||||
(string) new Uri('http://[2001:db8:3:4::192.0.2.33]/'); // http://[2001:db8:3:4::c000:221]/
|
||||
```
|
||||
|
||||
Applications that persist URI strings, for example as cache keys, will observe
|
||||
the new canonical form for previously non-canonical IPv6 spellings. Equivalent
|
||||
spellings of the same address now compare as same-origin in
|
||||
`UriComparator::isCrossOrigin()`, which canonicalizes bracketed IPv6 literals
|
||||
from any PSR-7 implementation before comparing hosts, and as equivalent in
|
||||
`UriNormalizer::isEquivalent()`. The new
|
||||
`UriNormalizer::CANONICALIZE_IPV6_HOST` flag, included in the default
|
||||
`UriNormalizer::PRESERVING_NORMALIZATIONS`, requests the canonical host from
|
||||
other PSR-7 implementations through `withHost()` and keeps the result only
|
||||
when the returned `getHost()` exactly matches the requested spelling; a
|
||||
nonexact result leaves that step unchanged while other selected normalizations
|
||||
still apply, and setter exceptions propagate. `UriComparator` does not share
|
||||
this limitation, since it canonicalizes the extracted host text directly. The
|
||||
public helper `Rfc3986::canonicalizeIpv6()` exposes the underlying
|
||||
transformation.
|
||||
|
||||
#### Request Host Synchronization
|
||||
|
||||
`Request::withUri()` now applies PSR-7 Host header synchronization before using
|
||||
the same-URI no-op shortcut. When the provided URI is the same object already
|
||||
attached to the request, the method may still return a new request if the URI
|
||||
has a host and the current Host header is missing, empty, or stale.
|
||||
|
||||
With `$preserveHost = true`, a non-empty Host header is still preserved. Missing
|
||||
or empty Host headers are treated as absent and are populated from the URI when
|
||||
the URI contains a host.
|
||||
|
||||
```php
|
||||
$request = (new Request('GET', 'http://example.com:8124/'))->withoutHeader('Host');
|
||||
|
||||
$updated = $request->withUri($request->getUri());
|
||||
|
||||
$updated->getHeaderLine('Host'); // example.com:8124
|
||||
```
|
||||
|
||||
If your application intentionally sends an empty or stale Host header, set it
|
||||
after calling `withUri()` or preserve a non-empty Host header explicitly.
|
||||
|
||||
`Message::toString()` now applies the same URI host and port synthesis when
|
||||
serializing a request without a `Host` header. Generated `Host` lines include
|
||||
non-null URI ports.
|
||||
|
||||
`Message::toString()` also validates the host it synthesizes from the request
|
||||
URI and throws `InvalidArgumentException` for an invalid host, closing a header-
|
||||
injection vector. This affects only a custom `UriInterface` implementation that
|
||||
returns an invalid host when the request has no stored `Host` header; first-
|
||||
party `Uri` instances always carry a valid host and are unaffected.
|
||||
|
||||
#### URI Paths and Request Targets
|
||||
|
||||
`Uri::getPath()` now normalizes multiple leading slashes to one slash when
|
||||
returning the path in isolation. Casting the URI to string still preserves the
|
||||
original URI representation.
|
||||
|
||||
```php
|
||||
$uri = new Uri('http://example.org//valid///path');
|
||||
|
||||
$uri->getPath(); // /valid///path
|
||||
(string) $uri; // http://example.org//valid///path
|
||||
```
|
||||
|
||||
`Request::getRequestTarget()` applies the same normalization for URI-derived
|
||||
origin-form request targets.
|
||||
|
||||
Reference resolution and normalization (`UriResolver`, `UriNormalizer`, and
|
||||
`Uri::isSameDocumentReference()`) operate on the raw path from the URI string
|
||||
form and are therefore unaffected by this normalization.
|
||||
|
||||
Authority-less `file` URIs with rootless paths now serialize without the `//`
|
||||
authority separator: `(string) new Uri('file:foo/bar')` returns `file:foo/bar`
|
||||
instead of `file://foo/bar`, which reparses with host `foo` and path `/bar`.
|
||||
Rooted paths such as `file:///myfile` keep their existing serialization.
|
||||
|
||||
Authority-less `file` URIs with empty paths now serialize as `file:` instead of
|
||||
`file://`, which `new Uri()` itself rejects as unparseable. This affects
|
||||
degenerate URIs such as `new Uri('file:')` or
|
||||
`Uri::fromParts(['scheme' => 'file'])`; the serialization of every file URI
|
||||
with a non-empty path is unchanged.
|
||||
|
||||
`UriResolver::removeDotSegments()` now applies RFC 3986 Section 5.2.4 to `..`
|
||||
segments above the root of an absolute path: excess `..` segments no longer
|
||||
consume the root, so a following empty segment is preserved. Resolving `/..//a`
|
||||
against `http://example.org/base` yields `http://example.org//a` where 2.x
|
||||
produced `http://example.org/a`. When the resulting URI has no authority,
|
||||
`UriResolver::resolve()` and `UriNormalizer::normalize()` serialize such a
|
||||
`//`-leading path with a `/.` prefix (`mailto:/.//a`), like the WHATWG URL
|
||||
Standard, instead of collapsing the slashes or throwing.
|
||||
|
||||
#### URI Reference Relativization
|
||||
|
||||
`UriResolver::relativize()` now returns a network-path reference (for example
|
||||
`//example.com`) when the target URI has the same authority as the base URI
|
||||
but an empty path that no other relative reference round-trips. No path
|
||||
reference can express such a target, as resolving one always produces a path
|
||||
of at least `/`, and an empty reference would keep the base path or inherit
|
||||
the base query or fragment. The returned reference resolves back to the
|
||||
exact target string, restoring the documented round-trip guarantee for these
|
||||
targets.
|
||||
|
||||
```php
|
||||
$base = new Uri('http://example.com/a');
|
||||
$target = new Uri('http://example.com');
|
||||
|
||||
// 2.x
|
||||
(string) UriResolver::relativize($base, $target); // ../
|
||||
// which resolved back to http://example.com/
|
||||
|
||||
// 3.0
|
||||
(string) UriResolver::relativize($base, $target); // //example.com
|
||||
```
|
||||
|
||||
The same applies when the base URI has a query or fragment component that an
|
||||
empty relative reference would otherwise inherit. When the base URI has an
|
||||
empty path as well and nothing would be inherited, shorter references such
|
||||
as the empty reference, `#fragment` or `?query` are still returned.
|
||||
|
||||
`relativize()` also no longer returns the empty reference when the target
|
||||
path equals the base path but the base has a fragment the target lacks, as
|
||||
the empty reference would reintroduce that fragment. A relative-path
|
||||
reference, or a query reference when the target has a query, is returned
|
||||
instead. When the relative-path reference would be a single path segment
|
||||
containing a colon, which would be mistaken for a scheme name, it is
|
||||
prefixed with `./` (for example `./a:b`); 2.x threw a `MalformedUriException`
|
||||
for such targets when the base had a query the target lacked.
|
||||
|
||||
#### HTTP Start-line Parsing
|
||||
|
||||
`Message::parseRequest()` and `Message::parseResponse()` now validate HTTP
|
||||
start-line fields more strictly. Malformed request methods, request targets
|
||||
containing whitespace or control characters, malformed protocol versions,
|
||||
invalid response status codes, invalid response spacing, and reason phrases
|
||||
containing invalid control characters now throw `InvalidArgumentException`.
|
||||
|
||||
`Request` and `Response` constructors and mutators apply the same validation to
|
||||
protocol versions, request targets, status codes, and reason phrases. If you
|
||||
parse raw HTTP messages or construct messages from partially validated input,
|
||||
normalize or reject invalid values before passing them to Guzzle PSR-7.
|
||||
|
||||
```php
|
||||
// 2.x-style tolerant input, no longer accepted in 3.0
|
||||
Message::parseRequest("GET /foo bar HTTP/1.1\r\nHost: example.com\r\n\r\n");
|
||||
new Response(200, [], null, 'HTTP/1.1');
|
||||
|
||||
// 3.0
|
||||
Message::parseRequest("GET /foo%20bar HTTP/1.1\r\nHost: example.com\r\n\r\n");
|
||||
new Response(200, [], null, '1.1');
|
||||
```
|
||||
|
||||
`ServerRequest::fromGlobals()` applies the same validation to the
|
||||
`REQUEST_METHOD` and `SERVER_PROTOCOL` server values. Malformed values that 2.x
|
||||
hydrated, such as the `SERVER_PROTOCOL` value `INCLUDED` that Apache sets for
|
||||
server-side include subrequests, now throw `InvalidArgumentException`. Sanitize
|
||||
`$_SERVER` before calling `fromGlobals()` if such environments must be
|
||||
tolerated.
|
||||
|
||||
`Request::withRequestTarget('')` throws `InvalidArgumentException`; omit the
|
||||
explicit request target to derive `/` or the URI-derived target automatically.
|
||||
|
||||
`Message::parseMessage()` no longer unfolds folded HTTP/1.0 messages whose
|
||||
start line carries control bytes in the request target; such messages now
|
||||
throw the obsolete-line-folding `InvalidArgumentException`.
|
||||
`Message::parseRequest()` and `Message::parseResponse()` rejected these
|
||||
messages either way.
|
||||
|
||||
#### Query Builder Values
|
||||
|
||||
`Query::build()` now rejects unsupported values instead of relying on PHP string
|
||||
casts. Query values must be scalar, `null`, stringable objects, or flat arrays of
|
||||
those values.
|
||||
|
||||
Nested arrays, resources, and objects without `__toString()` now throw
|
||||
`InvalidArgumentException`.
|
||||
|
||||
```php
|
||||
// Before: could produce warnings or silently mangle the value.
|
||||
Query::build(['filter' => ['name' => ['value']]]);
|
||||
|
||||
// After: use explicit query keys for nested query shapes.
|
||||
Query::build(['filter[name]' => 'value']);
|
||||
```
|
||||
|
||||
Flat arrays are still supported for repeated query parameters:
|
||||
|
||||
```php
|
||||
Query::build(['tag' => ['a', 'b']]);
|
||||
// tag=a&tag=b
|
||||
```
|
||||
|
||||
`Uri::withQueryValues()` is stricter than `Query::build()` and requires `string`
|
||||
or `null` values; cast numeric and boolean query values to string.
|
||||
|
||||
#### Non-string Scalar Bodies
|
||||
|
||||
`Utils::streamFor()` and message bodies no longer accept `int`, `float`, or
|
||||
`bool` values. Cast them to strings first.
|
||||
|
||||
```php
|
||||
// 2.x, no longer accepted in 3.0
|
||||
$response = new Response(200, [], 404);
|
||||
|
||||
// 3.0
|
||||
$response = new Response(200, [], '404');
|
||||
```
|
||||
|
||||
#### PumpStream Source Callables
|
||||
|
||||
`PumpStream` source callables must now return a non-empty string when producing
|
||||
data. Returning an empty string now throws `RuntimeException` instead of being
|
||||
retried indefinitely. Return `false` or `null` to signal EOF.
|
||||
|
||||
If your callable used `''` to mean "temporarily no data", update it to wait
|
||||
until data is available, return a non-empty string, or return `false` or `null`
|
||||
when the stream is complete.
|
||||
|
||||
#### Iterator-backed Streams
|
||||
|
||||
`Utils::streamFor()` now validates values yielded by `Iterator` instances before
|
||||
passing them to the internal `PumpStream`. Strings, integers, finite floats,
|
||||
booleans, `null`, and stringable objects are converted to string chunks.
|
||||
Non-finite floats, arrays, resources, and non-stringable objects now throw
|
||||
`UnexpectedValueException` when the stream is read.
|
||||
|
||||
Iterator exhaustion is now the only EOF signal for iterator-backed streams.
|
||||
Yielding `false`, `null`, or an empty string no longer ends the stream; those
|
||||
values are zero-length chunks and are skipped while the iterator advances. If
|
||||
your iterator yielded `false` or `null` to stop streaming, update it to finish
|
||||
iteration instead.
|
||||
|
||||
Avoid iterators that yield only zero-length chunks indefinitely. Such iterators
|
||||
never produce bytes and never reach EOF, so they cannot satisfy stream reads.
|
||||
|
||||
```php
|
||||
// Before: yielding false or null could stop an iterator-backed stream early.
|
||||
$stream = Utils::streamFor(new ArrayIterator([false, 'body']));
|
||||
|
||||
// After: false and null are skipped chunks. End the iterator to signal EOF.
|
||||
$stream = Utils::streamFor(new ArrayIterator(['body']));
|
||||
```
|
||||
|
||||
#### Stream Behavior Changes
|
||||
|
||||
All stream implementations now reject negative `read()` lengths with
|
||||
`RuntimeException`. In 2.x, some decorators passed negative lengths through,
|
||||
some returned sliced data, and some behavior varied by PHP version.
|
||||
|
||||
`LimitStream` now rejects negative offsets and limits below `-1`. For
|
||||
non-seekable streams, offsets are tracked by the number of bytes actually
|
||||
skipped. Short reads are retried until the offset is reached, EOF is reached, or
|
||||
the decorated stream stops making progress.
|
||||
|
||||
`StreamWrapper` now translates `RuntimeException` failures from the wrapped
|
||||
PSR-7 stream into PHP stream-wrapper failure values. When using a resource from
|
||||
`StreamWrapper::getResource()`, functions such as `fread()`, `fwrite()`,
|
||||
`fseek()`, `feof()`, and `fstat()` may now return normal PHP failure values
|
||||
instead of propagating the PSR-7 stream exception. Call the PSR-7 stream directly
|
||||
if you need exception-based failure handling.
|
||||
|
||||
The `StreamWrapper::stream_read()` callback no longer declares a native return
|
||||
type so read failures can return `false`. The `StreamWrapper::stream_tell()`
|
||||
callback no longer declares a native return type so post-seek position lookup
|
||||
failures can make `fseek()` fail.
|
||||
|
||||
#### Stream Mode Capabilities
|
||||
|
||||
`Stream::isReadable()` and `Stream::isWritable()` now follow PHP stream mode
|
||||
semantics more closely. Update modes are detected by the presence of `+`,
|
||||
including valid modes such as `rt+`, `wt+`, `at+`, `xt+`, and `ct+`.
|
||||
|
||||
Literal `rw` metadata is now treated as read-only, matching PHP real-file
|
||||
streams. If a custom stream wrapper previously exposed `rw` for a writable
|
||||
resource, open it with a valid update mode such as `r+`, `w+`, or `a+` instead.
|
||||
|
||||
#### Stream Copy Behavior
|
||||
|
||||
Stream sizes, offsets, high-water marks, and byte counts are now validated as
|
||||
non-negative PHP integers where applicable. Operations that would overflow
|
||||
`PHP_INT_MAX` throw `OverflowException` instead of silently wrapping or producing
|
||||
an invalid position or size.
|
||||
|
||||
`Utils::copyToStream()` now returns the number of bytes copied and throws a
|
||||
`RuntimeException` when the destination stream cannot make progress, for example
|
||||
a `BufferStream` at its high-water mark or a full `DroppingStream`. Its
|
||||
signature changed from `: void` to `: int`, but callers that ignore the return
|
||||
value do not need to change anything. In 2.x, the copy stopped silently when the
|
||||
destination could not make progress. For a guaranteed full copy, use a normal
|
||||
writable stream such as a file or `php://temp` stream.
|
||||
|
||||
#### Stream Timeout Detection
|
||||
|
||||
Timed-out stream operations now throw
|
||||
`GuzzleHttp\Psr7\Exception\TimeoutException`, which extends
|
||||
`RuntimeException`. `Stream::read()`, `Stream::write()`,
|
||||
`AppendStream::read()`, `CachingStream::read()`, `InflateStream::read()`,
|
||||
`Utils::copyToStream()`, `Utils::copyToString()`, `Utils::hash()`,
|
||||
`Utils::readLine()`, and `Utils::tryGetContents()` detect PHP-style stream
|
||||
timeout metadata when a read or write operation cannot make progress. Timeout
|
||||
detection is best-effort; custom stream implementations that do not expose
|
||||
`timed_out` metadata continue to behave as before. Previously, timed-out reads
|
||||
could be treated as EOF or return partial results, and timed-out writes could be
|
||||
reported as generic write failures or no-progress writes.
|
||||
|
||||
#### Message Body Summaries
|
||||
|
||||
`Message::bodySummary()` still summarizes seekable bodies from the beginning,
|
||||
even when the body was already partially read. It now restores the body cursor to
|
||||
the position it had before the summary was created. In 2.x, calling
|
||||
`bodySummary()` left seekable bodies rewound to the beginning. The optional
|
||||
`$truncateAt` argument now accepts `null` as an explicit request for the default
|
||||
summary length, matching the behavior of omitting the argument.
|
||||
|
||||
Most applications do not need to change anything. Check your code only if you
|
||||
called `bodySummary()` and then read the same body while relying on
|
||||
`bodySummary()` to leave the body rewound. If you need to read the body from the
|
||||
beginning after summarizing it, call `Message::rewindBody()` explicitly.
|
||||
|
||||
#### Stream Lifecycle
|
||||
|
||||
`FnStream` now treats `close()` and successful `detach()` calls as terminal
|
||||
lifecycle operations. Its configured `close` callback is invoked at most once;
|
||||
repeated `close()` calls are no-ops, destruction after explicit close no longer
|
||||
invokes the close callback, and closed or detached streams no longer forward
|
||||
read, write, seek, metadata, or stringification callbacks. `FnStream` also
|
||||
suppresses exceptions thrown by destructor-triggered close callbacks. Call
|
||||
`close()` explicitly if cleanup failures must be observed.
|
||||
|
||||
`CachingStream::close()` is now idempotent. Calling `close()` after `detach()`
|
||||
still closes the remote stream owned by the `CachingStream`, but it no longer
|
||||
closes the detached cache resource returned to the caller. Repeated `close()`
|
||||
calls are no-ops.
|
||||
|
||||
`InflateStream::close()` now also closes the compressed source stream that was
|
||||
passed to its constructor. In 2.x, closing an `InflateStream` left the source
|
||||
stream open. Call `detach()` instead of `close()` if the compressed source
|
||||
stream must stay open; `close()` after `detach()` no longer closes the source.
|
||||
|
||||
`PumpStream::close()` and `PumpStream::detach()` now discard internally buffered
|
||||
unread bytes. If a callable or iterator source returns more bytes than a read
|
||||
requested, drain the stream before closing it if you need those buffered bytes.
|
||||
|
||||
#### Multipart Part Headers and Metadata
|
||||
|
||||
`MultipartStream` no longer adds default `Content-Length` headers to individual
|
||||
`multipart/form-data` parts. RFC 7578 section 4.8 says multipart form-data
|
||||
parts must not include `Content-*` headers other than the supported multipart
|
||||
part headers, so 3.0 stops generating per-part `Content-Length` by default.
|
||||
|
||||
If your tests compare raw multipart payloads, remove the generated
|
||||
`Content-Length` lines from expected strings:
|
||||
|
||||
```text
|
||||
// 2.x generated:
|
||||
--boundary\r\n
|
||||
Content-Disposition: form-data; name="foo"\r\n
|
||||
Content-Length: 3\r\n
|
||||
\r\n
|
||||
bar\r\n
|
||||
|
||||
// 3.0 generates:
|
||||
--boundary\r\n
|
||||
Content-Disposition: form-data; name="foo"\r\n
|
||||
\r\n
|
||||
bar\r\n
|
||||
```
|
||||
|
||||
Applications can still pass an explicit `Content-Length` header in a multipart
|
||||
element's `headers` array if a non-standard peer requires it:
|
||||
|
||||
```php
|
||||
$body = new MultipartStream([
|
||||
[
|
||||
'name' => 'foo',
|
||||
'contents' => 'bar',
|
||||
'headers' => ['Content-Length' => '3'],
|
||||
],
|
||||
]);
|
||||
```
|
||||
|
||||
`MultipartStream` now escapes generated `Content-Disposition` `name` and
|
||||
`filename` parameters before serializing multipart part headers. Double quotes,
|
||||
carriage returns, and line feeds are encoded as `%22`, `%0D`, and `%0A`. Literal
|
||||
backslashes and other characters are serialized unchanged, matching browser
|
||||
multipart form submission behavior.
|
||||
|
||||
```php
|
||||
// Before: these values were interpolated into the generated part header.
|
||||
$body = new MultipartStream([
|
||||
[
|
||||
'name' => "field\"\r\nname",
|
||||
'filename' => "avatar\"\r\n.txt",
|
||||
'contents' => 'body',
|
||||
],
|
||||
]);
|
||||
|
||||
// After: the generated Content-Disposition parameters contain
|
||||
// field%22%0D%0Aname and avatar%22%0D%0A.txt.
|
||||
```
|
||||
|
||||
Explicit custom boundaries are now validated using RFC 2046 multipart boundary
|
||||
syntax. Omit the boundary or pass `null` to continue using a generated random
|
||||
boundary.
|
||||
|
||||
The string `'0'` is now treated as an explicit custom boundary and is serialized
|
||||
literally. In 2.x, PHP truthiness caused `new MultipartStream($elements, '0')`
|
||||
to use a generated random boundary. Omit the boundary or pass `null` when you
|
||||
want a generated boundary.
|
||||
|
||||
Custom multipart part header names and values are also validated before
|
||||
serialization. Header names must be valid HTTP tokens, and header values must be
|
||||
strings without CR, LF, or other invalid control bytes.
|
||||
|
||||
`MultipartStream` now preserves trailing spaces and tabs in custom multipart
|
||||
part header values when serializing the body. In 2.x, the final serialized part
|
||||
header line was trimmed as a side effect of removing the generated header
|
||||
terminator. Normal multipart parsers treat this optional whitespace as
|
||||
insignificant, but tests, signatures, or snapshots that compare raw multipart
|
||||
body bytes may need updated expectations.
|
||||
|
||||
#### URI Userinfo Redaction
|
||||
|
||||
`Utils::redactUserInfo()` now redacts all non-empty URI userinfo, including
|
||||
username-only userinfo. In 2.x, it only redacted the password portion when
|
||||
userinfo contained a password delimiter.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use GuzzleHttp\Psr7\Utils;
|
||||
|
||||
// 2.x: https://TOKEN@example.com
|
||||
// 3.0: https://***@example.com
|
||||
(string) Utils::redactUserInfo(new Uri('https://TOKEN@example.com'));
|
||||
|
||||
// 2.x: https://user:***@example.com
|
||||
// 3.0: https://***@example.com
|
||||
(string) Utils::redactUserInfo(new Uri('https://user:pass@example.com'));
|
||||
```
|
||||
|
||||
#### Header List Helpers
|
||||
|
||||
The deprecated `Header::normalize()` method was removed. Use
|
||||
`Header::splitList()` to split HTTP headers that are defined as comma-separated
|
||||
lists.
|
||||
|
||||
`Header::splitList()` now trims list elements with spaces, horizontal tabs,
|
||||
carriage returns, and line feeds. 2.x also trimmed null bytes and vertical
|
||||
tabs. Validated header values cannot contain those bytes, so this only affects
|
||||
strings passed to `Header::splitList()` directly.
|
||||
|
||||
#### Non-instantiable Utility Classes
|
||||
|
||||
Static utility and constant classes such as `Header`, `Message`, `MimeType`,
|
||||
`Query`, and `Utils` now have private constructors. Replace any accidental
|
||||
instantiation with static method calls or constant access.
|
||||
|
||||
#### Native PHP Serialization of Streams
|
||||
|
||||
Guzzle PSR-7 stream implementations no longer support native PHP `serialize()`
|
||||
or `unserialize()`. Persist stream contents explicitly and recreate streams with
|
||||
`Utils::streamFor()` when needed.
|
||||
|
||||
#### URI Normalization of Userinfo and Host
|
||||
|
||||
`UriNormalizer::CAPITALIZE_PERCENT_ENCODING` and
|
||||
`UriNormalizer::DECODE_UNRESERVED_CHARACTERS` now also apply to the userinfo and
|
||||
host components. In 2.x, these normalizations only rewrote the path, query, and
|
||||
fragment.
|
||||
|
||||
Since the host is case-insensitive and PSR-7 requires it to be lowercase, octets
|
||||
decoded in the host are lowercased. Reserved percent-encoded octets such as
|
||||
`%3A` are never decoded, so component boundaries cannot change, and these two
|
||||
flags never modify bracketed IP-literal hosts, which only the separate
|
||||
`UriNormalizer::CANONICALIZE_IPV6_HOST` normalization may canonicalize. Both
|
||||
flags are part of `UriNormalizer::PRESERVING_NORMALIZATIONS`, so the output of
|
||||
`UriNormalizer::normalize()` and the result of `UriNormalizer::isEquivalent()`
|
||||
can change for URIs whose userinfo or host contains percent-encoded octets.
|
||||
Custom `UriInterface` implementations now receive `withUserInfo()` or
|
||||
`withHost()` calls from the normalizer when a normalization changes those
|
||||
components; unchanged components are never rewritten. The rewrite is kept only
|
||||
when the value returned by the implementation matches the normalized form, and
|
||||
a userinfo with an empty user segment is never rewritten. If a setter returns a
|
||||
different representation, that rewrite is discarded, while other selected
|
||||
normalizations still apply, and setter exceptions propagate. No percent-encoding
|
||||
normalization is applied to a component with malformed percent syntax, such as a
|
||||
`%` not followed by two hexadecimal digits.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use GuzzleHttp\Psr7\UriNormalizer;
|
||||
|
||||
// 2.x: http://%75ser@ex%61mple.com/
|
||||
// 3.0: http://user@example.com/
|
||||
(string) UriNormalizer::normalize(new Uri('http://%75ser@ex%61mple.com/'));
|
||||
```
|
||||
|
||||
#### URI Ports and Authority Handling
|
||||
|
||||
Several 3.0 changes affect how URI ports are accepted, validated, and rendered.
|
||||
Each is described in its own section above:
|
||||
|
||||
- `UriInterface::withPort()` now requires `int|null`; see "Native PSR-7
|
||||
Parameter Types".
|
||||
- `Uri::fromParts()` validates ports instead of casting them, and `withHost()`
|
||||
rejects embedded `host:port` values; see "URI Host and Scheme Validation".
|
||||
- A generic `Uri` can represent ports 0 through 65535. Inbound HTTP authority
|
||||
parsing is stricter: `Message::parseRequest()` rejects zero-valued ports but
|
||||
accepts nonzero leading-zero ports, normalizing the reconstructed URI while
|
||||
preserving the raw `Host` or request-target text; see "HTTP Start-line
|
||||
Parsing" and "URI Host and Scheme Validation".
|
||||
- Server globals reject a zero-valued `HTTP_HOST` and validate `SERVER_PORT`
|
||||
when fallback authority reconstruction needs it. A recognized absolute-form
|
||||
or CONNECT `REQUEST_URI` authority takes precedence and can still produce a
|
||||
URI with port zero; see "URI Host and Scheme Validation".
|
||||
- Synthesized `Host` headers now include any non-default URI port; see "Request
|
||||
Modification Changes" and "Request Host Synchronization".
|
||||
|
||||
`Uri` now knows the default ports of the `ws` and `wss` schemes, 80 and 443 per
|
||||
RFC 6455. An explicit default port on a `ws` or `wss` URI is removed when the
|
||||
URI is constructed or modified, `Uri::isDefaultPort()` returns `true` for such
|
||||
URIs, and `UriNormalizer::normalize()` with the `REMOVE_DEFAULT_PORT` flag
|
||||
removes the port from other `UriInterface` implementations as well. In 2.x,
|
||||
these ports were preserved.
|
||||
|
||||
```php
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
|
||||
// 2.x: ws://example.com:80/chat
|
||||
// 3.0: ws://example.com/chat
|
||||
(string) new Uri('ws://example.com:80/chat');
|
||||
|
||||
// 2.x: 443
|
||||
// 3.0: null
|
||||
(new Uri('wss://example.com:443'))->getPort();
|
||||
```
|
||||
|
||||
Because a native `Uri` never carries a default `ws` or `wss` port, the `Host`
|
||||
header synchronized from such a request URI omits the port. `Request` and
|
||||
`Message` `Host` synthesis and `Utils::modifyRequest()` still append an explicit
|
||||
default port that a `ws` or `wss` URI from another `UriInterface` implementation
|
||||
reports.
|
||||
|
||||
`UriComparator::isCrossOrigin()` now applies these default ports when comparing
|
||||
effective ports, so two `ws` or `wss` URIs that differ only by an explicit
|
||||
default port, such as `ws://example.com/` and `ws://example.com:80/`, are
|
||||
same-origin no matter which `UriInterface` implementation supplies them. In 2.x,
|
||||
such pairs were considered cross-origin. Schemes other than `http`, `https`,
|
||||
`ws`, and `wss` still receive no implicit default port.
|
||||
|
||||
#### Sensitive Stack Trace Arguments
|
||||
|
||||
Credential-bearing URI, server-global, Authorization-header, and cookie
|
||||
arguments are marked with `#[\SensitiveParameter]`. PHP 8.2 and later replace
|
||||
those arguments in stack traces with `SensitiveParameterValue`. PHP 7.4 through
|
||||
8.1 do not redact trace arguments.
|
||||
|
||||
This does not redact logs, exception messages, object properties, wire traffic,
|
||||
captured variables, return values, user callbacks, or the separate executing
|
||||
object in an explicit backtrace.
|
||||
|
||||
1.x to 2.0
|
||||
----------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user