Add type hints to Stream, UploadedFile, and Uri

This commit is contained in:
PJ Dietz 2020-08-09 13:29:05 -04:00
parent 7ade042b4b
commit c339512f01
5 changed files with 51 additions and 35 deletions

View File

@ -17,6 +17,7 @@
<DocblockTypeContradiction> <DocblockTypeContradiction>
<errorLevel type="suppress"> <errorLevel type="suppress">
<file name="src/Message/ServerRequest.php" /> <file name="src/Message/ServerRequest.php" />
<file name="src/Message/Uri.php" />
</errorLevel> </errorLevel>
</DocblockTypeContradiction> </DocblockTypeContradiction>
<MissingClosureParamType> <MissingClosureParamType>

View File

@ -166,6 +166,7 @@ class Stream implements StreamInterface
* PHP $whence values for `fseek()`. SEEK_SET: Set position equal to * PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
* offset bytes SEEK_CUR: Set position to current location plus offset * offset bytes SEEK_CUR: Set position to current location plus offset
* SEEK_END: Set position to end-of-stream plus offset. * SEEK_END: Set position to end-of-stream plus offset.
* @return void
* @throws \RuntimeException on failure. * @throws \RuntimeException on failure.
*/ */
public function seek($offset, $whence = SEEK_SET) public function seek($offset, $whence = SEEK_SET)
@ -191,6 +192,7 @@ class Stream implements StreamInterface
* *
* @see seek() * @see seek()
* @link http://www.php.net/manual/en/function.fseek.php * @link http://www.php.net/manual/en/function.fseek.php
* @return void
* @throws \RuntimeException on failure. * @throws \RuntimeException on failure.
*/ */
public function rewind() public function rewind()

View File

