Skip to content

Commit fbf718a

Browse files
authored
feat: add @throws (#210)
1 parent 803f107 commit fbf718a

22 files changed

Lines changed: 234 additions & 36 deletions

phpstan.neon.dist

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,23 @@ parameters:
44
- %currentWorkingDirectory%/src
55
- %currentWorkingDirectory%/tests
66

7+
exceptions:
8+
check:
9+
missingCheckedExceptionInThrows: true
10+
tooWideThrowType: true
11+
uncheckedExceptionClasses:
12+
- RuntimeException
13+
714
ignoreErrors:
815
# There's no other way to test-pass without assertions while counting it towards coverage https://github.com/sebastianbergmann/phpunit/issues/3016
916
- '~Call to static method PHPUnit\\Framework\\Assert::assertTrue\(\) with true will always evaluate to true~'
1017

1118
# Adds unnecessary maintanence overhead. We rather rely on PHPStan telling us the method returns unhandled FALSE
1219
- "~Class DateTime(Immutable)? is unsafe to use. Its methods can return FALSE instead of throwing an exception. Please add 'use Safe\\\\DateTime(Immutable)?;' at the beginning of the file to use the variant provided by the 'thecodingmachine/safe' library~"
1320

21+
# No need to have @throws in some phpunit related methods
22+
- message: "~Method SimPod\\\\ClickHouseClient\\\\Tests\\\\.+?Test(CaseBase)?::(test.+?|provider.+?|setUp(BeforeClass)?|tearDown|setupClickHouseClient|tearDownDataBase)\\(\\) throws checked exception .+? but it's missing from the PHPDoc @throws tag~"
23+
path: tests
24+
1425
includes:
1526
- phpstan-baseline.neon

src/Client/ClickHouseClient.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,33 @@
44

55
namespace SimPod\ClickHouseClient\Client;
66

7+
use Psr\Http\Client\ClientExceptionInterface;
8+
use Safe\Exceptions\PcreException;
9+
use SimPod\ClickHouseClient\Exception\CannotInsert;
10+
use SimPod\ClickHouseClient\Exception\ServerError;
11+
use SimPod\ClickHouseClient\Exception\UnsupportedValue;
712
use SimPod\ClickHouseClient\Format\Format;
813
use SimPod\ClickHouseClient\Output\Output;
914

1015
interface ClickHouseClient
1116
{
12-
/** @param array<string, float|int|string> $settings */
17+
/**
18+
* @param array<string, float|int|string> $settings
19+
*
20+
* @throws ClientExceptionInterface
21+
* @throws PcreException
22+
* @throws ServerError
23+
*/
1324
public function executeQuery(string $query, array $settings = []): void;
1425

1526
/**
1627
* @param array<string, mixed> $params
1728
* @param array<string, float|int|string> $settings
29+
*
30+
* @throws ClientExceptionInterface
31+
* @throws PcreException
32+
* @throws ServerError
33+
* @throws UnsupportedValue
1834
*/
1935
public function executeQueryWithParams(string $query, array $params, array $settings = []): void;
2036

@@ -24,6 +40,10 @@ public function executeQueryWithParams(string $query, array $params, array $sett
2440
*
2541
* @return O
2642
*
43+
* @throws ClientExceptionInterface
44+
* @throws PcreException
45+
* @throws ServerError
46+
*
2747
* @template O of Output
2848
*/
2949
public function select(string $query, Format $outputFormat, array $settings = []): Output;
@@ -35,6 +55,11 @@ public function select(string $query, Format $outputFormat, array $settings = []
3555
*
3656
* @return O
3757
*
58+
* @throws ClientExceptionInterface
59+
* @throws PcreException
60+
* @throws ServerError
61+
* @throws UnsupportedValue
62+
*
3863
* @template O of Output
3964
*/
4065
public function selectWithParams(string $query, array $params, Format $outputFormat, array $settings = []): Output;
@@ -43,13 +68,21 @@ public function selectWithParams(string $query, array $params, Format $outputFor
4368
* @param array<array<mixed>> $values
4469
* @param array<string>|null $columns
4570
* @param array<string, float|int|string> $settings
71+
*
72+
* @throws CannotInsert
73+
* @throws ClientExceptionInterface
74+
* @throws PcreException
75+
* @throws ServerError
4676
*/
4777
public function insert(string $table, array $values, array|null $columns = null, array $settings = []): void;
4878

4979
/**
5080
* @param array<string, float|int|string> $settings
5181
* @param Format<O> $inputFormat
5282
*
83+
* @throws ClientExceptionInterface
84+
* @throws ServerError
85+
*
5386
* @template O of Output
5487
*/
5588
public function insertWithFormat(string $table, Format $inputFormat, string $data, array $settings = []): void;

src/Client/Http/RequestFactory.php

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
namespace SimPod\ClickHouseClient\Client\Http;
66

7+
use InvalidArgumentException;
78
use Psr\Http\Message\RequestFactoryInterface;
89
use Psr\Http\Message\RequestInterface;
910
use Psr\Http\Message\StreamFactoryInterface;
1011
use Psr\Http\Message\UriFactoryInterface;
1112
use Psr\Http\Message\UriInterface;
13+
use RuntimeException;
1214

1315
use function http_build_query;
1416
use function is_string;
@@ -17,12 +19,26 @@
1719

1820
final class RequestFactory
1921
{
22+
private UriInterface|null $uri;
23+
24+
/** @throws InvalidArgumentException */
2025
public function __construct(
2126
private RequestFactoryInterface $requestFactory,
2227
private StreamFactoryInterface $streamFactory,
23-
private UriFactoryInterface|null $uriFactory = null,
24-
private UriInterface|string $uri = '',
28+
UriFactoryInterface|null $uriFactory = null,
29+
UriInterface|string $uri = '',
2530
) {
31+
if ($uriFactory === null && $uri === '') {
32+
$uri = null;
33+
} elseif (is_string($uri)) {
34+
if ($uriFactory === null) {
35+
throw new InvalidArgumentException('UriFactoryInterface is required when `$uri` is string');
36+
}
37+
38+
$uri = $uriFactory->createUri($uri);
39+
}
40+
41+
$this->uri = $uri;
2642
}
2743

2844
public function prepareRequest(RequestOptions $requestOptions): RequestInterface
@@ -34,20 +50,32 @@ public function prepareRequest(RequestOptions $requestOptions): RequestInterface
3450
PHP_QUERY_RFC3986,
3551
);
3652

37-
if ($this->uriFactory === null) {
38-
return $this->requestFactory->createRequest('POST', $query === '' ? '' : '?' . $query)
39-
->withBody($this->streamFactory->createStream($requestOptions->sql));
53+
$body = $this->streamFactory->createStream($requestOptions->sql);
54+
55+
if ($this->uri === null) {
56+
$uri = $query === '' ? '' : '?' . $query;
57+
} else {
58+
$uriQuery = $this->uri->getQuery();
59+
try {
60+
$uri = $this->uri->withQuery($uriQuery . ($uriQuery !== '' && $query !== '' ? '&' : '') . $query);
61+
} catch (InvalidArgumentException) {
62+
$this->absurd();
63+
}
4064
}
4165

42-
$uri = $this->uri;
43-
if (is_string($uri)) {
44-
$uri = $this->uriFactory->createUri($uri);
66+
$request = $this->requestFactory->createRequest('POST', $uri);
67+
try {
68+
$request = $request->withBody($body);
69+
} catch (InvalidArgumentException) {
70+
$this->absurd();
4571
}
4672

47-
$uriQuery = $uri->getQuery();
48-
$uri = $uri->withQuery($uriQuery . ($uriQuery !== '' && $query !== '' ? '&' : '') . $query);
73+
return $request;
74+
}
4975

50-
return $this->requestFactory->createRequest('POST', $uri)
51-
->withBody($this->streamFactory->createStream($requestOptions->sql));
76+
/** @psalm-return never */
77+
private function absurd(): void
78+
{
79+
throw new RuntimeException('Called `absurd` function which should never be called');
5280
}
5381
}

src/Client/PsrClickHouseAsyncClient.php

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace SimPod\ClickHouseClient\Client;
66

77
use DateTimeZone;
8+
use Exception;
89
use GuzzleHttp\Promise\Create;
910
use GuzzleHttp\Promise\PromiseInterface;
1011
use Http\Client\HttpAsyncClient;
@@ -31,6 +32,11 @@ public function __construct(
3132
$this->sqlFactory = new SqlFactory(new ValueFormatter($clickHouseTimeZone));
3233
}
3334

35+
/**
36+
* {@inheritDoc}
37+
*
38+
* @throws Exception
39+
*/
3440
public function select(string $sql, Format $outputFormat, array $settings = []): PromiseInterface
3541
{
3642
$formatClause = $outputFormat::toSql();
@@ -45,6 +51,11 @@ public function select(string $sql, Format $outputFormat, array $settings = []):
4551
);
4652
}
4753

54+
/**
55+
* {@inheritDoc}
56+
*
57+
* @throws Exception
58+
*/
4859
public function selectWithParams(
4960
string $sql,
5061
array $params,
@@ -61,6 +72,8 @@ public function selectWithParams(
6172
/**
6273
* @param array<string, float|int|string> $settings
6374
* @param (callable(ResponseInterface):mixed)|null $processResponse
75+
*
76+
* @throws Exception
6477
*/
6578
private function executeRequest(
6679
string $sql,
@@ -75,18 +88,21 @@ private function executeRequest(
7588
),
7689
);
7790

78-
return Create::promiseFor($this->asyncClient->sendAsyncRequest($request))->then(
79-
static function (ResponseInterface $response) use ($processResponse) {
80-
if ($response->getStatusCode() !== 200) {
81-
throw ServerError::fromResponse($response);
82-
}
91+
return Create::promiseFor(
92+
$this->asyncClient->sendAsyncRequest($request),
93+
)
94+
->then(
95+
static function (ResponseInterface $response) use ($processResponse) {
96+
if ($response->getStatusCode() !== 200) {
97+
throw ServerError::fromResponse($response);
98+
}
8399

84-
if ($processResponse === null) {
85-
return $response;
86-
}
100+
if ($processResponse === null) {
101+
return $response;
102+
}
87103

88-
return $processResponse($response);
89-
},
90-
);
104+
return $processResponse($response);
105+
},
106+
);
91107
}
92108
}

src/Client/PsrClickHouseClient.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace SimPod\ClickHouseClient\Client;
66

77
use DateTimeZone;
8+
use Psr\Http\Client\ClientExceptionInterface;
89
use Psr\Http\Client\ClientInterface;
910
use Psr\Http\Message\ResponseInterface;
1011
use SimPod\ClickHouseClient\Client\Http\RequestFactory;
@@ -128,7 +129,12 @@ public function insertWithFormat(string $table, Format $inputFormat, string $dat
128129
);
129130
}
130131

131-
/** @param array<string, float|int|string> $settings */
132+
/**
133+
* @param array<string, float|int|string> $settings
134+
*
135+
* @throws ServerError
136+
* @throws ClientExceptionInterface
137+
*/
132138
private function executeRequest(string $sql, array $settings = []): ResponseInterface
133139
{
134140
$request = $this->requestFactory->prepareRequest(

src/Format/Json.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace SimPod\ClickHouseClient\Format;
66

7+
use Safe\Exceptions\JsonException;
78
use SimPod\ClickHouseClient\Output\Output;
89

910
/**
@@ -12,6 +13,7 @@
1213
*/
1314
final class Json implements Format
1415
{
16+
/** @throws JsonException */
1517
public static function output(string $contents): Output
1618
{
1719
return new \SimPod\ClickHouseClient\Output\Json($contents);

src/Format/JsonCompact.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace SimPod\ClickHouseClient\Format;
66

7+
use Safe\Exceptions\JsonException;
78
use SimPod\ClickHouseClient\Output\Output;
89

910
/**
@@ -12,6 +13,7 @@
1213
*/
1314
final class JsonCompact implements Format
1415
{
16+
/** @throws JsonException */
1517
public static function output(string $contents): Output
1618
{
1719
return new \SimPod\ClickHouseClient\Output\JsonCompact($contents);

src/Format/JsonEachRow.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace SimPod\ClickHouseClient\Format;
66

7+
use Safe\Exceptions\JsonException;
78
use SimPod\ClickHouseClient\Output\Output;
89

910
/**
@@ -12,6 +13,7 @@
1213
*/
1314
final class JsonEachRow implements Format
1415
{
16+
/** @throws JsonException */
1517
public static function output(string $contents): Output
1618
{
1719
return new \SimPod\ClickHouseClient\Output\JsonEachRow($contents);

src/Output/Json.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace SimPod\ClickHouseClient\Output;
66

7+
use Safe\Exceptions\JsonException;
8+
79
use function Safe\json_decode;
810

911
/**
@@ -26,6 +28,7 @@ final class Json implements Output
2628
/** @var array{elapsed: float, rows_read: int, bytes_read: int} */
2729
public array $statistics;
2830

31+
/** @throws JsonException */
2932
public function __construct(string $contentsJson)
3033
{
3134
// phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong

src/Output/JsonCompact.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace SimPod\ClickHouseClient\Output;
66

7+
use Safe\Exceptions\JsonException;
8+
79
use function Safe\json_decode;
810

911
/**
@@ -26,6 +28,7 @@ final class JsonCompact implements Output
2628
/** @var array{elapsed: float, rows_read: int, bytes_read: int} */
2729
public array $statistics;
2830

31+
/** @throws JsonException */
2932
public function __construct(string $contentsJson)
3033
{
3134
/**

0 commit comments

Comments
 (0)