@@ -1488,8 +1488,10 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
1488
1488
/**
1489
1489
* @author Tobias Nyholm <[email protected] >
1490
1490
* @author Martijn van der Ven <[email protected] >
1491
+ *
1492
+ * @final This class should never be extended. See https://github.com/Nyholm/psr7/blob/master/doc/final.md
1491
1493
*/
1492
- final class Psr17Factory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
1494
+ class Psr17Factory implements RequestFactoryInterface, ResponseFactoryInterface, ServerRequestFactoryInterface, StreamFactoryInterface, UploadedFileFactoryInterface, UriFactoryInterface
1493
1495
{
1494
1496
public function createRequest (string $ method , $ uri ): RequestInterface
1495
1497
{
@@ -1603,12 +1605,12 @@ public function getHeaders(): array
1603
1605
1604
1606
public function hasHeader ($ header ): bool
1605
1607
{
1606
- return isset ($ this ->headerNames [\strtolower ($ header )]);
1608
+ return isset ($ this ->headerNames [\strtr ($ header, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ ' , ' abcdefghijklmnopqrstuvwxyz ' )]);
1607
1609
}
1608
1610
1609
1611
public function getHeader ($ header ): array
1610
1612
{
1611
- $ header = \strtolower ($ header );
1613
+ $ header = \strtr ($ header, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ ' , ' abcdefghijklmnopqrstuvwxyz ' );
1612
1614
if (!isset ($ this ->headerNames [$ header ])) {
1613
1615
return [];
1614
1616
}
@@ -1626,7 +1628,7 @@ public function getHeaderLine($header): string
1626
1628
public function withHeader ($ header , $ value ): self
1627
1629
{
1628
1630
$ value = $ this ->validateAndTrimHeader ($ header , $ value );
1629
- $ normalized = \strtolower ($ header );
1631
+ $ normalized = \strtr ($ header, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ ' , ' abcdefghijklmnopqrstuvwxyz ' );
1630
1632
1631
1633
$ new = clone $ this ;
1632
1634
if (isset ($ new ->headerNames [$ normalized ])) {
@@ -1652,7 +1654,7 @@ public function withAddedHeader($header, $value): self
1652
1654
1653
1655
public function withoutHeader ($ header ): self
1654
1656
{
1655
- $ normalized = \strtolower ($ header );
1657
+ $ normalized = \strtr ($ header, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ ' , ' abcdefghijklmnopqrstuvwxyz ' );
1656
1658
if (!isset ($ this ->headerNames [$ normalized ])) {
1657
1659
return $ this ;
1658
1660
}
@@ -1688,8 +1690,13 @@ public function withBody(StreamInterface $body): self
1688
1690
private function setHeaders (array $ headers ) /*:void*/
1689
1691
{
1690
1692
foreach ($ headers as $ header => $ value ) {
1693
+ if (\is_int ($ header )) {
1694
+ // If a header name was set to a numeric string, PHP will cast the key to an int.
1695
+ // We must cast it back to a string in order to comply with validation.
1696
+ $ header = (string ) $ header ;
1697
+ }
1691
1698
$ value = $ this ->validateAndTrimHeader ($ header , $ value );
1692
- $ normalized = \strtolower ($ header );
1699
+ $ normalized = \strtr ($ header, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ ' , ' abcdefghijklmnopqrstuvwxyz ' );
1693
1700
if (isset ($ this ->headerNames [$ normalized ])) {
1694
1701
$ header = $ this ->headerNames [$ normalized ];
1695
1702
$ this ->headers [$ header ] = \array_merge ($ this ->headers [$ header ], $ value );
@@ -1760,8 +1767,10 @@ private function validateAndTrimHeader($header, $values): array
1760
1767
/**
1761
1768
* @author Tobias Nyholm <[email protected] >
1762
1769
* @author Martijn van der Ven <[email protected] >
1770
+ *
1771
+ * @final This class should never be extended. See https://github.com/Nyholm/psr7/blob/master/doc/final.md
1763
1772
*/
1764
- final class Request implements RequestInterface
1773
+ class Request implements RequestInterface
1765
1774
{
1766
1775
use MessageTrait;
1767
1776
use RequestTrait;
@@ -1917,8 +1926,10 @@ private function updateHostFromUri() /*:void*/
1917
1926
* @author Michael Dowling and contributors to guzzlehttp/psr7
1918
1927
* @author Tobias Nyholm <[email protected] >
1919
1928
* @author Martijn van der Ven <[email protected] >
1929
+ *
1930
+ * @final This class should never be extended. See https://github.com/Nyholm/psr7/blob/master/doc/final.md
1920
1931
*/
1921
- final class Response implements ResponseInterface
1932
+ class Response implements ResponseInterface
1922
1933
{
1923
1934
use MessageTrait;
1924
1935
@@ -1980,7 +1991,7 @@ public function withStatus($code, $reasonPhrase = ''): self
1980
1991
1981
1992
$ code = (int ) $ code ;
1982
1993
if ($ code < 100 || $ code > 599 ) {
1983
- throw new \InvalidArgumentException ('Status code has to be an integer between 100 and 599 ' );
1994
+ throw new \InvalidArgumentException (\sprintf ( 'Status code has to be an integer between 100 and 599. A status code of %d was given ' , $ code ) );
1984
1995
}
1985
1996
1986
1997
$ new = clone $ this ;
@@ -2004,8 +2015,10 @@ public function withStatus($code, $reasonPhrase = ''): self
2004
2015
* @author Michael Dowling and contributors to guzzlehttp/psr7
2005
2016
* @author Tobias Nyholm <[email protected] >
2006
2017
* @author Martijn van der Ven <[email protected] >
2018
+ *
2019
+ * @final This class should never be extended. See https://github.com/Nyholm/psr7/blob/master/doc/final.md
2007
2020
*/
2008
- final class ServerRequest implements ServerRequestInterface
2021
+ class ServerRequest implements ServerRequestInterface
2009
2022
{
2010
2023
use MessageTrait;
2011
2024
use RequestTrait;
@@ -2160,13 +2173,17 @@ public function withoutAttribute($attribute): self
2160
2173
namespace Nyholm \Psr7 {
2161
2174
2162
2175
use Psr \Http \Message \StreamInterface ;
2176
+ use Symfony \Component \Debug \ErrorHandler as SymfonyLegacyErrorHandler ;
2177
+ use Symfony \Component \ErrorHandler \ErrorHandler as SymfonyErrorHandler ;
2163
2178
2164
2179
/**
2165
2180
* @author Michael Dowling and contributors to guzzlehttp/psr7
2166
2181
* @author Tobias Nyholm <[email protected] >
2167
2182
* @author Martijn van der Ven <[email protected] >
2183
+ *
2184
+ * @final This class should never be extended. See https://github.com/Nyholm/psr7/blob/master/doc/final.md
2168
2185
*/
2169
- final class Stream implements StreamInterface
2186
+ class Stream implements StreamInterface
2170
2187
{
2171
2188
/** @var resource|null A resource reference */
2172
2189
private $ stream ;
@@ -2180,7 +2197,7 @@ final class Stream implements StreamInterface
2180
2197
/** @var bool */
2181
2198
private $ writable ;
2182
2199
2183
- /** @var array|mixed|void|null */
2200
+ /** @var array|mixed|void|bool| null */
2184
2201
private $ uri ;
2185
2202
2186
2203
/** @var int|null */
@@ -2211,8 +2228,6 @@ private function __construct()
2211
2228
*
2212
2229
* @param string|resource|StreamInterface $body
2213
2230
*
2214
- * @return StreamInterface
2215
- *
2216
2231
* @throws \InvalidArgumentException
2217
2232
*/
2218
2233
public static function create ($ body = '' ): StreamInterface
@@ -2234,7 +2249,6 @@ public static function create($body = ''): StreamInterface
2234
2249
$ new ->seekable = $ meta ['seekable ' ] && 0 === \fseek ($ new ->stream , 0 , \SEEK_CUR );
2235
2250
$ new ->readable = isset (self ::READ_WRITE_HASH ['read ' ][$ meta ['mode ' ]]);
2236
2251
$ new ->writable = isset (self ::READ_WRITE_HASH ['write ' ][$ meta ['mode ' ]]);
2237
- $ new ->uri = $ new ->getMetadata ('uri ' );
2238
2252
2239
2253
return $ new ;
2240
2254
}
@@ -2250,15 +2264,31 @@ public function __destruct()
2250
2264
$ this ->close ();
2251
2265
}
2252
2266
2253
- public function __toString (): string
2267
+ /**
2268
+ * @return string
2269
+ */
2270
+ public function __toString ()
2254
2271
{
2255
2272
try {
2256
2273
if ($ this ->isSeekable ()) {
2257
2274
$ this ->seek (0 );
2258
2275
}
2259
2276
2260
2277
return $ this ->getContents ();
2261
- } catch (\Exception $ e ) {
2278
+ } catch (\Throwable $ e ) {
2279
+ if (\PHP_VERSION_ID >= 70400 ) {
2280
+ throw $ e ;
2281
+ }
2282
+
2283
+ if (\is_array ($ errorHandler = \set_error_handler ('var_dump ' ))) {
2284
+ $ errorHandler = $ errorHandler [0 ] ?? null ;
2285
+ }
2286
+ \restore_error_handler ();
2287
+
2288
+ if ($ e instanceof \Error || $ errorHandler instanceof SymfonyErrorHandler || $ errorHandler instanceof SymfonyLegacyErrorHandler) {
2289
+ return \trigger_error ((string ) $ e , \E_USER_ERROR );
2290
+ }
2291
+
2262
2292
return '' ;
2263
2293
}
2264
2294
}
@@ -2287,6 +2317,15 @@ public function detach()
2287
2317
return $ result ;
2288
2318
}
2289
2319
2320
+ private function getUri ()
2321
+ {
2322
+ if (false !== $ this ->uri ) {
2323
+ $ this ->uri = $ this ->getMetadata ('uri ' ) ?? false ;
2324
+ }
2325
+
2326
+ return $ this ->uri ;
2327
+ }
2328
+
2290
2329
public function getSize () /*:?int*/
2291
2330
{
2292
2331
if (null !== $ this ->size ) {
@@ -2298,8 +2337,8 @@ public function getSize() /*:?int*/
2298
2337
}
2299
2338
2300
2339
// Clear the stat cache if the stream has a URI
2301
- if ($ this ->uri ) {
2302
- \clearstatcache (true , $ this -> uri );
2340
+ if ($ uri = $ this ->getUri () ) {
2341
+ \clearstatcache (true , $ uri );
2303
2342
}
2304
2343
2305
2344
$ stats = \fstat ($ this ->stream );
@@ -2379,7 +2418,11 @@ public function read($length): string
2379
2418
throw new \RuntimeException ('Cannot read from non-readable stream ' );
2380
2419
}
2381
2420
2382
- return \fread ($ this ->stream , $ length );
2421
+ if (false === $ result = \fread ($ this ->stream , $ length )) {
2422
+ throw new \RuntimeException ('Unable to read from stream ' );
2423
+ }
2424
+
2425
+ return $ result ;
2383
2426
}
2384
2427
2385
2428
public function getContents (): string
@@ -2421,8 +2464,10 @@ public function getMetadata($key = null)
2421
2464
* @author Michael Dowling and contributors to guzzlehttp/psr7
2422
2465
* @author Tobias Nyholm <[email protected] >
2423
2466
* @author Martijn van der Ven <[email protected] >
2467
+ *
2468
+ * @final This class should never be extended. See https://github.com/Nyholm/psr7/blob/master/doc/final.md
2424
2469
*/
2425
- final class UploadedFile implements UploadedFileInterface
2470
+ class UploadedFile implements UploadedFileInterface
2426
2471
{
2427
2472
/** @var array */
2428
2473
/*private*/ const ERRORS = [
@@ -2595,8 +2640,10 @@ public function getClientMediaType() /*:?string*/
2595
2640
* @author Matthew Weier O'Phinney
2596
2641
* @author Tobias Nyholm <[email protected] >
2597
2642
* @author Martijn van der Ven <[email protected] >
2643
+ *
2644
+ * @final This class should never be extended. See https://github.com/Nyholm/psr7/blob/master/doc/final.md
2598
2645
*/
2599
- final class Uri implements UriInterface
2646
+ class Uri implements UriInterface
2600
2647
{
2601
2648
/*private*/ const SCHEMES = ['http ' => 80 , 'https ' => 443 ];
2602
2649
@@ -2633,9 +2680,9 @@ public function __construct(string $uri = '')
2633
2680
}
2634
2681
2635
2682
// Apply parse_url parts to a URI.
2636
- $ this ->scheme = isset ($ parts ['scheme ' ]) ? \strtolower ($ parts ['scheme ' ]) : '' ;
2683
+ $ this ->scheme = isset ($ parts ['scheme ' ]) ? \strtr ($ parts ['scheme ' ], ' ABCDEFGHIJKLMNOPQRSTUVWXYZ ' , ' abcdefghijklmnopqrstuvwxyz ' ) : '' ;
2637
2684
$ this ->userInfo = $ parts ['user ' ] ?? '' ;
2638
- $ this ->host = isset ($ parts ['host ' ]) ? \strtolower ($ parts ['host ' ]) : '' ;
2685
+ $ this ->host = isset ($ parts ['host ' ]) ? \strtr ($ parts ['host ' ], ' ABCDEFGHIJKLMNOPQRSTUVWXYZ ' , ' abcdefghijklmnopqrstuvwxyz ' ) : '' ;
2639
2686
$ this ->port = isset ($ parts ['port ' ]) ? $ this ->filterPort ($ parts ['port ' ]) : null ;
2640
2687
$ this ->path = isset ($ parts ['path ' ]) ? $ this ->filterPath ($ parts ['path ' ]) : '' ;
2641
2688
$ this ->query = isset ($ parts ['query ' ]) ? $ this ->filterQueryAndFragment ($ parts ['query ' ]) : '' ;
@@ -2710,7 +2757,7 @@ public function withScheme($scheme): self
2710
2757
throw new \InvalidArgumentException ('Scheme must be a string ' );
2711
2758
}
2712
2759
2713
- if ($ this ->scheme === $ scheme = \strtolower ($ scheme )) {
2760
+ if ($ this ->scheme === $ scheme = \strtr ($ scheme, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ ' , ' abcdefghijklmnopqrstuvwxyz ' )) {
2714
2761
return $ this ;
2715
2762
}
2716
2763
@@ -2744,7 +2791,7 @@ public function withHost($host): self
2744
2791
throw new \InvalidArgumentException ('Host must be a string ' );
2745
2792
}
2746
2793
2747
- if ($ this ->host === $ host = \strtolower ($ host )) {
2794
+ if ($ this ->host === $ host = \strtr ($ host, ' ABCDEFGHIJKLMNOPQRSTUVWXYZ ' , ' abcdefghijklmnopqrstuvwxyz ' )) {
2748
2795
return $ this ;
2749
2796
}
2750
2797
@@ -2941,20 +2988,42 @@ public function fromGlobals(): ServerRequestInterface
2941
2988
2942
2989
$ headers = \function_exists ('getallheaders ' ) ? getallheaders () : static ::getHeadersFromServer ($ _SERVER );
2943
2990
2944
- return $ this ->fromArrays ($ server , $ headers , $ _COOKIE , $ _GET , $ _POST , $ _FILES , \fopen ('php://input ' , 'r ' ) ?: null );
2991
+ $ post = null ;
2992
+ if ('POST ' === $ this ->getMethodFromEnv ($ server )) {
2993
+ foreach ($ headers as $ headerName => $ headerValue ) {
2994
+ if (true === \is_int ($ headerName ) || 'content-type ' !== \strtolower ($ headerName )) {
2995
+ continue ;
2996
+ }
2997
+ if (\in_array (
2998
+ \strtolower (\trim (\explode ('; ' , $ headerValue , 2 )[0 ])),
2999
+ ['application/x-www-form-urlencoded ' , 'multipart/form-data ' ]
3000
+ )) {
3001
+ $ post = $ _POST ;
3002
+
3003
+ break ;
3004
+ }
3005
+ }
3006
+ }
3007
+
3008
+ return $ this ->fromArrays ($ server , $ headers , $ _COOKIE , $ _GET , $ post , $ _FILES , \fopen ('php://input ' , 'r ' ) ?: null );
2945
3009
}
2946
3010
2947
3011
/**
2948
3012
* {@inheritdoc}
2949
3013
*/
2950
- public function fromArrays (array $ server , array $ headers = [], array $ cookie = [], array $ get = [], array $ post = [] , array $ files = [], $ body = null ): ServerRequestInterface
3014
+ public function fromArrays (array $ server , array $ headers = [], array $ cookie = [], array $ get = [], /*? array*/ $ post = null , array $ files = [], $ body = null ): ServerRequestInterface
2951
3015
{
2952
3016
$ method = $ this ->getMethodFromEnv ($ server );
2953
3017
$ uri = $ this ->getUriFromEnvWithHTTP ($ server );
2954
3018
$ protocol = isset ($ server ['SERVER_PROTOCOL ' ]) ? \str_replace ('HTTP/ ' , '' , $ server ['SERVER_PROTOCOL ' ]) : '1.1 ' ;
2955
3019
2956
3020
$ serverRequest = $ this ->serverRequestFactory ->createServerRequest ($ method , $ uri , $ server );
2957
3021
foreach ($ headers as $ name => $ value ) {
3022
+ // Because PHP automatically casts array keys set with numeric strings to integers, we have to make sure
3023
+ // that numeric headers will not be sent along as integers, as withAddedHeader can only accept strings.
3024
+ if (\is_int ($ name )) {
3025
+ $ name = (string ) $ name ;
3026
+ }
2958
3027
$ serverRequest = $ serverRequest ->withAddedHeader ($ name , $ value );
2959
3028
}
2960
3029
@@ -2981,7 +3050,7 @@ public function fromArrays(array $server, array $headers = [], array $cookie = [
2981
3050
}
2982
3051
2983
3052
/**
2984
- * Implementation from Zend \Diactoros\marshalHeadersFromSapi().
3053
+ * Implementation from Laminas \Diactoros\marshalHeadersFromSapi().
2985
3054
*/
2986
3055
public static function getHeadersFromServer (array $ server ): array
2987
3056
{
@@ -3080,10 +3149,14 @@ private function createUploadedFileFromSpec(array $value)
3080
3149
return $ this ->normalizeNestedFileSpec ($ value );
3081
3150
}
3082
3151
3083
- try {
3084
- $ stream = $ this ->streamFactory ->createStreamFromFile ($ value ['tmp_name ' ]);
3085
- } catch (\RuntimeException $ e ) {
3152
+ if (UPLOAD_ERR_OK !== $ value ['error ' ]) {
3086
3153
$ stream = $ this ->streamFactory ->createStream ();
3154
+ } else {
3155
+ try {
3156
+ $ stream = $ this ->streamFactory ->createStreamFromFile ($ value ['tmp_name ' ]);
3157
+ } catch (\RuntimeException $ e ) {
3158
+ $ stream = $ this ->streamFactory ->createStream ();
3159
+ }
3087
3160
}
3088
3161
3089
3162
return $ this ->uploadedFileFactory ->createUploadedFile (
@@ -3101,8 +3174,6 @@ private function createUploadedFileFromSpec(array $value)
3101
3174
* Loops through all nested files and returns a normalized array of
3102
3175
* UploadedFileInterface instances.
3103
3176
*
3104
- * @param array $files
3105
- *
3106
3177
* @return UploadedFileInterface[]
3107
3178
*/
3108
3179
private function normalizeNestedFileSpec (array $ files = []): array
@@ -3199,7 +3270,7 @@ public function fromGlobals(): ServerRequestInterface;
3199
3270
* @param array $headers typically the output of getallheaders() or similar structure
3200
3271
* @param array $cookie typically $_COOKIE or similar structure
3201
3272
* @param array $get typically $_GET or similar structure
3202
- * @param array $post typically $_POST or similar structure
3273
+ * @param array|null $post typically $_POST or similar structure, represents parsed request body
3203
3274
* @param array $files typically $_FILES or similar structure
3204
3275
* @param StreamInterface|resource|string|null $body Typically stdIn
3205
3276
*
@@ -3209,8 +3280,7 @@ public function fromArrays(
3209
3280
array $ server ,
3210
3281
array $ headers = [],
3211
3282
array $ cookie = [],
3212
- array $ get = [],
3213
- array $ post = [],
3283
+ array $ get = [], /*?array*/ $ post = null ,
3214
3284
array $ files = [],
3215
3285
$ body = null
3216
3286
): ServerRequestInterface ;
@@ -3219,8 +3289,6 @@ public function fromArrays(
3219
3289
* Get parsed headers from ($_SERVER) array.
3220
3290
*
3221
3291
* @param array $server typically $_SERVER or similar structure
3222
- *
3223
- * @return array
3224
3292
*/
3225
3293
public static function getHeadersFromServer (array $ server ): array ;
3226
3294
}
0 commit comments