Skip to content

Commit 873f19c

Browse files
committed
use phpstan
1 parent 12d1e26 commit 873f19c

File tree

7 files changed

+134
-39
lines changed

7 files changed

+134
-39
lines changed

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
}
1010
],
1111
"require": {
12-
"php": "^7.1 | ^8.0",
12+
"php": "^7.2",
1313
"nyholm/psr7": "^1.3",
1414
"php-http/httplug": "^2.0",
1515
"psr/http-client": "^1.0",

phpstan.neon.dist

+35-2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,38 @@ parameters:
33
paths:
44
- src
55
ignoreErrors:
6-
- message: '#^Method Http\\Adapter\\React\\Client::sendRequest\(\) should return Psr\\Http\\Message\\ResponseInterface but returns mixed.$#'
7-
path: src/Client.php
6+
# phpstan seems confused by passing a variable by reference to stream_select
7+
-
8+
message: '#^Negated boolean expression is always false.$#'
9+
count: 1
10+
path: src/RequestWriter.php
11+
12+
-
13+
message: "#^Constructor of class Http\\\\Client\\\\Socket\\\\Client has an unused parameter \\$config2\\.$#"
14+
count: 1
15+
path: src/Client.php
16+
17+
-
18+
message: "#^Method Http\\\\Client\\\\Socket\\\\Client\\:\\:__construct\\(\\) has parameter \\$config1 with no type specified\\.$#"
19+
count: 1
20+
path: src/Client.php
21+
22+
-
23+
message: "#^Method Http\\\\Client\\\\Socket\\\\Client\\:\\:__construct\\(\\) has parameter \\$config2 with no type specified\\.$#"
24+
count: 1
25+
path: src/Client.php
26+
27+
-
28+
message: "#^Method Http\\\\Client\\\\Socket\\\\Client\\:\\:configure\\(\\) should return array\\{remote_socket\\: string\\|null, timeout\\: int, stream_context\\: resource, stream_context_options\\: array\\<string, mixed\\>, stream_context_param\\: array\\<string, mixed\\>, ssl\\: bool\\|null, write_buffer_size\\: int, ssl_method\\: int\\} but returns array\\.$#"
29+
count: 1
30+
path: src/Client.php
31+
32+
-
33+
message: "#^Parameter \\#1 \\$options of function stream_context_create expects array\\|null, mixed given\\.$#"
34+
count: 1
35+
path: src/Client.php
36+
37+
-
38+
message: "#^Parameter \\#2 \\$params of function stream_context_create expects array\\|null, mixed given\\.$#"
39+
count: 1
40+
path: src/Client.php

src/Client.php

+22-23
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,24 @@ class Client implements HttpClient
2424
use RequestWriter;
2525
use ResponseReader;
2626

27-
private $config = [
28-
'remote_socket' => null,
29-
'timeout' => null,
30-
'stream_context_options' => [],
31-
'stream_context_param' => [],
32-
'ssl' => null,
33-
'write_buffer_size' => 8192,
34-
'ssl_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
35-
];
27+
/**
28+
* @var array{remote_socket: string|null, timeout: int, stream_context: resource, stream_context_options: array<string, mixed>, stream_context_param: array<string, mixed>, ssl: ?boolean, write_buffer_size: int, ssl_method: int}
29+
*/
30+
private $config;
3631

3732
/**
3833
* Constructor.
3934
*
40-
* @param array $config {
35+
* @param array{remote_socket?: string|null, timeout?: int, stream_context?: resource, stream_context_options?: array<string, mixed>, stream_context_param?: array<string, mixed>, ssl?: ?boolean, write_buffer_size?: int, ssl_method?: int} $config
4136
*
42-
* @var string $remote_socket Remote entrypoint (can be a tcp or unix domain address)
43-
* @var int $timeout Timeout before canceling request
44-
* @var array $stream_context_options Context options as defined in the PHP documentation
45-
* @var array $stream_context_param Context params as defined in the PHP documentation
46-
* @var bool $ssl Use ssl, default to scheme from request, false if not present
47-
* @var int $write_buffer_size Buffer when writing the request body, defaults to 8192
48-
* @var int $ssl_method Crypto method for ssl/tls, see PHP doc, defaults to STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
49-
* }
37+
* string|null remote_socket Remote entrypoint (can be a tcp or unix domain address)
38+
* int timeout Timeout before canceling request
39+
* stream resource The initialized stream context, if not set the context is created from the options and param.
40+
* array<string, mixed> stream_context_options Context options as defined in the PHP documentation
41+
* array<string, mixed> stream_context_param Context params as defined in the PHP documentation
42+
* boolean ssl Use ssl, default to scheme from request, false if not present
43+
* int write_buffer_size Buffer when writing the request body, defaults to 8192
44+
* int ssl_method Crypto method for ssl/tls, see PHP doc, defaults to STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
5045
*/
5146
public function __construct($config1 = [], $config2 = null, array $config = [])
5247
{
@@ -120,10 +115,10 @@ protected function createSocket(RequestInterface $request, string $remote, bool
120115
throw new ConnectionException($errMsg, $request);
121116
}
122117

123-
stream_set_timeout($socket, floor($this->config['timeout'] / 1000), $this->config['timeout'] % 1000);
118+
stream_set_timeout($socket, (int) floor($this->config['timeout'] / 1000), $this->config['timeout'] % 1000);
124119

125120
if ($useSsl && false === @stream_socket_enable_crypto($socket, true, $this->config['ssl_method'])) {
126-
throw new SSLConnectionException(sprintf('Cannot enable tls: %s', error_get_last()['message']), $request);
121+
throw new SSLConnectionException(sprintf('Cannot enable tls: %s', error_get_last()['message'] ?? 'no error reported'), $request);
127122
}
128123

129124
return $socket;
@@ -133,6 +128,8 @@ protected function createSocket(RequestInterface $request, string $remote, bool
133128
* Close the socket, used when having an error.
134129
*
135130
* @param resource $socket
131+
*
132+
* @return void
136133
*/
137134
protected function closeSocket($socket)
138135
{
@@ -142,9 +139,9 @@ protected function closeSocket($socket)
142139
/**
143140
* Return configuration for the socket client.
144141
*
145-
* @param array $config Configuration from user
142+
* @param array{remote_socket?: string|null, timeout?: int, stream_context?: resource, stream_context_options?: array<string, mixed>, stream_context_param?: array<string, mixed>, ssl?: ?boolean, write_buffer_size?: int, ssl_method?: int} $config
146143
*
147-
* @return array Configuration resolved
144+
* @return array{remote_socket: string|null, timeout: int, stream_context: resource, stream_context_options: array<string, mixed>, stream_context_param: array<string, mixed>, ssl: ?boolean, write_buffer_size: int, ssl_method: int}
148145
*/
149146
protected function configure(array $config = [])
150147
{
@@ -154,12 +151,14 @@ protected function configure(array $config = [])
154151
return stream_context_create($options['stream_context_options'], $options['stream_context_param']);
155152
});
156153

157-
$resolver->setDefault('timeout', ini_get('default_socket_timeout') * 1000);
154+
$resolver->setDefault('timeout', ((int) ini_get('default_socket_timeout')) * 1000);
158155

159156
$resolver->setAllowedTypes('stream_context_options', 'array');
160157
$resolver->setAllowedTypes('stream_context_param', 'array');
161158
$resolver->setAllowedTypes('stream_context', 'resource');
162159
$resolver->setAllowedTypes('ssl', ['bool', 'null']);
160+
$resolver->setDefault('ssl_method', STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT);
161+
$resolver->setDefault('write_buffer_size', 8192);
163162

164163
return $resolver->resolve($config);
165164
}

src/Exception/NetworkException.php

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
class NetworkException extends \RuntimeException implements NetworkExceptionInterface
99
{
10+
/**
11+
* @var RequestInterface
12+
*/
1013
private $request;
1114

1215
public function __construct(string $message, RequestInterface $request, \Exception $previous = null)

src/RequestWriter.php

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ trait RequestWriter
1919
*
2020
* @param resource $socket
2121
*
22+
* @return void
23+
*
2224
* @throws BrokenPipeException
2325
*/
2426
protected function writeRequest($socket, RequestInterface $request, int $bufferSize = 8192)
@@ -37,6 +39,8 @@ protected function writeRequest($socket, RequestInterface $request, int $bufferS
3739
*
3840
* @param resource $socket
3941
*
42+
* @return void
43+
*
4044
* @throws BrokenPipeException
4145
*/
4246
protected function writeBody($socket, RequestInterface $request, int $bufferSize = 8192)

src/ResponseReader.php

+6-3
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ protected function readResponse(RequestInterface $request, $socket): ResponseInt
4848
if (array_key_exists('timed_out', $metadatas) && true === $metadatas['timed_out']) {
4949
throw new TimeoutException('Error while reading response, stream timed out', $request, null);
5050
}
51-
52-
$parts = explode(' ', array_shift($headers), 3);
51+
$header = array_shift($headers);
52+
$parts = null !== $header ? explode(' ', $header, 3) : [];
5353

5454
if (count($parts) <= 1) {
5555
throw new BrokenPipeException('Cannot read the response', $request);
@@ -77,7 +77,7 @@ protected function readResponse(RequestInterface $request, $socket): ResponseInt
7777
: '';
7878
}
7979

80-
$response = new Response($status, $responseHeaders, null, $protocol, $reason);
80+
$response = new Response((int) $status, $responseHeaders, null, $protocol, $reason);
8181
$stream = $this->createStream($socket, $request, $response);
8282

8383
return $response->withBody($stream);
@@ -95,6 +95,9 @@ protected function createStream($socket, RequestInterface $request, ResponseInte
9595
if ($response->hasHeader('Content-Length')) {
9696
$size = (int) $response->getHeaderLine('Content-Length');
9797
}
98+
if ($size < 0) {
99+
$size = null;
100+
}
98101

99102
return new Stream($request, $socket, $size);
100103
}

src/Stream.php

+63-10
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
*/
2626
class Stream implements StreamInterface
2727
{
28-
/** @var resource Underlying socket */
28+
/** @var resource|null Underlying socket */
2929
private $socket;
3030

3131
/**
@@ -34,12 +34,12 @@ class Stream implements StreamInterface
3434
private $isDetached = false;
3535

3636
/**
37-
* @var int|null Size of the stream, so we know what we must read, null if not available (i.e. a chunked stream)
37+
* @var int<0, max>|null Size of the stream, so we know what we must read, null if not available (i.e. a chunked stream)
3838
*/
3939
private $size;
4040

4141
/**
42-
* @var int Size of the stream readed, to avoid reading more than available and have the user blocked
42+
* @var int<0, max> Size of the stream readed, to avoid reading more than available and have the user blocked
4343
*/
4444
private $readed = 0;
4545

@@ -51,7 +51,8 @@ class Stream implements StreamInterface
5151
/**
5252
* Create the stream.
5353
*
54-
* @param resource $socket
54+
* @param resource $socket
55+
* @param int<0, max>|null $size
5556
*/
5657
public function __construct(RequestInterface $request, $socket, ?int $size = null)
5758
{
@@ -77,6 +78,9 @@ public function __toString()
7778
*/
7879
public function close()
7980
{
81+
if ($this->isDetached || null === $this->socket) {
82+
throw new StreamException('Stream is detached');
83+
}
8084
fclose($this->socket);
8185
}
8286

@@ -85,6 +89,9 @@ public function close()
8589
*/
8690
public function detach()
8791
{
92+
if ($this->isDetached) {
93+
return null;
94+
}
8895
$this->isDetached = true;
8996
$socket = $this->socket;
9097
$this->socket = null;
@@ -94,6 +101,8 @@ public function detach()
94101

95102
/**
96103
* {@inheritdoc}
104+
*
105+
* @return int<0, max>|null
97106
*/
98107
public function getSize()
99108
{
@@ -105,14 +114,26 @@ public function getSize()
105114
*/
106115
public function tell()
107116
{
108-
return ftell($this->socket);
117+
if ($this->isDetached || null === $this->socket) {
118+
throw new StreamException('Stream is detached');
119+
}
120+
$tell = ftell($this->socket);
121+
if (false === $tell) {
122+
throw new StreamException('ftell returned false');
123+
}
124+
125+
return $tell;
109126
}
110127

111128
/**
112129
* {@inheritdoc}
113130
*/
114131
public function eof()
115132
{
133+
if ($this->isDetached || null === $this->socket) {
134+
throw new StreamException('Stream is detached');
135+
}
136+
116137
return feof($this->socket);
117138
}
118139

@@ -126,6 +147,8 @@ public function isSeekable()
126147

127148
/**
128149
* {@inheritdoc}
150+
*
151+
* @return void
129152
*/
130153
public function seek($offset, $whence = SEEK_SET)
131154
{
@@ -134,6 +157,8 @@ public function seek($offset, $whence = SEEK_SET)
134157

135158
/**
136159
* {@inheritdoc}
160+
*
161+
* @return void
137162
*/
138163
public function rewind()
139164
{
@@ -166,11 +191,21 @@ public function isReadable()
166191

167192
/**
168193
* {@inheritdoc}
194+
*
195+
* @param int<0, max> $length
169196
*/
170197
public function read($length)
171198
{
199+
if ($this->isDetached || null === $this->socket) {
200+
throw new StreamException('Stream is detached');
201+
}
172202
if (null === $this->getSize()) {
173-
return fread($this->socket, $length);
203+
$read = fread($this->socket, $length);
204+
if (false === $read) {
205+
throw new StreamException('Failed to read from stream');
206+
}
207+
208+
return $read;
174209
}
175210

176211
if ($this->getSize() === $this->readed) {
@@ -179,6 +214,9 @@ public function read($length)
179214

180215
// Even if we request a length a non blocking stream can return less data than asked
181216
$read = fread($this->socket, $length);
217+
if (false === $read) {
218+
throw new StreamException('Failed to read from stream');
219+
}
182220

183221
if ($this->getMetadata('timed_out')) {
184222
throw new TimeoutException('Stream timed out while reading data', $this->request);
@@ -194,15 +232,26 @@ public function read($length)
194232
*/
195233
public function getContents()
196234
{
235+
if ($this->isDetached || null === $this->socket) {
236+
throw new StreamException('Stream is detached');
237+
}
238+
197239
if (null === $this->getSize()) {
198-
return stream_get_contents($this->socket);
240+
$contents = stream_get_contents($this->socket);
241+
if (false === $contents) {
242+
throw new StreamException('failed to get contents of stream');
243+
}
244+
245+
return $contents;
199246
}
200247

201248
$contents = '';
202249

203-
do {
204-
$contents .= $this->read($this->getSize() - $this->readed);
205-
} while ($this->readed < $this->getSize());
250+
$toread = $this->getSize() - $this->readed;
251+
while ($toread > 0) {
252+
$contents .= $this->read($toread);
253+
$toread = $this->getSize() - $this->readed;
254+
}
206255

207256
return $contents;
208257
}
@@ -212,6 +261,10 @@ public function getContents()
212261
*/
213262
public function getMetadata($key = null)
214263
{
264+
if ($this->isDetached || null === $this->socket) {
265+
throw new StreamException('Stream is detached');
266+
}
267+
215268
$meta = stream_get_meta_data($this->socket);
216269

217270
if (null === $key) {

0 commit comments

Comments
 (0)