@ -10,12 +10,19 @@ use Psr\Http\Message\UploadedFileInterface;
*/ */
class UploadedFile implements UploadedFileInterface class UploadedFile implements UploadedFileInterface
{ {
/** @var string */
private $clientFilename; private $clientFilename;
/** @var string */
private $clientMediaType; private $clientMediaType;
/** @var int */
private $error; private $error;
/** @var bool */
private $moved = false; private $moved = false;
/** @var int */
private $size; private $size;
/** @var StreamInterface */
private $stream; private $stream;
/** @var string|null */
private $tmpName; private $tmpName;
/** /**
@ -57,10 +64,11 @@ class UploadedFile implements UploadedFileInterface
$this->size = $size; $this->size = $size;
if (file_exists($tmpName)) { if (file_exists($tmpName)) {
$this->stream = new Stream(fopen($tmpName, 'r'));
$this->tmpName = $tmpName; $this->tmpName = $tmpName;
$this->stream = new Stream(fopen($tmpName, "r"));
} else { } else {
$this->stream = new NullStream(); $this->stream = new NullStream();
$this->tmpName = null;
} }
} }
@ -82,8 +90,11 @@ class UploadedFile implements UploadedFileInterface
*/ */
public function getStream() public function getStream()
{ {
if ($this->tmpName === null) {
throw new \RuntimeException("Unable to read uploaded file.");
}
if ($this->moved) { if ($this->moved) {
throw new \RuntimeException("File has already been moved"); throw new \RuntimeException("File has already been moved.");
} }
if (php_sapi_name() !== "cli" && !is_uploaded_file($this->tmpName)) { if (php_sapi_name() !== "cli" && !is_uploaded_file($this->tmpName)) {
throw new \RuntimeException("File is not an uploaded file."); throw new \RuntimeException("File is not an uploaded file.");
@ -105,6 +116,7 @@ class UploadedFile implements UploadedFileInterface
* @see http://php.net/is_uploaded_file * @see http://php.net/is_uploaded_file
* @see http://php.net/move_uploaded_file * @see http://php.net/move_uploaded_file
* @param string $path Path to which to move the uploaded file. * @param string $path Path to which to move the uploaded file.
* @return void
* @throws \InvalidArgumentException if the $path specified is invalid. * @throws \InvalidArgumentException if the $path specified is invalid.
* @throws \RuntimeException on any error during the move operation, or on * @throws \RuntimeException on any error during the move operation, or on
* the second or subsequent call to the method. * the second or subsequent call to the method.

View File

@ -40,36 +40,36 @@ class Uri implements UriInterface
/** /**
* @param string $uri A string representation of a URI. * @param string $uri A string representation of a URI.
*/ */
public function __construct($uri = "") public function __construct(string $uri = '')
{ {
if (is_string($uri) && $uri !== "") { $parsed = parse_url($uri);
$parsed = parse_url($uri); if (!$parsed) {
if ($parsed !== false) { return;
if (isset($parsed["scheme"])) { }
$this->scheme = $parsed["scheme"];
} if (isset($parsed['scheme'])) {
if (isset($parsed["host"])) { $this->scheme = $parsed['scheme'];
$this->host = strtolower($parsed["host"]); }
} if (isset($parsed['host'])) {
if (isset($parsed["port"])) { $this->host = strtolower($parsed['host']);
$this->port = $parsed["port"]; }
} if (isset($parsed['port'])) {
if (isset($parsed["user"])) { $this->port = $parsed['port'];
$this->user = $parsed["user"]; }
} if (isset($parsed['user'])) {
if (isset($parsed["pass"])) { $this->user = $parsed['user'];
$this->password = $parsed["pass"]; }
} if (isset($parsed['pass'])) {
if (isset($parsed["path"])) { $this->password = $parsed['pass'];
$this->path = $parsed["path"]; }
} if (isset($parsed['path'])) {
if (isset($parsed["query"])) { $this->path = $parsed['path'];
$this->query = $parsed["query"]; }
} if (isset($parsed['query'])) {
if (isset($parsed["fragment"])) { $this->query = $parsed['query'];
$this->fragment = $parsed["fragment"]; }
} if (isset($parsed['fragment'])) {
} $this->fragment = $parsed['fragment'];
} }
} }
@ -538,7 +538,7 @@ class Uri implements UriInterface
$reserved = ':/?#[]@!$&\'()*+,;='; $reserved = ':/?#[]@!$&\'()*+,;=';
$reserved = preg_quote($reserved); $reserved = preg_quote($reserved);
$pattern = '~(?:%(?![a-fA-F0-9]{2}))|(?:[^%a-zA-Z0-9\-\.\_\~' . $reserved . ']{1})~'; $pattern = '~(?:%(?![a-fA-F0-9]{2}))|(?:[^%a-zA-Z0-9\-\.\_\~' . $reserved . ']{1})~';
$callback = function ($matches) { $callback = function (array $matches): string {
return urlencode($matches[0]); return urlencode($matches[0]);
}; };
return preg_replace_callback($pattern, $callback, $subject); return preg_replace_callback($pattern, $callback, $subject);

View File

@ -40,7 +40,7 @@ class UploadedFileTest extends TestCase
public function testGetStreamReturnsStreamInterface() public function testGetStreamReturnsStreamInterface()
{ {
$file = new UploadedFile("", "", 0, "", 0); $file = new UploadedFile("", "", 0, $this->tmpName, 0);
$this->assertInstanceOf(StreamInterface::class, $file->getStream()); $this->assertInstanceOf(StreamInterface::class, $file->getStream());
} }
@ -53,10 +53,11 @@ class UploadedFileTest extends TestCase
$this->assertEquals($content, (string) $stream); $this->assertEquals($content, (string) $stream);
} }
public function testGetStreamReturnsEmptyStreamForNoFile() public function testGetStreamThrowsRuntimeExceptionForNoFile()
{ {
$file = new UploadedFile("", "", 0, "", 0); $file = new UploadedFile("", "", 0, "", 0);
$this->assertTrue($file->getStream()->eof()); $this->expectException(RuntimeException::class);
$file->getStream();
} }
public function testGetStreamThrowsExceptionAfterMoveTo() public function testGetStreamThrowsExceptionAfterMoveTo()