Passed
Pull Request — master (#31)
by Anatoly
03:57
created
src/Uri/Component/User.php 2 patches
Indentation   +63 added lines, -63 removed lines patch added patch discarded remove patch
@@ -31,74 +31,74 @@
 block discarded – undo
31 31
 final class User implements ComponentInterface
32 32
 {
33 33
 
34
-    /**
35
-     * Regular expression used for the component normalization
36
-     *
37
-     * @var string
38
-     */
39
-    // phpcs:ignore Generic.Files.LineLength
40
-    private const NORMALIZATION_REGEX = '/(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x2e\x30-\x39\x3b\x3d\x41-\x5a\x5f\x61-\x7a\x7e]+)|(.?)/u';
34
+	/**
35
+	 * Regular expression used for the component normalization
36
+	 *
37
+	 * @var string
38
+	 */
39
+	// phpcs:ignore Generic.Files.LineLength
40
+	private const NORMALIZATION_REGEX = '/(?:%[0-9A-Fa-f]{2}|[\x21\x24\x26-\x2e\x30-\x39\x3b\x3d\x41-\x5a\x5f\x61-\x7a\x7e]+)|(.?)/u';
41 41
 
42
-    /**
43
-     * The component value
44
-     *
45
-     * @var string
46
-     */
47
-    private string $value = '';
42
+	/**
43
+	 * The component value
44
+	 *
45
+	 * @var string
46
+	 */
47
+	private string $value = '';
48 48
 
49
-    /**
50
-     * Constructor of the class
51
-     *
52
-     * @param mixed $value
53
-     *
54
-     * @throws InvalidArgumentException
55
-     *         If the component isn't valid.
56
-     */
57
-    public function __construct($value)
58
-    {
59
-        if ($value === '') {
60
-            return;
61
-        }
49
+	/**
50
+	 * Constructor of the class
51
+	 *
52
+	 * @param mixed $value
53
+	 *
54
+	 * @throws InvalidArgumentException
55
+	 *         If the component isn't valid.
56
+	 */
57
+	public function __construct($value)
58
+	{
59
+		if ($value === '') {
60
+			return;
61
+		}
62 62
 
63
-        if (!is_string($value)) {
64
-            throw new InvalidArgumentException('URI component "user" must be a string');
65
-        }
63
+		if (!is_string($value)) {
64
+			throw new InvalidArgumentException('URI component "user" must be a string');
65
+		}
66 66
 
67
-        $this->value = (string) preg_replace_callback(
68
-            self::NORMALIZATION_REGEX,
69
-            static function (array $match): string {
70
-                /** @var array{0: string, 1?: string} $match */
71
-                return isset($match[1]) ? rawurlencode($match[1]) : $match[0];
72
-            },
73
-            $value
74
-        );
75
-    }
67
+		$this->value = (string) preg_replace_callback(
68
+			self::NORMALIZATION_REGEX,
69
+			static function (array $match): string {
70
+				/** @var array{0: string, 1?: string} $match */
71
+				return isset($match[1]) ? rawurlencode($match[1]) : $match[0];
72
+			},
73
+			$value
74
+		);
75
+	}
76 76
 
77
-    /**
78
-     * Creates a user component
79
-     *
80
-     * @param mixed $user
81
-     *
82
-     * @return User
83
-     *
84
-     * @throws InvalidArgumentException
85
-     */
86
-    public static function create($user): User
87
-    {
88
-        if ($user instanceof User) {
89
-            return $user;
90
-        }
77
+	/**
78
+	 * Creates a user component
79
+	 *
80
+	 * @param mixed $user
81
+	 *
82
+	 * @return User
83
+	 *
84
+	 * @throws InvalidArgumentException
85
+	 */
86
+	public static function create($user): User
87
+	{
88
+		if ($user instanceof User) {
89
+			return $user;
90
+		}
91 91
 
92
-        return new User($user);
93
-    }
92
+		return new User($user);
93
+	}
94 94
 
95
-    /**
96
-     * {@inheritdoc}
97
-     *
98
-     * @return string
99
-     */
100
-    public function getValue(): string
101
-    {
102
-        return $this->value;
103
-    }
95
+	/**
96
+	 * {@inheritdoc}
97
+	 *
98
+	 * @return string
99
+	 */
100
+	public function getValue(): string
101
+	{
102
+		return $this->value;
103
+	}
104 104
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -66,7 +66,7 @@
 block discarded – undo
66 66
 
67 67
         $this->value = (string) preg_replace_callback(
68 68
             self::NORMALIZATION_REGEX,
69
-            static function (array $match): string {
69
+            static function(array $match): string {
70 70
                 /** @var array{0: string, 1?: string} $match */
71 71
                 return isset($match[1]) ? rawurlencode($match[1]) : $match[0];
72 72
             },
Please login to merge, or discard this patch.
src/Stream/FileStream.php 1 patch
Indentation   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -30,26 +30,26 @@
 block discarded – undo
30 30
 final class FileStream extends Stream
31 31
 {
32 32
 
33
-    /**
34
-     * Constructor of the class
35
-     *
36
-     * @param string $filename
37
-     * @param string $mode
38
-     *
39
-     * @throws InvalidArgumentException
40
-     */
41
-    public function __construct(string $filename, string $mode)
42
-    {
43
-        $resource = @fopen($filename, $mode);
44
-
45
-        if (!is_resource($resource)) {
46
-            throw new InvalidArgumentException(sprintf(
47
-                'Unable to open the file "%s" in the mode "%s"',
48
-                $filename,
49
-                $mode
50
-            ));
51
-        }
52
-
53
-        parent::__construct($resource);
54
-    }
33
+	/**
34
+	 * Constructor of the class
35
+	 *
36
+	 * @param string $filename
37
+	 * @param string $mode
38
+	 *
39
+	 * @throws InvalidArgumentException
40
+	 */
41
+	public function __construct(string $filename, string $mode)
42
+	{
43
+		$resource = @fopen($filename, $mode);
44
+
45
+		if (!is_resource($resource)) {
46
+			throw new InvalidArgumentException(sprintf(
47
+				'Unable to open the file "%s" in the mode "%s"',
48
+				$filename,
49
+				$mode
50
+			));
51
+		}
52
+
53
+		parent::__construct($resource);
54
+	}
55 55
 }
Please login to merge, or discard this patch.
src/Stream/PhpInputStream.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -28,21 +28,21 @@
 block discarded – undo
28 28
 final class PhpInputStream extends Stream
29 29
 {
30 30
 
31
-    /**
32
-     * Constructor of the class
33
-     */
34
-    public function __construct()
35
-    {
36
-        /** @var resource */
37
-        $input = fopen('php://input', 'rb');
31
+	/**
32
+	 * Constructor of the class
33
+	 */
34
+	public function __construct()
35
+	{
36
+		/** @var resource */
37
+		$input = fopen('php://input', 'rb');
38 38
 
39
-        /** @var resource */
40
-        $handle = fopen('php://temp', 'r+b');
39
+		/** @var resource */
40
+		$handle = fopen('php://temp', 'r+b');
41 41
 
42
-        stream_copy_to_stream($input, $handle);
42
+		stream_copy_to_stream($input, $handle);
43 43
 
44
-        parent::__construct($handle);
44
+		parent::__construct($handle);
45 45
 
46
-        $this->rewind();
47
-    }
46
+		$this->rewind();
47
+	}
48 48
 }
Please login to merge, or discard this patch.
src/Uri.php 1 patch
Indentation   +474 added lines, -474 removed lines patch added patch discarded remove patch
@@ -41,478 +41,478 @@
 block discarded – undo
41 41
 class Uri implements UriInterface
42 42
 {
43 43
 
44
-    /**
45
-     * Scheme of the URI
46
-     *
47
-     * @var string
48
-     */
49
-    private string $scheme = '';
50
-
51
-    /**
52
-     * User Information of the URI
53
-     *
54
-     * @var string
55
-     */
56
-    private string $userInfo = '';
57
-
58
-    /**
59
-     * Host of the URI
60
-     *
61
-     * @var string
62
-     */
63
-    private string $host = '';
64
-
65
-    /**
66
-     * Port of the URI
67
-     *
68
-     * @var int|null
69
-     */
70
-    private ?int $port = null;
71
-
72
-    /**
73
-     * Path of the URI
74
-     *
75
-     * @var string
76
-     */
77
-    private string $path = '';
78
-
79
-    /**
80
-     * Query of the URI
81
-     *
82
-     * @var string
83
-     */
84
-    private string $query = '';
85
-
86
-    /**
87
-     * Fragment of the URI
88
-     *
89
-     * @var string
90
-     */
91
-    private string $fragment = '';
92
-
93
-    /**
94
-     * Constructor of the class
95
-     *
96
-     * @param string $uri
97
-     *
98
-     * @throws InvalidArgumentException
99
-     *         If the URI isn't valid.
100
-     */
101
-    public function __construct(string $uri = '')
102
-    {
103
-        if ($uri === '') {
104
-            return;
105
-        }
106
-
107
-        $components = parse_url($uri);
108
-        if ($components === false) {
109
-            throw new InvalidArgumentException('Invalid URI');
110
-        }
111
-
112
-        if (isset($components['scheme'])) {
113
-            $this->setScheme($components['scheme']);
114
-        }
115
-
116
-        if (isset($components['user'])) {
117
-            $this->setUserInfo(
118
-                $components['user'],
119
-                $components['pass'] ?? null
120
-            );
121
-        }
122
-
123
-        if (isset($components['host'])) {
124
-            $this->setHost($components['host']);
125
-        }
126
-
127
-        if (isset($components['port'])) {
128
-            $this->setPort($components['port']);
129
-        }
130
-
131
-        if (isset($components['path'])) {
132
-            $this->setPath($components['path']);
133
-        }
134
-
135
-        if (isset($components['query'])) {
136
-            $this->setQuery($components['query']);
137
-        }
138
-
139
-        if (isset($components['fragment'])) {
140
-            $this->setFragment($components['fragment']);
141
-        }
142
-    }
143
-
144
-    /**
145
-     * Creates a URI
146
-     *
147
-     * @param mixed $uri
148
-     *
149
-     * @return UriInterface
150
-     *
151
-     * @throws InvalidArgumentException
152
-     *         If the URI isn't valid.
153
-     */
154
-    public static function create($uri): UriInterface
155
-    {
156
-        if ($uri instanceof UriInterface) {
157
-            return $uri;
158
-        }
159
-
160
-        if (!is_string($uri)) {
161
-            throw new InvalidArgumentException('URI should be a string');
162
-        }
163
-
164
-        return new self($uri);
165
-    }
166
-
167
-    /**
168
-     * {@inheritdoc}
169
-     *
170
-     * @throws InvalidArgumentException
171
-     *         If the scheme isn't valid.
172
-     */
173
-    public function withScheme($scheme): UriInterface
174
-    {
175
-        $clone = clone $this;
176
-        $clone->setScheme($scheme);
177
-
178
-        return $clone;
179
-    }
180
-
181
-    /**
182
-     * {@inheritdoc}
183
-     *
184
-     * @throws InvalidArgumentException
185
-     *         If the user information isn't valid.
186
-     */
187
-    public function withUserInfo($user, $password = null): UriInterface
188
-    {
189
-        $clone = clone $this;
190
-        $clone->setUserInfo($user, $password);
191
-
192
-        return $clone;
193
-    }
194
-
195
-    /**
196
-     * {@inheritdoc}
197
-     *
198
-     * @throws InvalidArgumentException
199
-     *         If the host isn't valid.
200
-     */
201
-    public function withHost($host): UriInterface
202
-    {
203
-        $clone = clone $this;
204
-        $clone->setHost($host);
205
-
206
-        return $clone;
207
-    }
208
-
209
-    /**
210
-     * {@inheritdoc}
211
-     *
212
-     * @throws InvalidArgumentException
213
-     *         If the port isn't valid.
214
-     */
215
-    public function withPort($port): UriInterface
216
-    {
217
-        $clone = clone $this;
218
-        $clone->setPort($port);
219
-
220
-        return $clone;
221
-    }
222
-
223
-    /**
224
-     * {@inheritdoc}
225
-     *
226
-     * @throws InvalidArgumentException
227
-     *         If the path isn't valid.
228
-     */
229
-    public function withPath($path): UriInterface
230
-    {
231
-        $clone = clone $this;
232
-        $clone->setPath($path);
233
-
234
-        return $clone;
235
-    }
236
-
237
-    /**
238
-     * {@inheritdoc}
239
-     *
240
-     * @throws InvalidArgumentException
241
-     *         If the query isn't valid.
242
-     */
243
-    public function withQuery($query): UriInterface
244
-    {
245
-        $clone = clone $this;
246
-        $clone->setQuery($query);
247
-
248
-        return $clone;
249
-    }
250
-
251
-    /**
252
-     * {@inheritdoc}
253
-     *
254
-     * @throws InvalidArgumentException
255
-     *         If the fragment isn't valid.
256
-     */
257
-    public function withFragment($fragment): UriInterface
258
-    {
259
-        $clone = clone $this;
260
-        $clone->setFragment($fragment);
261
-
262
-        return $clone;
263
-    }
264
-
265
-    /**
266
-     * {@inheritdoc}
267
-     */
268
-    public function getScheme(): string
269
-    {
270
-        return $this->scheme;
271
-    }
272
-
273
-    /**
274
-     * {@inheritdoc}
275
-     */
276
-    public function getUserInfo(): string
277
-    {
278
-        return $this->userInfo;
279
-    }
280
-
281
-    /**
282
-     * {@inheritdoc}
283
-     */
284
-    public function getHost(): string
285
-    {
286
-        return $this->host;
287
-    }
288
-
289
-    /**
290
-     * {@inheritdoc}
291
-     */
292
-    public function getPort(): ?int
293
-    {
294
-        // The 80 is the default port number for the HTTP protocol.
295
-        if ($this->port === 80 && $this->scheme === 'http') {
296
-            return null;
297
-        }
298
-
299
-        // The 443 is the default port number for the HTTPS protocol.
300
-        if ($this->port === 443 && $this->scheme === 'https') {
301
-            return null;
302
-        }
303
-
304
-        return $this->port;
305
-    }
306
-
307
-    /**
308
-     * {@inheritdoc}
309
-     */
310
-    public function getPath(): string
311
-    {
312
-        // CVE-2015-3257
313
-        if (strncmp($this->path, '//', 2) === 0) {
314
-            return '/' . ltrim($this->path, '/');
315
-        }
316
-
317
-        return $this->path;
318
-    }
319
-
320
-    /**
321
-     * {@inheritdoc}
322
-     */
323
-    public function getQuery(): string
324
-    {
325
-        return $this->query;
326
-    }
327
-
328
-    /**
329
-     * {@inheritdoc}
330
-     */
331
-    public function getFragment(): string
332
-    {
333
-        return $this->fragment;
334
-    }
335
-
336
-    /**
337
-     * {@inheritdoc}
338
-     */
339
-    public function getAuthority(): string
340
-    {
341
-        // The host is the basic subcomponent.
342
-        if ($this->host === '') {
343
-            return '';
344
-        }
345
-
346
-        $authority = $this->host;
347
-        if ($this->userInfo !== '') {
348
-            $authority = $this->userInfo . '@' . $authority;
349
-        }
350
-
351
-        $port = $this->getPort();
352
-        if ($port !== null) {
353
-            $authority = $authority . ':' . $port;
354
-        }
355
-
356
-        return $authority;
357
-    }
358
-
359
-    /**
360
-     * {@inheritdoc}
361
-     */
362
-    public function __toString(): string
363
-    {
364
-        $uri = '';
365
-
366
-        $scheme = $this->scheme;
367
-        if ($scheme !== '') {
368
-            $uri .= $scheme . ':';
369
-        }
370
-
371
-        $authority = $this->getAuthority();
372
-        if ($authority !== '') {
373
-            $uri .= '//' . $authority;
374
-        }
375
-
376
-        $path = $this->path;
377
-        if ($path !== '') {
378
-            // https://github.com/sunrise-php/uri/issues/31
379
-            // https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
380
-            //
381
-            // If a URI contains an authority component,
382
-            // then the path component must either be empty
383
-            // or begin with a slash ("/") character.
384
-            if ($authority !== '' && strncmp($path, '/', 1) !== 0) {
385
-                $path = '/' . $path;
386
-            }
387
-
388
-            // https://github.com/sunrise-php/uri/issues/31
389
-            // https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
390
-            //
391
-            // If a URI does not contain an authority component,
392
-            // then the path cannot begin with two slash characters ("//").
393
-            if ($authority === '' && strncmp($path, '//', 2) === 0) {
394
-                $path = '/' . ltrim($path, '/');
395
-            }
396
-
397
-            $uri .= $path;
398
-        }
399
-
400
-        $query = $this->query;
401
-        if ($query !== '') {
402
-            $uri .= '?' . $query;
403
-        }
404
-
405
-        $fragment = $this->fragment;
406
-        if ($fragment !== '') {
407
-            $uri .= '#' . $fragment;
408
-        }
409
-
410
-        return $uri;
411
-    }
412
-
413
-    /**
414
-     * Sets the given scheme to the URI
415
-     *
416
-     * @param mixed $scheme
417
-     *
418
-     * @return void
419
-     *
420
-     * @throws InvalidArgumentException
421
-     *         If the scheme isn't valid.
422
-     */
423
-    final protected function setScheme($scheme): void
424
-    {
425
-        $this->scheme = (new Scheme($scheme))->getValue();
426
-    }
427
-
428
-    /**
429
-     * Sets the given user information to the URI
430
-     *
431
-     * @param mixed $user
432
-     * @param mixed $password
433
-     *
434
-     * @return void
435
-     *
436
-     * @throws InvalidArgumentException
437
-     *         If the user information isn't valid.
438
-     */
439
-    final protected function setUserInfo($user, $password): void
440
-    {
441
-        $this->userInfo = (new UserInfo($user, $password))->getValue();
442
-    }
443
-
444
-    /**
445
-     * Sets the given host to the URI
446
-     *
447
-     * @param mixed $host
448
-     *
449
-     * @return void
450
-     *
451
-     * @throws InvalidArgumentException
452
-     *         If the host isn't valid.
453
-     */
454
-    final protected function setHost($host): void
455
-    {
456
-        $this->host = (new Host($host))->getValue();
457
-    }
458
-
459
-    /**
460
-     * Sets the given port to the URI
461
-     *
462
-     * @param mixed $port
463
-     *
464
-     * @return void
465
-     *
466
-     * @throws InvalidArgumentException
467
-     *         If the port isn't valid.
468
-     */
469
-    final protected function setPort($port): void
470
-    {
471
-        $this->port = (new Port($port))->getValue();
472
-    }
473
-
474
-    /**
475
-     * Sets the given path to the URI
476
-     *
477
-     * @param mixed $path
478
-     *
479
-     * @return void
480
-     *
481
-     * @throws InvalidArgumentException
482
-     *         If the path isn't valid.
483
-     */
484
-    final protected function setPath($path): void
485
-    {
486
-        $this->path = (new Path($path))->getValue();
487
-    }
488
-
489
-    /**
490
-     * Sets the given query to the URI
491
-     *
492
-     * @param mixed $query
493
-     *
494
-     * @return void
495
-     *
496
-     * @throws InvalidArgumentException
497
-     *         If the query isn't valid.
498
-     */
499
-    final protected function setQuery($query): void
500
-    {
501
-        $this->query = (new Query($query))->getValue();
502
-    }
503
-
504
-    /**
505
-     * Sets the given fragment to the URI
506
-     *
507
-     * @param mixed $fragment
508
-     *
509
-     * @return void
510
-     *
511
-     * @throws InvalidArgumentException
512
-     *         If the fragment isn't valid.
513
-     */
514
-    final protected function setFragment($fragment): void
515
-    {
516
-        $this->fragment = (new Fragment($fragment))->getValue();
517
-    }
44
+	/**
45
+	 * Scheme of the URI
46
+	 *
47
+	 * @var string
48
+	 */
49
+	private string $scheme = '';
50
+
51
+	/**
52
+	 * User Information of the URI
53
+	 *
54
+	 * @var string
55
+	 */
56
+	private string $userInfo = '';
57
+
58
+	/**
59
+	 * Host of the URI
60
+	 *
61
+	 * @var string
62
+	 */
63
+	private string $host = '';
64
+
65
+	/**
66
+	 * Port of the URI
67
+	 *
68
+	 * @var int|null
69
+	 */
70
+	private ?int $port = null;
71
+
72
+	/**
73
+	 * Path of the URI
74
+	 *
75
+	 * @var string
76
+	 */
77
+	private string $path = '';
78
+
79
+	/**
80
+	 * Query of the URI
81
+	 *
82
+	 * @var string
83
+	 */
84
+	private string $query = '';
85
+
86
+	/**
87
+	 * Fragment of the URI
88
+	 *
89
+	 * @var string
90
+	 */
91
+	private string $fragment = '';
92
+
93
+	/**
94
+	 * Constructor of the class
95
+	 *
96
+	 * @param string $uri
97
+	 *
98
+	 * @throws InvalidArgumentException
99
+	 *         If the URI isn't valid.
100
+	 */
101
+	public function __construct(string $uri = '')
102
+	{
103
+		if ($uri === '') {
104
+			return;
105
+		}
106
+
107
+		$components = parse_url($uri);
108
+		if ($components === false) {
109
+			throw new InvalidArgumentException('Invalid URI');
110
+		}
111
+
112
+		if (isset($components['scheme'])) {
113
+			$this->setScheme($components['scheme']);
114
+		}
115
+
116
+		if (isset($components['user'])) {
117
+			$this->setUserInfo(
118
+				$components['user'],
119
+				$components['pass'] ?? null
120
+			);
121
+		}
122
+
123
+		if (isset($components['host'])) {
124
+			$this->setHost($components['host']);
125
+		}
126
+
127
+		if (isset($components['port'])) {
128
+			$this->setPort($components['port']);
129
+		}
130
+
131
+		if (isset($components['path'])) {
132
+			$this->setPath($components['path']);
133
+		}
134
+
135
+		if (isset($components['query'])) {
136
+			$this->setQuery($components['query']);
137
+		}
138
+
139
+		if (isset($components['fragment'])) {
140
+			$this->setFragment($components['fragment']);
141
+		}
142
+	}
143
+
144
+	/**
145
+	 * Creates a URI
146
+	 *
147
+	 * @param mixed $uri
148
+	 *
149
+	 * @return UriInterface
150
+	 *
151
+	 * @throws InvalidArgumentException
152
+	 *         If the URI isn't valid.
153
+	 */
154
+	public static function create($uri): UriInterface
155
+	{
156
+		if ($uri instanceof UriInterface) {
157
+			return $uri;
158
+		}
159
+
160
+		if (!is_string($uri)) {
161
+			throw new InvalidArgumentException('URI should be a string');
162
+		}
163
+
164
+		return new self($uri);
165
+	}
166
+
167
+	/**
168
+	 * {@inheritdoc}
169
+	 *
170
+	 * @throws InvalidArgumentException
171
+	 *         If the scheme isn't valid.
172
+	 */
173
+	public function withScheme($scheme): UriInterface
174
+	{
175
+		$clone = clone $this;
176
+		$clone->setScheme($scheme);
177
+
178
+		return $clone;
179
+	}
180
+
181
+	/**
182
+	 * {@inheritdoc}
183
+	 *
184
+	 * @throws InvalidArgumentException
185
+	 *         If the user information isn't valid.
186
+	 */
187
+	public function withUserInfo($user, $password = null): UriInterface
188
+	{
189
+		$clone = clone $this;
190
+		$clone->setUserInfo($user, $password);
191
+
192
+		return $clone;
193
+	}
194
+
195
+	/**
196
+	 * {@inheritdoc}
197
+	 *
198
+	 * @throws InvalidArgumentException
199
+	 *         If the host isn't valid.
200
+	 */
201
+	public function withHost($host): UriInterface
202
+	{
203
+		$clone = clone $this;
204
+		$clone->setHost($host);
205
+
206
+		return $clone;
207
+	}
208
+
209
+	/**
210
+	 * {@inheritdoc}
211
+	 *
212
+	 * @throws InvalidArgumentException
213
+	 *         If the port isn't valid.
214
+	 */
215
+	public function withPort($port): UriInterface
216
+	{
217
+		$clone = clone $this;
218
+		$clone->setPort($port);
219
+
220
+		return $clone;
221
+	}
222
+
223
+	/**
224
+	 * {@inheritdoc}
225
+	 *
226
+	 * @throws InvalidArgumentException
227
+	 *         If the path isn't valid.
228
+	 */
229
+	public function withPath($path): UriInterface
230
+	{
231
+		$clone = clone $this;
232
+		$clone->setPath($path);
233
+
234
+		return $clone;
235
+	}
236
+
237
+	/**
238
+	 * {@inheritdoc}
239
+	 *
240
+	 * @throws InvalidArgumentException
241
+	 *         If the query isn't valid.
242
+	 */
243
+	public function withQuery($query): UriInterface
244
+	{
245
+		$clone = clone $this;
246
+		$clone->setQuery($query);
247
+
248
+		return $clone;
249
+	}
250
+
251
+	/**
252
+	 * {@inheritdoc}
253
+	 *
254
+	 * @throws InvalidArgumentException
255
+	 *         If the fragment isn't valid.
256
+	 */
257
+	public function withFragment($fragment): UriInterface
258
+	{
259
+		$clone = clone $this;
260
+		$clone->setFragment($fragment);
261
+
262
+		return $clone;
263
+	}
264
+
265
+	/**
266
+	 * {@inheritdoc}
267
+	 */
268
+	public function getScheme(): string
269
+	{
270
+		return $this->scheme;
271
+	}
272
+
273
+	/**
274
+	 * {@inheritdoc}
275
+	 */
276
+	public function getUserInfo(): string
277
+	{
278
+		return $this->userInfo;
279
+	}
280
+
281
+	/**
282
+	 * {@inheritdoc}
283
+	 */
284
+	public function getHost(): string
285
+	{
286
+		return $this->host;
287
+	}
288
+
289
+	/**
290
+	 * {@inheritdoc}
291
+	 */
292
+	public function getPort(): ?int
293
+	{
294
+		// The 80 is the default port number for the HTTP protocol.
295
+		if ($this->port === 80 && $this->scheme === 'http') {
296
+			return null;
297
+		}
298
+
299
+		// The 443 is the default port number for the HTTPS protocol.
300
+		if ($this->port === 443 && $this->scheme === 'https') {
301
+			return null;
302
+		}
303
+
304
+		return $this->port;
305
+	}
306
+
307
+	/**
308
+	 * {@inheritdoc}
309
+	 */
310
+	public function getPath(): string
311
+	{
312
+		// CVE-2015-3257
313
+		if (strncmp($this->path, '//', 2) === 0) {
314
+			return '/' . ltrim($this->path, '/');
315
+		}
316
+
317
+		return $this->path;
318
+	}
319
+
320
+	/**
321
+	 * {@inheritdoc}
322
+	 */
323
+	public function getQuery(): string
324
+	{
325
+		return $this->query;
326
+	}
327
+
328
+	/**
329
+	 * {@inheritdoc}
330
+	 */
331
+	public function getFragment(): string
332
+	{
333
+		return $this->fragment;
334
+	}
335
+
336
+	/**
337
+	 * {@inheritdoc}
338
+	 */
339
+	public function getAuthority(): string
340
+	{
341
+		// The host is the basic subcomponent.
342
+		if ($this->host === '') {
343
+			return '';
344
+		}
345
+
346
+		$authority = $this->host;
347
+		if ($this->userInfo !== '') {
348
+			$authority = $this->userInfo . '@' . $authority;
349
+		}
350
+
351
+		$port = $this->getPort();
352
+		if ($port !== null) {
353
+			$authority = $authority . ':' . $port;
354
+		}
355
+
356
+		return $authority;
357
+	}
358
+
359
+	/**
360
+	 * {@inheritdoc}
361
+	 */
362
+	public function __toString(): string
363
+	{
364
+		$uri = '';
365
+
366
+		$scheme = $this->scheme;
367
+		if ($scheme !== '') {
368
+			$uri .= $scheme . ':';
369
+		}
370
+
371
+		$authority = $this->getAuthority();
372
+		if ($authority !== '') {
373
+			$uri .= '//' . $authority;
374
+		}
375
+
376
+		$path = $this->path;
377
+		if ($path !== '') {
378
+			// https://github.com/sunrise-php/uri/issues/31
379
+			// https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
380
+			//
381
+			// If a URI contains an authority component,
382
+			// then the path component must either be empty
383
+			// or begin with a slash ("/") character.
384
+			if ($authority !== '' && strncmp($path, '/', 1) !== 0) {
385
+				$path = '/' . $path;
386
+			}
387
+
388
+			// https://github.com/sunrise-php/uri/issues/31
389
+			// https://datatracker.ietf.org/doc/html/rfc3986#section-3.3
390
+			//
391
+			// If a URI does not contain an authority component,
392
+			// then the path cannot begin with two slash characters ("//").
393
+			if ($authority === '' && strncmp($path, '//', 2) === 0) {
394
+				$path = '/' . ltrim($path, '/');
395
+			}
396
+
397
+			$uri .= $path;
398
+		}
399
+
400
+		$query = $this->query;
401
+		if ($query !== '') {
402
+			$uri .= '?' . $query;
403
+		}
404
+
405
+		$fragment = $this->fragment;
406
+		if ($fragment !== '') {
407
+			$uri .= '#' . $fragment;
408
+		}
409
+
410
+		return $uri;
411
+	}
412
+
413
+	/**
414
+	 * Sets the given scheme to the URI
415
+	 *
416
+	 * @param mixed $scheme
417
+	 *
418
+	 * @return void
419
+	 *
420
+	 * @throws InvalidArgumentException
421
+	 *         If the scheme isn't valid.
422
+	 */
423
+	final protected function setScheme($scheme): void
424
+	{
425
+		$this->scheme = (new Scheme($scheme))->getValue();
426
+	}
427
+
428
+	/**
429
+	 * Sets the given user information to the URI
430
+	 *
431
+	 * @param mixed $user
432
+	 * @param mixed $password
433
+	 *
434
+	 * @return void
435
+	 *
436
+	 * @throws InvalidArgumentException
437
+	 *         If the user information isn't valid.
438
+	 */
439
+	final protected function setUserInfo($user, $password): void
440
+	{
441
+		$this->userInfo = (new UserInfo($user, $password))->getValue();
442
+	}
443
+
444
+	/**
445
+	 * Sets the given host to the URI
446
+	 *
447
+	 * @param mixed $host
448
+	 *
449
+	 * @return void
450
+	 *
451
+	 * @throws InvalidArgumentException
452
+	 *         If the host isn't valid.
453
+	 */
454
+	final protected function setHost($host): void
455
+	{
456
+		$this->host = (new Host($host))->getValue();
457
+	}
458
+
459
+	/**
460
+	 * Sets the given port to the URI
461
+	 *
462
+	 * @param mixed $port
463
+	 *
464
+	 * @return void
465
+	 *
466
+	 * @throws InvalidArgumentException
467
+	 *         If the port isn't valid.
468
+	 */
469
+	final protected function setPort($port): void
470
+	{
471
+		$this->port = (new Port($port))->getValue();
472
+	}
473
+
474
+	/**
475
+	 * Sets the given path to the URI
476
+	 *
477
+	 * @param mixed $path
478
+	 *
479
+	 * @return void
480
+	 *
481
+	 * @throws InvalidArgumentException
482
+	 *         If the path isn't valid.
483
+	 */
484
+	final protected function setPath($path): void
485
+	{
486
+		$this->path = (new Path($path))->getValue();
487
+	}
488
+
489
+	/**
490
+	 * Sets the given query to the URI
491
+	 *
492
+	 * @param mixed $query
493
+	 *
494
+	 * @return void
495
+	 *
496
+	 * @throws InvalidArgumentException
497
+	 *         If the query isn't valid.
498
+	 */
499
+	final protected function setQuery($query): void
500
+	{
501
+		$this->query = (new Query($query))->getValue();
502
+	}
503
+
504
+	/**
505
+	 * Sets the given fragment to the URI
506
+	 *
507
+	 * @param mixed $fragment
508
+	 *
509
+	 * @return void
510
+	 *
511
+	 * @throws InvalidArgumentException
512
+	 *         If the fragment isn't valid.
513
+	 */
514
+	final protected function setFragment($fragment): void
515
+	{
516
+		$this->fragment = (new Fragment($fragment))->getValue();
517
+	}
518 518
 }
Please login to merge, or discard this patch.
src/Stream.php 1 patch
Indentation   +384 added lines, -384 removed lines patch added patch discarded remove patch
@@ -47,388 +47,388 @@
 block discarded – undo
47 47
 class Stream implements StreamInterface
48 48
 {
49 49
 
50
-    /**
51
-     * The stream resource
52
-     *
53
-     * @var resource|null
54
-     */
55
-    private $resource;
56
-
57
-    /**
58
-     * Signals to close the stream on destruction
59
-     *
60
-     * @var bool
61
-     */
62
-    private bool $autoClose;
63
-
64
-    /**
65
-     * Constructor of the class
66
-     *
67
-     * @param mixed $resource
68
-     * @param bool $autoClose
69
-     *
70
-     * @throws InvalidArgumentException
71
-     *         If the stream cannot be initialized with the resource.
72
-     */
73
-    public function __construct($resource, bool $autoClose = true)
74
-    {
75
-        if (!is_resource($resource)) {
76
-            throw new InvalidArgumentException('Unexpected stream resource');
77
-        }
78
-
79
-        $this->resource = $resource;
80
-        $this->autoClose = $autoClose;
81
-    }
82
-
83
-    /**
84
-     * Creates a stream
85
-     *
86
-     * @param mixed $resource
87
-     *
88
-     * @return StreamInterface
89
-     *
90
-     * @throws InvalidArgumentException
91
-     *         If the stream cannot be initialized with the resource.
92
-     */
93
-    public static function create($resource): StreamInterface
94
-    {
95
-        if ($resource instanceof StreamInterface) {
96
-            return $resource;
97
-        }
98
-
99
-        return new self($resource);
100
-    }
101
-
102
-    /**
103
-     * Destructor of the class
104
-     */
105
-    public function __destruct()
106
-    {
107
-        if ($this->autoClose) {
108
-            $this->close();
109
-        }
110
-    }
111
-
112
-    /**
113
-     * Detaches a resource from the stream
114
-     *
115
-     * Returns NULL if the stream already without a resource.
116
-     *
117
-     * @return resource|null
118
-     */
119
-    public function detach()
120
-    {
121
-        $resource = $this->resource;
122
-        $this->resource = null;
123
-
124
-        return $resource;
125
-    }
126
-
127
-    /**
128
-     * Closes the stream
129
-     *
130
-     * @link http://php.net/manual/en/function.fclose.php
131
-     *
132
-     * @return void
133
-     */
134
-    public function close(): void
135
-    {
136
-        $resource = $this->detach();
137
-        if (!is_resource($resource)) {
138
-            return;
139
-        }
140
-
141
-        fclose($resource);
142
-    }
143
-
144
-    /**
145
-     * Checks if the end of the stream is reached
146
-     *
147
-     * @link http://php.net/manual/en/function.feof.php
148
-     *
149
-     * @return bool
150
-     */
151
-    public function eof(): bool
152
-    {
153
-        if (!is_resource($this->resource)) {
154
-            return true;
155
-        }
156
-
157
-        return feof($this->resource);
158
-    }
159
-
160
-    /**
161
-     * Gets the stream pointer position
162
-     *
163
-     * @link http://php.net/manual/en/function.ftell.php
164
-     *
165
-     * @return int
166
-     *
167
-     * @throws RuntimeException
168
-     */
169
-    public function tell(): int
170
-    {
171
-        if (!is_resource($this->resource)) {
172
-            throw new RuntimeException('Stream has no resource');
173
-        }
174
-
175
-        $result = ftell($this->resource);
176
-        if ($result === false) {
177
-            throw new RuntimeException('Unable to get the stream pointer position');
178
-        }
179
-
180
-        return $result;
181
-    }
182
-
183
-    /**
184
-     * Checks if the stream is seekable
185
-     *
186
-     * @return bool
187
-     */
188
-    public function isSeekable(): bool
189
-    {
190
-        if (!is_resource($this->resource)) {
191
-            return false;
192
-        }
193
-
194
-        /** @var array{seekable: bool} */
195
-        $metadata = stream_get_meta_data($this->resource);
196
-
197
-        return $metadata['seekable'];
198
-    }
199
-
200
-    /**
201
-     * Moves the stream pointer to the beginning
202
-     *
203
-     * @return void
204
-     *
205
-     * @throws RuntimeException
206
-     */
207
-    public function rewind(): void
208
-    {
209
-        $this->seek(0);
210
-    }
211
-
212
-    /**
213
-     * Moves the stream pointer to the given position
214
-     *
215
-     * @link http://php.net/manual/en/function.fseek.php
216
-     *
217
-     * @param int $offset
218
-     * @param int $whence
219
-     *
220
-     * @return void
221
-     *
222
-     * @throws RuntimeException
223
-     */
224
-    public function seek($offset, $whence = SEEK_SET): void
225
-    {
226
-        if (!is_resource($this->resource)) {
227
-            throw new RuntimeException('Stream has no resource');
228
-        }
229
-
230
-        if (!$this->isSeekable()) {
231
-            throw new RuntimeException('Stream is not seekable');
232
-        }
233
-
234
-        $result = fseek($this->resource, $offset, $whence);
235
-        if ($result !== 0) {
236
-            throw new RuntimeException('Unable to move the stream pointer position');
237
-        }
238
-    }
239
-
240
-    /**
241
-     * Checks if the stream is writable
242
-     *
243
-     * @return bool
244
-     */
245
-    public function isWritable(): bool
246
-    {
247
-        if (!is_resource($this->resource)) {
248
-            return false;
249
-        }
250
-
251
-        /** @var array{mode: string} */
252
-        $metadata = stream_get_meta_data($this->resource);
253
-
254
-        return strpbrk($metadata['mode'], '+acwx') !== false;
255
-    }
256
-
257
-    /**
258
-     * Writes the given string to the stream
259
-     *
260
-     * Returns the number of bytes written to the stream.
261
-     *
262
-     * @link http://php.net/manual/en/function.fwrite.php
263
-     *
264
-     * @param string $string
265
-     *
266
-     * @return int
267
-     *
268
-     * @throws RuntimeException
269
-     */
270
-    public function write($string): int
271
-    {
272
-        if (!is_resource($this->resource)) {
273
-            throw new RuntimeException('Stream has no resource');
274
-        }
275
-
276
-        if (!$this->isWritable()) {
277
-            throw new RuntimeException('Stream is not writable');
278
-        }
279
-
280
-        $result = fwrite($this->resource, $string);
281
-        if ($result === false) {
282
-            throw new RuntimeException('Unable to write to the stream');
283
-        }
284
-
285
-        return $result;
286
-    }
287
-
288
-    /**
289
-     * Checks if the stream is readable
290
-     *
291
-     * @return bool
292
-     */
293
-    public function isReadable(): bool
294
-    {
295
-        if (!is_resource($this->resource)) {
296
-            return false;
297
-        }
298
-
299
-        /** @var array{mode: string} */
300
-        $metadata = stream_get_meta_data($this->resource);
301
-
302
-        return strpbrk($metadata['mode'], '+r') !== false;
303
-    }
304
-
305
-    /**
306
-     * Reads the given number of bytes from the stream
307
-     *
308
-     * @link http://php.net/manual/en/function.fread.php
309
-     *
310
-     * @param int $length
311
-     * @psalm-param int $length
312
-     * @phpstan-param int<0, max> $length
313
-     *
314
-     * @return string
315
-     *
316
-     * @throws RuntimeException
317
-     */
318
-    public function read($length): string
319
-    {
320
-        if (!is_resource($this->resource)) {
321
-            throw new RuntimeException('Stream has no resource');
322
-        }
323
-
324
-        if (!$this->isReadable()) {
325
-            throw new RuntimeException('Stream is not readable');
326
-        }
327
-
328
-        $result = fread($this->resource, $length);
329
-        if ($result === false) {
330
-            throw new RuntimeException('Unable to read from the stream');
331
-        }
332
-
333
-        return $result;
334
-    }
335
-
336
-    /**
337
-     * Reads the remainder of the stream
338
-     *
339
-     * @link http://php.net/manual/en/function.stream-get-contents.php
340
-     *
341
-     * @return string
342
-     *
343
-     * @throws RuntimeException
344
-     */
345
-    public function getContents(): string
346
-    {
347
-        if (!is_resource($this->resource)) {
348
-            throw new RuntimeException('Stream has no resource');
349
-        }
350
-
351
-        if (!$this->isReadable()) {
352
-            throw new RuntimeException('Stream is not readable');
353
-        }
354
-
355
-        $result = stream_get_contents($this->resource);
356
-        if ($result === false) {
357
-            throw new RuntimeException('Unable to read the remainder of the stream');
358
-        }
359
-
360
-        return $result;
361
-    }
362
-
363
-    /**
364
-     * Gets the stream metadata
365
-     *
366
-     * @link http://php.net/manual/en/function.stream-get-meta-data.php
367
-     *
368
-     * @param string|null $key
369
-     *
370
-     * @return mixed
371
-     */
372
-    public function getMetadata($key = null)
373
-    {
374
-        if (!is_resource($this->resource)) {
375
-            return null;
376
-        }
377
-
378
-        $metadata = stream_get_meta_data($this->resource);
379
-        if ($key === null) {
380
-            return $metadata;
381
-        }
382
-
383
-        return $metadata[$key] ?? null;
384
-    }
385
-
386
-    /**
387
-     * Gets the stream size
388
-     *
389
-     * Returns NULL if the stream doesn't have a resource,
390
-     * or if the stream size cannot be determined.
391
-     *
392
-     * @link http://php.net/manual/en/function.fstat.php
393
-     *
394
-     * @return int|null
395
-     */
396
-    public function getSize(): ?int
397
-    {
398
-        if (!is_resource($this->resource)) {
399
-            return null;
400
-        }
401
-
402
-        /** @var array{size: int}|false */
403
-        $stats = fstat($this->resource);
404
-        if ($stats === false) {
405
-            return null;
406
-        }
407
-
408
-        return $stats['size'];
409
-    }
410
-
411
-    /**
412
-     * Converts the stream to a string
413
-     *
414
-     * @link http://php.net/manual/en/language.oop5.magic.php#object.tostring
415
-     *
416
-     * @return string
417
-     */
418
-    public function __toString(): string
419
-    {
420
-        if (!$this->isReadable()) {
421
-            return '';
422
-        }
423
-
424
-        try {
425
-            if ($this->isSeekable()) {
426
-                $this->rewind();
427
-            }
428
-
429
-            return $this->getContents();
430
-        } catch (Throwable $e) {
431
-            return '';
432
-        }
433
-    }
50
+	/**
51
+	 * The stream resource
52
+	 *
53
+	 * @var resource|null
54
+	 */
55
+	private $resource;
56
+
57
+	/**
58
+	 * Signals to close the stream on destruction
59
+	 *
60
+	 * @var bool
61
+	 */
62
+	private bool $autoClose;
63
+
64
+	/**
65
+	 * Constructor of the class
66
+	 *
67
+	 * @param mixed $resource
68
+	 * @param bool $autoClose
69
+	 *
70
+	 * @throws InvalidArgumentException
71
+	 *         If the stream cannot be initialized with the resource.
72
+	 */
73
+	public function __construct($resource, bool $autoClose = true)
74
+	{
75
+		if (!is_resource($resource)) {
76
+			throw new InvalidArgumentException('Unexpected stream resource');
77
+		}
78
+
79
+		$this->resource = $resource;
80
+		$this->autoClose = $autoClose;
81
+	}
82
+
83
+	/**
84
+	 * Creates a stream
85
+	 *
86
+	 * @param mixed $resource
87
+	 *
88
+	 * @return StreamInterface
89
+	 *
90
+	 * @throws InvalidArgumentException
91
+	 *         If the stream cannot be initialized with the resource.
92
+	 */
93
+	public static function create($resource): StreamInterface
94
+	{
95
+		if ($resource instanceof StreamInterface) {
96
+			return $resource;
97
+		}
98
+
99
+		return new self($resource);
100
+	}
101
+
102
+	/**
103
+	 * Destructor of the class
104
+	 */
105
+	public function __destruct()
106
+	{
107
+		if ($this->autoClose) {
108
+			$this->close();
109
+		}
110
+	}
111
+
112
+	/**
113
+	 * Detaches a resource from the stream
114
+	 *
115
+	 * Returns NULL if the stream already without a resource.
116
+	 *
117
+	 * @return resource|null
118
+	 */
119
+	public function detach()
120
+	{
121
+		$resource = $this->resource;
122
+		$this->resource = null;
123
+
124
+		return $resource;
125
+	}
126
+
127
+	/**
128
+	 * Closes the stream
129
+	 *
130
+	 * @link http://php.net/manual/en/function.fclose.php
131
+	 *
132
+	 * @return void
133
+	 */
134
+	public function close(): void
135
+	{
136
+		$resource = $this->detach();
137
+		if (!is_resource($resource)) {
138
+			return;
139
+		}
140
+
141
+		fclose($resource);
142
+	}
143
+
144
+	/**
145
+	 * Checks if the end of the stream is reached
146
+	 *
147
+	 * @link http://php.net/manual/en/function.feof.php
148
+	 *
149
+	 * @return bool
150
+	 */
151
+	public function eof(): bool
152
+	{
153
+		if (!is_resource($this->resource)) {
154
+			return true;
155
+		}
156
+
157
+		return feof($this->resource);
158
+	}
159
+
160
+	/**
161
+	 * Gets the stream pointer position
162
+	 *
163
+	 * @link http://php.net/manual/en/function.ftell.php
164
+	 *
165
+	 * @return int
166
+	 *
167
+	 * @throws RuntimeException
168
+	 */
169
+	public function tell(): int
170
+	{
171
+		if (!is_resource($this->resource)) {
172
+			throw new RuntimeException('Stream has no resource');
173
+		}
174
+
175
+		$result = ftell($this->resource);
176
+		if ($result === false) {
177
+			throw new RuntimeException('Unable to get the stream pointer position');
178
+		}
179
+
180
+		return $result;
181
+	}
182
+
183
+	/**
184
+	 * Checks if the stream is seekable
185
+	 *
186
+	 * @return bool
187
+	 */
188
+	public function isSeekable(): bool
189
+	{
190
+		if (!is_resource($this->resource)) {
191
+			return false;
192
+		}
193
+
194
+		/** @var array{seekable: bool} */
195
+		$metadata = stream_get_meta_data($this->resource);
196
+
197
+		return $metadata['seekable'];
198
+	}
199
+
200
+	/**
201
+	 * Moves the stream pointer to the beginning
202
+	 *
203
+	 * @return void
204
+	 *
205
+	 * @throws RuntimeException
206
+	 */
207
+	public function rewind(): void
208
+	{
209
+		$this->seek(0);
210
+	}
211
+
212
+	/**
213
+	 * Moves the stream pointer to the given position
214
+	 *
215
+	 * @link http://php.net/manual/en/function.fseek.php
216
+	 *
217
+	 * @param int $offset
218
+	 * @param int $whence
219
+	 *
220
+	 * @return void
221
+	 *
222
+	 * @throws RuntimeException
223
+	 */
224
+	public function seek($offset, $whence = SEEK_SET): void
225
+	{
226
+		if (!is_resource($this->resource)) {
227
+			throw new RuntimeException('Stream has no resource');
228
+		}
229
+
230
+		if (!$this->isSeekable()) {
231
+			throw new RuntimeException('Stream is not seekable');
232
+		}
233
+
234
+		$result = fseek($this->resource, $offset, $whence);
235
+		if ($result !== 0) {
236
+			throw new RuntimeException('Unable to move the stream pointer position');
237
+		}
238
+	}
239
+
240
+	/**
241
+	 * Checks if the stream is writable
242
+	 *
243
+	 * @return bool
244
+	 */
245
+	public function isWritable(): bool
246
+	{
247
+		if (!is_resource($this->resource)) {
248
+			return false;
249
+		}
250
+
251
+		/** @var array{mode: string} */
252
+		$metadata = stream_get_meta_data($this->resource);
253
+
254
+		return strpbrk($metadata['mode'], '+acwx') !== false;
255
+	}
256
+
257
+	/**
258
+	 * Writes the given string to the stream
259
+	 *
260
+	 * Returns the number of bytes written to the stream.
261
+	 *
262
+	 * @link http://php.net/manual/en/function.fwrite.php
263
+	 *
264
+	 * @param string $string
265
+	 *
266
+	 * @return int
267
+	 *
268
+	 * @throws RuntimeException
269
+	 */
270
+	public function write($string): int
271
+	{
272
+		if (!is_resource($this->resource)) {
273
+			throw new RuntimeException('Stream has no resource');
274
+		}
275
+
276
+		if (!$this->isWritable()) {
277
+			throw new RuntimeException('Stream is not writable');
278
+		}
279
+
280
+		$result = fwrite($this->resource, $string);
281
+		if ($result === false) {
282
+			throw new RuntimeException('Unable to write to the stream');
283
+		}
284
+
285
+		return $result;
286
+	}
287
+
288
+	/**
289
+	 * Checks if the stream is readable
290
+	 *
291
+	 * @return bool
292
+	 */
293
+	public function isReadable(): bool
294
+	{
295
+		if (!is_resource($this->resource)) {
296
+			return false;
297
+		}
298
+
299
+		/** @var array{mode: string} */
300
+		$metadata = stream_get_meta_data($this->resource);
301
+
302
+		return strpbrk($metadata['mode'], '+r') !== false;
303
+	}
304
+
305
+	/**
306
+	 * Reads the given number of bytes from the stream
307
+	 *
308
+	 * @link http://php.net/manual/en/function.fread.php
309
+	 *
310
+	 * @param int $length
311
+	 * @psalm-param int $length
312
+	 * @phpstan-param int<0, max> $length
313
+	 *
314
+	 * @return string
315
+	 *
316
+	 * @throws RuntimeException
317
+	 */
318
+	public function read($length): string
319
+	{
320
+		if (!is_resource($this->resource)) {
321
+			throw new RuntimeException('Stream has no resource');
322
+		}
323
+
324
+		if (!$this->isReadable()) {
325
+			throw new RuntimeException('Stream is not readable');
326
+		}
327
+
328
+		$result = fread($this->resource, $length);
329
+		if ($result === false) {
330
+			throw new RuntimeException('Unable to read from the stream');
331
+		}
332
+
333
+		return $result;
334
+	}
335
+
336
+	/**
337
+	 * Reads the remainder of the stream
338
+	 *
339
+	 * @link http://php.net/manual/en/function.stream-get-contents.php
340
+	 *
341
+	 * @return string
342
+	 *
343
+	 * @throws RuntimeException
344
+	 */
345
+	public function getContents(): string
346
+	{
347
+		if (!is_resource($this->resource)) {
348
+			throw new RuntimeException('Stream has no resource');
349
+		}
350
+
351
+		if (!$this->isReadable()) {
352
+			throw new RuntimeException('Stream is not readable');
353
+		}
354
+
355
+		$result = stream_get_contents($this->resource);
356
+		if ($result === false) {
357
+			throw new RuntimeException('Unable to read the remainder of the stream');
358
+		}
359
+
360
+		return $result;
361
+	}
362
+
363
+	/**
364
+	 * Gets the stream metadata
365
+	 *
366
+	 * @link http://php.net/manual/en/function.stream-get-meta-data.php
367
+	 *
368
+	 * @param string|null $key
369
+	 *
370
+	 * @return mixed
371
+	 */
372
+	public function getMetadata($key = null)
373
+	{
374
+		if (!is_resource($this->resource)) {
375
+			return null;
376
+		}
377
+
378
+		$metadata = stream_get_meta_data($this->resource);
379
+		if ($key === null) {
380
+			return $metadata;
381
+		}
382
+
383
+		return $metadata[$key] ?? null;
384
+	}
385
+
386
+	/**
387
+	 * Gets the stream size
388
+	 *
389
+	 * Returns NULL if the stream doesn't have a resource,
390
+	 * or if the stream size cannot be determined.
391
+	 *
392
+	 * @link http://php.net/manual/en/function.fstat.php
393
+	 *
394
+	 * @return int|null
395
+	 */
396
+	public function getSize(): ?int
397
+	{
398
+		if (!is_resource($this->resource)) {
399
+			return null;
400
+		}
401
+
402
+		/** @var array{size: int}|false */
403
+		$stats = fstat($this->resource);
404
+		if ($stats === false) {
405
+			return null;
406
+		}
407
+
408
+		return $stats['size'];
409
+	}
410
+
411
+	/**
412
+	 * Converts the stream to a string
413
+	 *
414
+	 * @link http://php.net/manual/en/language.oop5.magic.php#object.tostring
415
+	 *
416
+	 * @return string
417
+	 */
418
+	public function __toString(): string
419
+	{
420
+		if (!$this->isReadable()) {
421
+			return '';
422
+		}
423
+
424
+		try {
425
+			if ($this->isSeekable()) {
426
+				$this->rewind();
427
+			}
428
+
429
+			return $this->getContents();
430
+		} catch (Throwable $e) {
431
+			return '';
432
+		}
433
+	}
434 434
 }
Please login to merge, or discard this patch.
src/UploadedFile.php 1 patch
Indentation   +226 added lines, -226 removed lines patch added patch discarded remove patch
@@ -50,230 +50,230 @@
 block discarded – undo
50 50
 class UploadedFile implements UploadedFileInterface
51 51
 {
52 52
 
53
-    /**
54
-     * List of upload errors
55
-     *
56
-     * @link https://www.php.net/manual/en/features.file-upload.errors.php
57
-     *
58
-     * @var array<int, non-empty-string>
59
-     */
60
-    public const UPLOAD_ERRORS = [
61
-        UPLOAD_ERR_OK         => 'No error',
62
-        UPLOAD_ERR_INI_SIZE   => 'Uploaded file exceeds the upload_max_filesize directive in the php.ini',
63
-        UPLOAD_ERR_FORM_SIZE  => 'Uploaded file exceeds the MAX_FILE_SIZE directive in the HTML form',
64
-        UPLOAD_ERR_PARTIAL    => 'Uploaded file was only partially uploaded',
65
-        UPLOAD_ERR_NO_FILE    => 'No file was uploaded',
66
-        UPLOAD_ERR_NO_TMP_DIR => 'Missing temporary directory',
67
-        UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk',
68
-        UPLOAD_ERR_EXTENSION  => 'File upload was stopped by a PHP extension',
69
-    ];
70
-
71
-    /**
72
-     * The file stream
73
-     *
74
-     * @var StreamInterface|null
75
-     */
76
-    private ?StreamInterface $stream = null;
77
-
78
-    /**
79
-     * The file size
80
-     *
81
-     * @var int|null
82
-     */
83
-    private ?int $size;
84
-
85
-    /**
86
-     * The file's error code
87
-     *
88
-     * @var int
89
-     */
90
-    private int $errorCode;
91
-
92
-    /**
93
-     * The file's error message
94
-     *
95
-     * @var string
96
-     */
97
-    private string $errorMessage;
98
-
99
-    /**
100
-     * The client's file name
101
-     *
102
-     * @var string|null
103
-     */
104
-    private ?string $clientFilename;
105
-
106
-    /**
107
-     * The client's file media type
108
-     *
109
-     * @var string|null
110
-     */
111
-    private ?string $clientMediaType;
112
-
113
-    /**
114
-     * Constructor of the class
115
-     *
116
-     * @param StreamInterface|null $stream
117
-     * @param int|null $size
118
-     * @param int $error
119
-     * @param string|null $clientFilename
120
-     * @param string|null $clientMediaType
121
-     */
122
-    public function __construct(
123
-        ?StreamInterface $stream,
124
-        ?int $size = null,
125
-        int $error = UPLOAD_ERR_OK,
126
-        ?string $clientFilename = null,
127
-        ?string $clientMediaType = null
128
-    ) {
129
-        // It doesn't make sense to keep the stream
130
-        // if the file wasn't successfully uploaded...
131
-        if (UPLOAD_ERR_OK === $error) {
132
-            $this->stream = $stream;
133
-        }
134
-
135
-        $this->size = $size;
136
-        $this->errorCode = $error;
137
-        $this->errorMessage = self::UPLOAD_ERRORS[$error] ?? 'Unknown error';
138
-        $this->clientFilename = $clientFilename;
139
-        $this->clientMediaType = $clientMediaType;
140
-    }
141
-
142
-    /**
143
-     * Gets the file stream
144
-     *
145
-     * @return StreamInterface
146
-     *
147
-     * @throws RuntimeException
148
-     *         - If the file has no a stream due to an error;
149
-     *         - If the file was already moved.
150
-     */
151
-    public function getStream(): StreamInterface
152
-    {
153
-        if (UPLOAD_ERR_OK <> $this->errorCode) {
154
-            throw new RuntimeException(sprintf(
155
-                'Uploaded file has no a stream due to the error #%d (%s)',
156
-                $this->errorCode,
157
-                $this->errorMessage
158
-            ));
159
-        }
160
-
161
-        if (!isset($this->stream)) {
162
-            throw new RuntimeException(
163
-                'Uploaded file has no a stream because it was already moved'
164
-            );
165
-        }
166
-
167
-        return $this->stream;
168
-    }
169
-
170
-    /**
171
-     * Moves the file to the given path
172
-     *
173
-     * @param string $targetPath
174
-     *
175
-     * @return void
176
-     *
177
-     * @throws InvalidArgumentException
178
-     *         If the target path cannot be used.
179
-     *
180
-     * @throws RuntimeException
181
-     *         - If the file has no a stream due to an error;
182
-     *         - If the file was already moved;
183
-     *         - If the file cannot be read.
184
-     */
185
-    public function moveTo($targetPath): void
186
-    {
187
-        if (UPLOAD_ERR_OK <> $this->errorCode) {
188
-            throw new RuntimeException(sprintf(
189
-                'Uploaded file cannot be moved due to the error #%d (%s)',
190
-                $this->errorCode,
191
-                $this->errorMessage
192
-            ));
193
-        }
194
-
195
-        if (!isset($this->stream)) {
196
-            throw new RuntimeException(
197
-                'Uploaded file cannot be moved because it was already moved'
198
-            );
199
-        }
200
-
201
-        if (!$this->stream->isReadable()) {
202
-            throw new RuntimeException(
203
-                'Uploaded file cannot be moved because it is not readable'
204
-            );
205
-        }
206
-
207
-        try {
208
-            $targetStream = new FileStream($targetPath, 'wb');
209
-        } catch (InvalidArgumentException $e) {
210
-            throw new InvalidArgumentException(sprintf(
211
-                'Uploaded file cannot be moved due to the error: %s',
212
-                $e->getMessage()
213
-            ));
214
-        }
215
-
216
-        if ($this->stream->isSeekable()) {
217
-            $this->stream->rewind();
218
-        }
219
-
220
-        while (!$this->stream->eof()) {
221
-            $targetStream->write($this->stream->read(4096));
222
-        }
223
-
224
-        $targetStream->close();
225
-
226
-        /** @var string|null */
227
-        $sourcePath = $this->stream->getMetadata('uri');
228
-
229
-        $this->stream->close();
230
-        $this->stream = null;
231
-
232
-        if (isset($sourcePath) && is_file($sourcePath)) {
233
-            $sourceDir = dirname($sourcePath);
234
-            if (is_writable($sourceDir)) {
235
-                unlink($sourcePath);
236
-            }
237
-        }
238
-    }
239
-
240
-    /**
241
-     * Gets the file size
242
-     *
243
-     * @return int|null
244
-     */
245
-    public function getSize(): ?int
246
-    {
247
-        return $this->size;
248
-    }
249
-
250
-    /**
251
-     * Gets the file's error code
252
-     *
253
-     * @return int
254
-     */
255
-    public function getError(): int
256
-    {
257
-        return $this->errorCode;
258
-    }
259
-
260
-    /**
261
-     * Gets the client's file name
262
-     *
263
-     * @return string|null
264
-     */
265
-    public function getClientFilename(): ?string
266
-    {
267
-        return $this->clientFilename;
268
-    }
269
-
270
-    /**
271
-     * Gets the client's file media type
272
-     *
273
-     * @return string|null
274
-     */
275
-    public function getClientMediaType(): ?string
276
-    {
277
-        return $this->clientMediaType;
278
-    }
53
+	/**
54
+	 * List of upload errors
55
+	 *
56
+	 * @link https://www.php.net/manual/en/features.file-upload.errors.php
57
+	 *
58
+	 * @var array<int, non-empty-string>
59
+	 */
60
+	public const UPLOAD_ERRORS = [
61
+		UPLOAD_ERR_OK         => 'No error',
62
+		UPLOAD_ERR_INI_SIZE   => 'Uploaded file exceeds the upload_max_filesize directive in the php.ini',
63
+		UPLOAD_ERR_FORM_SIZE  => 'Uploaded file exceeds the MAX_FILE_SIZE directive in the HTML form',
64
+		UPLOAD_ERR_PARTIAL    => 'Uploaded file was only partially uploaded',
65
+		UPLOAD_ERR_NO_FILE    => 'No file was uploaded',
66
+		UPLOAD_ERR_NO_TMP_DIR => 'Missing temporary directory',
67
+		UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk',
68
+		UPLOAD_ERR_EXTENSION  => 'File upload was stopped by a PHP extension',
69
+	];
70
+
71
+	/**
72
+	 * The file stream
73
+	 *
74
+	 * @var StreamInterface|null
75
+	 */
76
+	private ?StreamInterface $stream = null;
77
+
78
+	/**
79
+	 * The file size
80
+	 *
81
+	 * @var int|null
82
+	 */
83
+	private ?int $size;
84
+
85
+	/**
86
+	 * The file's error code
87
+	 *
88
+	 * @var int
89
+	 */
90
+	private int $errorCode;
91
+
92
+	/**
93
+	 * The file's error message
94
+	 *
95
+	 * @var string
96
+	 */
97
+	private string $errorMessage;
98
+
99
+	/**
100
+	 * The client's file name
101
+	 *
102
+	 * @var string|null
103
+	 */
104
+	private ?string $clientFilename;
105
+
106
+	/**
107
+	 * The client's file media type
108
+	 *
109
+	 * @var string|null
110
+	 */
111
+	private ?string $clientMediaType;
112
+
113
+	/**
114
+	 * Constructor of the class
115
+	 *
116
+	 * @param StreamInterface|null $stream
117
+	 * @param int|null $size
118
+	 * @param int $error
119
+	 * @param string|null $clientFilename
120
+	 * @param string|null $clientMediaType
121
+	 */
122
+	public function __construct(
123
+		?StreamInterface $stream,
124
+		?int $size = null,
125
+		int $error = UPLOAD_ERR_OK,
126
+		?string $clientFilename = null,
127
+		?string $clientMediaType = null
128
+	) {
129
+		// It doesn't make sense to keep the stream
130
+		// if the file wasn't successfully uploaded...
131
+		if (UPLOAD_ERR_OK === $error) {
132
+			$this->stream = $stream;
133
+		}
134
+
135
+		$this->size = $size;
136
+		$this->errorCode = $error;
137
+		$this->errorMessage = self::UPLOAD_ERRORS[$error] ?? 'Unknown error';
138
+		$this->clientFilename = $clientFilename;
139
+		$this->clientMediaType = $clientMediaType;
140
+	}
141
+
142
+	/**
143
+	 * Gets the file stream
144
+	 *
145
+	 * @return StreamInterface
146
+	 *
147
+	 * @throws RuntimeException
148
+	 *         - If the file has no a stream due to an error;
149
+	 *         - If the file was already moved.
150
+	 */
151
+	public function getStream(): StreamInterface
152
+	{
153
+		if (UPLOAD_ERR_OK <> $this->errorCode) {
154
+			throw new RuntimeException(sprintf(
155
+				'Uploaded file has no a stream due to the error #%d (%s)',
156
+				$this->errorCode,
157
+				$this->errorMessage
158
+			));
159
+		}
160
+
161
+		if (!isset($this->stream)) {
162
+			throw new RuntimeException(
163
+				'Uploaded file has no a stream because it was already moved'
164
+			);
165
+		}
166
+
167
+		return $this->stream;
168
+	}
169
+
170
+	/**
171
+	 * Moves the file to the given path
172
+	 *
173
+	 * @param string $targetPath
174
+	 *
175
+	 * @return void
176
+	 *
177
+	 * @throws InvalidArgumentException
178
+	 *         If the target path cannot be used.
179
+	 *
180
+	 * @throws RuntimeException
181
+	 *         - If the file has no a stream due to an error;
182
+	 *         - If the file was already moved;
183
+	 *         - If the file cannot be read.
184
+	 */
185
+	public function moveTo($targetPath): void
186
+	{
187
+		if (UPLOAD_ERR_OK <> $this->errorCode) {
188
+			throw new RuntimeException(sprintf(
189
+				'Uploaded file cannot be moved due to the error #%d (%s)',
190
+				$this->errorCode,
191
+				$this->errorMessage
192
+			));
193
+		}
194
+
195
+		if (!isset($this->stream)) {
196
+			throw new RuntimeException(
197
+				'Uploaded file cannot be moved because it was already moved'
198
+			);
199
+		}
200
+
201
+		if (!$this->stream->isReadable()) {
202
+			throw new RuntimeException(
203
+				'Uploaded file cannot be moved because it is not readable'
204
+			);
205
+		}
206
+
207
+		try {
208
+			$targetStream = new FileStream($targetPath, 'wb');
209
+		} catch (InvalidArgumentException $e) {
210
+			throw new InvalidArgumentException(sprintf(
211
+				'Uploaded file cannot be moved due to the error: %s',
212
+				$e->getMessage()
213
+			));
214
+		}
215
+
216
+		if ($this->stream->isSeekable()) {
217
+			$this->stream->rewind();
218
+		}
219
+
220
+		while (!$this->stream->eof()) {
221
+			$targetStream->write($this->stream->read(4096));
222
+		}
223
+
224
+		$targetStream->close();
225
+
226
+		/** @var string|null */
227
+		$sourcePath = $this->stream->getMetadata('uri');
228
+
229
+		$this->stream->close();
230
+		$this->stream = null;
231
+
232
+		if (isset($sourcePath) && is_file($sourcePath)) {
233
+			$sourceDir = dirname($sourcePath);
234
+			if (is_writable($sourceDir)) {
235
+				unlink($sourcePath);
236
+			}
237
+		}
238
+	}
239
+
240
+	/**
241
+	 * Gets the file size
242
+	 *
243
+	 * @return int|null
244
+	 */
245
+	public function getSize(): ?int
246
+	{
247
+		return $this->size;
248
+	}
249
+
250
+	/**
251
+	 * Gets the file's error code
252
+	 *
253
+	 * @return int
254
+	 */
255
+	public function getError(): int
256
+	{
257
+		return $this->errorCode;
258
+	}
259
+
260
+	/**
261
+	 * Gets the client's file name
262
+	 *
263
+	 * @return string|null
264
+	 */
265
+	public function getClientFilename(): ?string
266
+	{
267
+		return $this->clientFilename;
268
+	}
269
+
270
+	/**
271
+	 * Gets the client's file media type
272
+	 *
273
+	 * @return string|null
274
+	 */
275
+	public function getClientMediaType(): ?string
276
+	{
277
+		return $this->clientMediaType;
278
+	}
279 279
 }
Please login to merge, or discard this patch.
src/Message.php 1 patch
Indentation   +390 added lines, -390 removed lines patch added patch discarded remove patch
@@ -39,394 +39,394 @@
 block discarded – undo
39 39
 abstract class Message implements MessageInterface
40 40
 {
41 41
 
42
-    /**
43
-     * Allowed HTTP versions
44
-     *
45
-     * @var list<string>
46
-     */
47
-    public const ALLOWED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2'];
48
-
49
-    /**
50
-     * Default HTTP version
51
-     *
52
-     * @var string
53
-     */
54
-    public const DEFAULT_HTTP_VERSION = '1.1';
55
-
56
-    /**
57
-     * The message HTTP version
58
-     *
59
-     * @var string
60
-     */
61
-    private string $protocolVersion = self::DEFAULT_HTTP_VERSION;
62
-
63
-    /**
64
-     * The message headers
65
-     *
66
-     * @var array<string, list<string>>
67
-     */
68
-    private array $headers = [];
69
-
70
-    /**
71
-     * Original header names
72
-     *
73
-     * @var array<string, string>
74
-     */
75
-    private array $headerNames = [];
76
-
77
-    /**
78
-     * The message body
79
-     *
80
-     * @var StreamInterface|null
81
-     */
82
-    private ?StreamInterface $body = null;
83
-
84
-    /**
85
-     * Gets the message HTTP version
86
-     *
87
-     * @return string
88
-     */
89
-    public function getProtocolVersion(): string
90
-    {
91
-        return $this->protocolVersion;
92
-    }
93
-
94
-    /**
95
-     * Creates a new instance of the message with the given HTTP version
96
-     *
97
-     * @param string $version
98
-     *
99
-     * @return static
100
-     *
101
-     * @throws InvalidArgumentException
102
-     *         If the HTTP version isn't valid.
103
-     */
104
-    public function withProtocolVersion($version): MessageInterface
105
-    {
106
-        $clone = clone $this;
107
-        $clone->setProtocolVersion($version);
108
-
109
-        return $clone;
110
-    }
111
-
112
-    /**
113
-     * Gets the message headers
114
-     *
115
-     * @return array<string, list<string>>
116
-     */
117
-    public function getHeaders(): array
118
-    {
119
-        return $this->headers;
120
-    }
121
-
122
-    /**
123
-     * Checks if a header exists in the message by the given name
124
-     *
125
-     * @param string $name
126
-     *
127
-     * @return bool
128
-     */
129
-    public function hasHeader($name): bool
130
-    {
131
-        $key = strtolower($name);
132
-
133
-        return isset($this->headerNames[$key]);
134
-    }
135
-
136
-    /**
137
-     * Gets a header value(s) from the message by the given name
138
-     *
139
-     * @param string $name
140
-     *
141
-     * @return list<string>
142
-     */
143
-    public function getHeader($name): array
144
-    {
145
-        $key = strtolower($name);
146
-
147
-        if (!isset($this->headerNames[$key])) {
148
-            return [];
149
-        }
150
-
151
-        return $this->headers[$this->headerNames[$key]];
152
-    }
153
-
154
-    /**
155
-     * Gets a header value as a string from the message by the given name
156
-     *
157
-     * @param string $name
158
-     *
159
-     * @return string
160
-     */
161
-    public function getHeaderLine($name): string
162
-    {
163
-        $key = strtolower($name);
164
-
165
-        if (!isset($this->headerNames[$key])) {
166
-            return '';
167
-        }
168
-
169
-        return implode(',', $this->headers[$this->headerNames[$key]]);
170
-    }
171
-
172
-    /**
173
-     * Creates a new instance of the message with the given header overwriting the old header
174
-     *
175
-     * @param string $name
176
-     * @param string|string[] $value
177
-     *
178
-     * @return static
179
-     *
180
-     * @throws InvalidArgumentException
181
-     *         If the header isn't valid.
182
-     */
183
-    public function withHeader($name, $value): MessageInterface
184
-    {
185
-        $clone = clone $this;
186
-        $clone->setHeader($name, $value, true);
187
-
188
-        return $clone;
189
-    }
190
-
191
-    /**
192
-     * Creates a new instance of the message with the given header NOT overwriting the old header
193
-     *
194
-     * @param string $name
195
-     * @param string|string[] $value
196
-     *
197
-     * @return static
198
-     *
199
-     * @throws InvalidArgumentException
200
-     *         If the header isn't valid.
201
-     */
202
-    public function withAddedHeader($name, $value): MessageInterface
203
-    {
204
-        $clone = clone $this;
205
-        $clone->setHeader($name, $value, false);
206
-
207
-        return $clone;
208
-    }
209
-
210
-    /**
211
-     * Creates a new instance of the message without a header by the given name
212
-     *
213
-     * @param string $name
214
-     *
215
-     * @return static
216
-     */
217
-    public function withoutHeader($name): MessageInterface
218
-    {
219
-        $clone = clone $this;
220
-        $clone->deleteHeader($name);
221
-
222
-        return $clone;
223
-    }
224
-
225
-    /**
226
-     * Gets the message body
227
-     *
228
-     * @return StreamInterface
229
-     */
230
-    public function getBody(): StreamInterface
231
-    {
232
-        return $this->body ??= new PhpTempStream();
233
-    }
234
-
235
-    /**
236
-     * Creates a new instance of the message with the given body
237
-     *
238
-     * @param StreamInterface $body
239
-     *
240
-     * @return static
241
-     */
242
-    public function withBody(StreamInterface $body): MessageInterface
243
-    {
244
-        $clone = clone $this;
245
-        $clone->setBody($body);
246
-
247
-        return $clone;
248
-    }
249
-
250
-    /**
251
-     * Sets the given HTTP version to the message
252
-     *
253
-     * @param string $protocolVersion
254
-     *
255
-     * @return void
256
-     *
257
-     * @throws InvalidArgumentException
258
-     *         If the HTTP version isn't valid.
259
-     */
260
-    final protected function setProtocolVersion($protocolVersion): void
261
-    {
262
-        $this->validateProtocolVersion($protocolVersion);
263
-
264
-        $this->protocolVersion = $protocolVersion;
265
-    }
266
-
267
-    /**
268
-     * Sets a new header to the message with the given name and value(s)
269
-     *
270
-     * @param string $name
271
-     * @param string|string[] $value
272
-     * @param bool $replace
273
-     *
274
-     * @return void
275
-     *
276
-     * @throws InvalidArgumentException
277
-     *         If the header isn't valid.
278
-     */
279
-    final protected function setHeader($name, $value, bool $replace = true): void
280
-    {
281
-        if (!is_array($value)) {
282
-            $value = [$value];
283
-        }
284
-
285
-        $this->validateHeaderName($name);
286
-        $this->validateHeaderValue($name, $value);
287
-
288
-        if ($replace) {
289
-            $this->deleteHeader($name);
290
-        }
291
-
292
-        $key = strtolower($name);
293
-
294
-        $this->headerNames[$key] ??= $name;
295
-        $this->headers[$this->headerNames[$key]] ??= [];
296
-
297
-        foreach ($value as $item) {
298
-            $this->headers[$this->headerNames[$key]][] = $item;
299
-        }
300
-    }
301
-
302
-    /**
303
-     * Sets the given headers to the message
304
-     *
305
-     * @param array<string, string|string[]> $headers
306
-     *
307
-     * @return void
308
-     *
309
-     * @throws InvalidArgumentException
310
-     *         If one of the headers isn't valid.
311
-     */
312
-    final protected function setHeaders(array $headers): void
313
-    {
314
-        foreach ($headers as $name => $value) {
315
-            $this->setHeader($name, $value, false);
316
-        }
317
-    }
318
-
319
-    /**
320
-     * Deletes a header from the message by the given name
321
-     *
322
-     * @param string $name
323
-     *
324
-     * @return void
325
-     */
326
-    final protected function deleteHeader($name): void
327
-    {
328
-        $key = strtolower($name);
329
-
330
-        if (isset($this->headerNames[$key])) {
331
-            unset($this->headers[$this->headerNames[$key]]);
332
-            unset($this->headerNames[$key]);
333
-        }
334
-    }
335
-
336
-    /**
337
-     * Sets the given body to the message
338
-     *
339
-     * @param StreamInterface $body
340
-     *
341
-     * @return void
342
-     */
343
-    final protected function setBody(StreamInterface $body): void
344
-    {
345
-        $this->body = $body;
346
-    }
347
-
348
-    /**
349
-     * Validates the given HTTP version
350
-     *
351
-     * @param mixed $protocolVersion
352
-     *
353
-     * @return void
354
-     *
355
-     * @throws InvalidArgumentException
356
-     *         If the HTTP version isn't valid.
357
-     */
358
-    private function validateProtocolVersion($protocolVersion): void
359
-    {
360
-        if (!in_array($protocolVersion, self::ALLOWED_HTTP_VERSIONS, true)) {
361
-            throw new InvalidArgumentException('Unallowed HTTP version');
362
-        }
363
-    }
364
-
365
-    /**
366
-     * Validates the given header name
367
-     *
368
-     * @param mixed $name
369
-     *
370
-     * @return void
371
-     *
372
-     * @throws InvalidArgumentException
373
-     *         If the header name isn't valid.
374
-     */
375
-    private function validateHeaderName($name): void
376
-    {
377
-        if ($name === '') {
378
-            throw new InvalidArgumentException('HTTP header name cannot be an empty');
379
-        }
380
-
381
-        if (!is_string($name)) {
382
-            throw new InvalidArgumentException('HTTP header name must be a string');
383
-        }
384
-
385
-        if (!preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $name)) {
386
-            throw new InvalidArgumentException('HTTP header name is invalid');
387
-        }
388
-    }
389
-
390
-    /**
391
-     * Validates the given header value
392
-     *
393
-     * @param string $name
394
-     * @param array<array-key, mixed> $value
395
-     *
396
-     * @return void
397
-     *
398
-     * @throws InvalidArgumentException
399
-     *         If the header value isn't valid.
400
-     */
401
-    private function validateHeaderValue(string $name, array $value): void
402
-    {
403
-        if ([] === $value) {
404
-            throw new InvalidArgumentException(sprintf(
405
-                'The "%s" HTTP header value cannot be an empty array',
406
-                $name,
407
-            ));
408
-        }
409
-
410
-        foreach ($value as $key => $item) {
411
-            if ('' === $item) {
412
-                continue;
413
-            }
414
-
415
-            if (!is_string($item)) {
416
-                throw new InvalidArgumentException(sprintf(
417
-                    'The "%s[%s]" HTTP header value must be a string',
418
-                    $name,
419
-                    $key
420
-                ));
421
-            }
422
-
423
-            if (!preg_match(HeaderInterface::RFC7230_FIELD_VALUE_REGEX, $item)) {
424
-                throw new InvalidArgumentException(sprintf(
425
-                    'The "%s[%s]" HTTP header value is invalid',
426
-                    $name,
427
-                    $key
428
-                ));
429
-            }
430
-        }
431
-    }
42
+	/**
43
+	 * Allowed HTTP versions
44
+	 *
45
+	 * @var list<string>
46
+	 */
47
+	public const ALLOWED_HTTP_VERSIONS = ['1.0', '1.1', '2.0', '2'];
48
+
49
+	/**
50
+	 * Default HTTP version
51
+	 *
52
+	 * @var string
53
+	 */
54
+	public const DEFAULT_HTTP_VERSION = '1.1';
55
+
56
+	/**
57
+	 * The message HTTP version
58
+	 *
59
+	 * @var string
60
+	 */
61
+	private string $protocolVersion = self::DEFAULT_HTTP_VERSION;
62
+
63
+	/**
64
+	 * The message headers
65
+	 *
66
+	 * @var array<string, list<string>>
67
+	 */
68
+	private array $headers = [];
69
+
70
+	/**
71
+	 * Original header names
72
+	 *
73
+	 * @var array<string, string>
74
+	 */
75
+	private array $headerNames = [];
76
+
77
+	/**
78
+	 * The message body
79
+	 *
80
+	 * @var StreamInterface|null
81
+	 */
82
+	private ?StreamInterface $body = null;
83
+
84
+	/**
85
+	 * Gets the message HTTP version
86
+	 *
87
+	 * @return string
88
+	 */
89
+	public function getProtocolVersion(): string
90
+	{
91
+		return $this->protocolVersion;
92
+	}
93
+
94
+	/**
95
+	 * Creates a new instance of the message with the given HTTP version
96
+	 *
97
+	 * @param string $version
98
+	 *
99
+	 * @return static
100
+	 *
101
+	 * @throws InvalidArgumentException
102
+	 *         If the HTTP version isn't valid.
103
+	 */
104
+	public function withProtocolVersion($version): MessageInterface
105
+	{
106
+		$clone = clone $this;
107
+		$clone->setProtocolVersion($version);
108
+
109
+		return $clone;
110
+	}
111
+
112
+	/**
113
+	 * Gets the message headers
114
+	 *
115
+	 * @return array<string, list<string>>
116
+	 */
117
+	public function getHeaders(): array
118
+	{
119
+		return $this->headers;
120
+	}
121
+
122
+	/**
123
+	 * Checks if a header exists in the message by the given name
124
+	 *
125
+	 * @param string $name
126
+	 *
127
+	 * @return bool
128
+	 */
129
+	public function hasHeader($name): bool
130
+	{
131
+		$key = strtolower($name);
132
+
133
+		return isset($this->headerNames[$key]);
134
+	}
135
+
136
+	/**
137
+	 * Gets a header value(s) from the message by the given name
138
+	 *
139
+	 * @param string $name
140
+	 *
141
+	 * @return list<string>
142
+	 */
143
+	public function getHeader($name): array
144
+	{
145
+		$key = strtolower($name);
146
+
147
+		if (!isset($this->headerNames[$key])) {
148
+			return [];
149
+		}
150
+
151
+		return $this->headers[$this->headerNames[$key]];
152
+	}
153
+
154
+	/**
155
+	 * Gets a header value as a string from the message by the given name
156
+	 *
157
+	 * @param string $name
158
+	 *
159
+	 * @return string
160
+	 */
161
+	public function getHeaderLine($name): string
162
+	{
163
+		$key = strtolower($name);
164
+
165
+		if (!isset($this->headerNames[$key])) {
166
+			return '';
167
+		}
168
+
169
+		return implode(',', $this->headers[$this->headerNames[$key]]);
170
+	}
171
+
172
+	/**
173
+	 * Creates a new instance of the message with the given header overwriting the old header
174
+	 *
175
+	 * @param string $name
176
+	 * @param string|string[] $value
177
+	 *
178
+	 * @return static
179
+	 *
180
+	 * @throws InvalidArgumentException
181
+	 *         If the header isn't valid.
182
+	 */
183
+	public function withHeader($name, $value): MessageInterface
184
+	{
185
+		$clone = clone $this;
186
+		$clone->setHeader($name, $value, true);
187
+
188
+		return $clone;
189
+	}
190
+
191
+	/**
192
+	 * Creates a new instance of the message with the given header NOT overwriting the old header
193
+	 *
194
+	 * @param string $name
195
+	 * @param string|string[] $value
196
+	 *
197
+	 * @return static
198
+	 *
199
+	 * @throws InvalidArgumentException
200
+	 *         If the header isn't valid.
201
+	 */
202
+	public function withAddedHeader($name, $value): MessageInterface
203
+	{
204
+		$clone = clone $this;
205
+		$clone->setHeader($name, $value, false);
206
+
207
+		return $clone;
208
+	}
209
+
210
+	/**
211
+	 * Creates a new instance of the message without a header by the given name
212
+	 *
213
+	 * @param string $name
214
+	 *
215
+	 * @return static
216
+	 */
217
+	public function withoutHeader($name): MessageInterface
218
+	{
219
+		$clone = clone $this;
220
+		$clone->deleteHeader($name);
221
+
222
+		return $clone;
223
+	}
224
+
225
+	/**
226
+	 * Gets the message body
227
+	 *
228
+	 * @return StreamInterface
229
+	 */
230
+	public function getBody(): StreamInterface
231
+	{
232
+		return $this->body ??= new PhpTempStream();
233
+	}
234
+
235
+	/**
236
+	 * Creates a new instance of the message with the given body
237
+	 *
238
+	 * @param StreamInterface $body
239
+	 *
240
+	 * @return static
241
+	 */
242
+	public function withBody(StreamInterface $body): MessageInterface
243
+	{
244
+		$clone = clone $this;
245
+		$clone->setBody($body);
246
+
247
+		return $clone;
248
+	}
249
+
250
+	/**
251
+	 * Sets the given HTTP version to the message
252
+	 *
253
+	 * @param string $protocolVersion
254
+	 *
255
+	 * @return void
256
+	 *
257
+	 * @throws InvalidArgumentException
258
+	 *         If the HTTP version isn't valid.
259
+	 */
260
+	final protected function setProtocolVersion($protocolVersion): void
261
+	{
262
+		$this->validateProtocolVersion($protocolVersion);
263
+
264
+		$this->protocolVersion = $protocolVersion;
265
+	}
266
+
267
+	/**
268
+	 * Sets a new header to the message with the given name and value(s)
269
+	 *
270
+	 * @param string $name
271
+	 * @param string|string[] $value
272
+	 * @param bool $replace
273
+	 *
274
+	 * @return void
275
+	 *
276
+	 * @throws InvalidArgumentException
277
+	 *         If the header isn't valid.
278
+	 */
279
+	final protected function setHeader($name, $value, bool $replace = true): void
280
+	{
281
+		if (!is_array($value)) {
282
+			$value = [$value];
283
+		}
284
+
285
+		$this->validateHeaderName($name);
286
+		$this->validateHeaderValue($name, $value);
287
+
288
+		if ($replace) {
289
+			$this->deleteHeader($name);
290
+		}
291
+
292
+		$key = strtolower($name);
293
+
294
+		$this->headerNames[$key] ??= $name;
295
+		$this->headers[$this->headerNames[$key]] ??= [];
296
+
297
+		foreach ($value as $item) {
298
+			$this->headers[$this->headerNames[$key]][] = $item;
299
+		}
300
+	}
301
+
302
+	/**
303
+	 * Sets the given headers to the message
304
+	 *
305
+	 * @param array<string, string|string[]> $headers
306
+	 *
307
+	 * @return void
308
+	 *
309
+	 * @throws InvalidArgumentException
310
+	 *         If one of the headers isn't valid.
311
+	 */
312
+	final protected function setHeaders(array $headers): void
313
+	{
314
+		foreach ($headers as $name => $value) {
315
+			$this->setHeader($name, $value, false);
316
+		}
317
+	}
318
+
319
+	/**
320
+	 * Deletes a header from the message by the given name
321
+	 *
322
+	 * @param string $name
323
+	 *
324
+	 * @return void
325
+	 */
326
+	final protected function deleteHeader($name): void
327
+	{
328
+		$key = strtolower($name);
329
+
330
+		if (isset($this->headerNames[$key])) {
331
+			unset($this->headers[$this->headerNames[$key]]);
332
+			unset($this->headerNames[$key]);
333
+		}
334
+	}
335
+
336
+	/**
337
+	 * Sets the given body to the message
338
+	 *
339
+	 * @param StreamInterface $body
340
+	 *
341
+	 * @return void
342
+	 */
343
+	final protected function setBody(StreamInterface $body): void
344
+	{
345
+		$this->body = $body;
346
+	}
347
+
348
+	/**
349
+	 * Validates the given HTTP version
350
+	 *
351
+	 * @param mixed $protocolVersion
352
+	 *
353
+	 * @return void
354
+	 *
355
+	 * @throws InvalidArgumentException
356
+	 *         If the HTTP version isn't valid.
357
+	 */
358
+	private function validateProtocolVersion($protocolVersion): void
359
+	{
360
+		if (!in_array($protocolVersion, self::ALLOWED_HTTP_VERSIONS, true)) {
361
+			throw new InvalidArgumentException('Unallowed HTTP version');
362
+		}
363
+	}
364
+
365
+	/**
366
+	 * Validates the given header name
367
+	 *
368
+	 * @param mixed $name
369
+	 *
370
+	 * @return void
371
+	 *
372
+	 * @throws InvalidArgumentException
373
+	 *         If the header name isn't valid.
374
+	 */
375
+	private function validateHeaderName($name): void
376
+	{
377
+		if ($name === '') {
378
+			throw new InvalidArgumentException('HTTP header name cannot be an empty');
379
+		}
380
+
381
+		if (!is_string($name)) {
382
+			throw new InvalidArgumentException('HTTP header name must be a string');
383
+		}
384
+
385
+		if (!preg_match(HeaderInterface::RFC7230_TOKEN_REGEX, $name)) {
386
+			throw new InvalidArgumentException('HTTP header name is invalid');
387
+		}
388
+	}
389
+
390
+	/**
391
+	 * Validates the given header value
392
+	 *
393
+	 * @param string $name
394
+	 * @param array<array-key, mixed> $value
395
+	 *
396
+	 * @return void
397
+	 *
398
+	 * @throws InvalidArgumentException
399
+	 *         If the header value isn't valid.
400
+	 */
401
+	private function validateHeaderValue(string $name, array $value): void
402
+	{
403
+		if ([] === $value) {
404
+			throw new InvalidArgumentException(sprintf(
405
+				'The "%s" HTTP header value cannot be an empty array',
406
+				$name,
407
+			));
408
+		}
409
+
410
+		foreach ($value as $key => $item) {
411
+			if ('' === $item) {
412
+				continue;
413
+			}
414
+
415
+			if (!is_string($item)) {
416
+				throw new InvalidArgumentException(sprintf(
417
+					'The "%s[%s]" HTTP header value must be a string',
418
+					$name,
419
+					$key
420
+				));
421
+			}
422
+
423
+			if (!preg_match(HeaderInterface::RFC7230_FIELD_VALUE_REGEX, $item)) {
424
+				throw new InvalidArgumentException(sprintf(
425
+					'The "%s[%s]" HTTP header value is invalid',
426
+					$name,
427
+					$key
428
+				));
429
+			}
430
+		}
431
+	}
432 432
 }
Please login to merge, or discard this patch.
functions/server_request_files.php 2 patches
Indentation   +35 added lines, -35 removed lines patch added patch discarded remove patch
@@ -45,45 +45,45 @@
 block discarded – undo
45 45
  */
46 46
 function server_request_files(?array $files = null): array
47 47
 {
48
-    $files ??= $_FILES;
48
+	$files ??= $_FILES;
49 49
 
50
-    $walker = static function ($path, $size, $error, $name, $type) use (&$walker) {
51
-        if (!is_array($path)) {
52
-            // It makes no sense to create a stream
53
-            // if the file has not been successfully uploaded.
54
-            $stream = UPLOAD_ERR_OK <> $error ? null : new FileStream($path, 'rb');
50
+	$walker = static function ($path, $size, $error, $name, $type) use (&$walker) {
51
+		if (!is_array($path)) {
52
+			// It makes no sense to create a stream
53
+			// if the file has not been successfully uploaded.
54
+			$stream = UPLOAD_ERR_OK <> $error ? null : new FileStream($path, 'rb');
55 55
 
56
-            return new UploadedFile($stream, $size, $error, $name, $type);
57
-        }
56
+			return new UploadedFile($stream, $size, $error, $name, $type);
57
+		}
58 58
 
59
-        $result = [];
60
-        foreach ($path as $key => $_) {
61
-            if (UPLOAD_ERR_NO_FILE <> $error[$key]) {
62
-                $result[$key] = $walker(
63
-                    $path[$key],
64
-                    $size[$key],
65
-                    $error[$key],
66
-                    $name[$key],
67
-                    $type[$key]
68
-                );
69
-            }
70
-        }
59
+		$result = [];
60
+		foreach ($path as $key => $_) {
61
+			if (UPLOAD_ERR_NO_FILE <> $error[$key]) {
62
+				$result[$key] = $walker(
63
+					$path[$key],
64
+					$size[$key],
65
+					$error[$key],
66
+					$name[$key],
67
+					$type[$key]
68
+				);
69
+			}
70
+		}
71 71
 
72
-        return $result;
73
-    };
72
+		return $result;
73
+	};
74 74
 
75
-    $result = [];
76
-    foreach ($files as $key => $file) {
77
-        if (UPLOAD_ERR_NO_FILE <> $file['error']) {
78
-            $result[$key] = $walker(
79
-                $file['tmp_name'],
80
-                $file['size'],
81
-                $file['error'],
82
-                $file['name'],
83
-                $file['type']
84
-            );
85
-        }
86
-    }
75
+	$result = [];
76
+	foreach ($files as $key => $file) {
77
+		if (UPLOAD_ERR_NO_FILE <> $file['error']) {
78
+			$result[$key] = $walker(
79
+				$file['tmp_name'],
80
+				$file['size'],
81
+				$file['error'],
82
+				$file['name'],
83
+				$file['type']
84
+			);
85
+		}
86
+	}
87 87
 
88
-    return $result;
88
+	return $result;
89 89
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -47,7 +47,7 @@
 block discarded – undo
47 47
 {
48 48
     $files ??= $_FILES;
49 49
 
50
-    $walker = static function ($path, $size, $error, $name, $type) use (&$walker) {
50
+    $walker = static function($path, $size, $error, $name, $type) use (&$walker) {
51 51
         if (!is_array($path)) {
52 52
             // It makes no sense to create a stream
53 53
             // if the file has not been successfully uploaded.
Please login to merge, or discard this patch.