Skip to content

Commit

Permalink
feat: add @throws (#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
simPod committed Oct 16, 2023
1 parent 803f107 commit fbf718a
Show file tree
Hide file tree
Showing 22 changed files with 234 additions and 36 deletions.
11 changes: 11 additions & 0 deletions phpstan.neon.dist
Expand Up @@ -4,12 +4,23 @@ parameters:
- %currentWorkingDirectory%/src
- %currentWorkingDirectory%/tests

exceptions:
check:
missingCheckedExceptionInThrows: true
tooWideThrowType: true
uncheckedExceptionClasses:
- RuntimeException

ignoreErrors:
# There's no other way to test-pass without assertions while counting it towards coverage https://github.com/sebastianbergmann/phpunit/issues/3016
- '~Call to static method PHPUnit\\Framework\\Assert::assertTrue\(\) with true will always evaluate to true~'

# Adds unnecessary maintanence overhead. We rather rely on PHPStan telling us the method returns unhandled FALSE
- "~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~"

# No need to have @throws in some phpunit related methods
- 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~"
path: tests

includes:
- phpstan-baseline.neon
35 changes: 34 additions & 1 deletion src/Client/ClickHouseClient.php
Expand Up @@ -4,17 +4,33 @@

namespace SimPod\ClickHouseClient\Client;

use Psr\Http\Client\ClientExceptionInterface;
use Safe\Exceptions\PcreException;
use SimPod\ClickHouseClient\Exception\CannotInsert;
use SimPod\ClickHouseClient\Exception\ServerError;
use SimPod\ClickHouseClient\Exception\UnsupportedValue;
use SimPod\ClickHouseClient\Format\Format;
use SimPod\ClickHouseClient\Output\Output;

interface ClickHouseClient
{
/** @param array<string, float|int|string> $settings */
/**
* @param array<string, float|int|string> $settings
*
* @throws ClientExceptionInterface
* @throws PcreException
* @throws ServerError
*/
public function executeQuery(string $query, array $settings = []): void;

/**
* @param array<string, mixed> $params
* @param array<string, float|int|string> $settings
*
* @throws ClientExceptionInterface
* @throws PcreException
* @throws ServerError
* @throws UnsupportedValue
*/
public function executeQueryWithParams(string $query, array $params, array $settings = []): void;

Expand All @@ -24,6 +40,10 @@ public function executeQueryWithParams(string $query, array $params, array $sett
*
* @return O
*
* @throws ClientExceptionInterface
* @throws PcreException
* @throws ServerError
*
* @template O of Output
*/
public function select(string $query, Format $outputFormat, array $settings = []): Output;
Expand All @@ -35,6 +55,11 @@ public function select(string $query, Format $outputFormat, array $settings = []
*
* @return O
*
* @throws ClientExceptionInterface
* @throws PcreException
* @throws ServerError
* @throws UnsupportedValue
*
* @template O of Output
*/
public function selectWithParams(string $query, array $params, Format $outputFormat, array $settings = []): Output;
Expand All @@ -43,13 +68,21 @@ public function selectWithParams(string $query, array $params, Format $outputFor
* @param array<array<mixed>> $values
* @param array<string>|null $columns
* @param array<string, float|int|string> $settings
*
* @throws CannotInsert
* @throws ClientExceptionInterface
* @throws PcreException
* @throws ServerError
*/
public function insert(string $table, array $values, array|null $columns = null, array $settings = []): void;

/**
* @param array<string, float|int|string> $settings
* @param Format<O> $inputFormat
*
* @throws ClientExceptionInterface
* @throws ServerError
*
* @template O of Output
*/
public function insertWithFormat(string $table, Format $inputFormat, string $data, array $settings = []): void;
Expand Down
52 changes: 40 additions & 12 deletions src/Client/Http/RequestFactory.php
Expand Up @@ -4,11 +4,13 @@

namespace SimPod\ClickHouseClient\Client\Http;

use InvalidArgumentException;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface;
use Psr\Http\Message\UriInterface;
use RuntimeException;

use function http_build_query;
use function is_string;
Expand All @@ -17,12 +19,26 @@

final class RequestFactory
{
private UriInterface|null $uri;

/** @throws InvalidArgumentException */
public function __construct(
private RequestFactoryInterface $requestFactory,
private StreamFactoryInterface $streamFactory,
private UriFactoryInterface|null $uriFactory = null,
private UriInterface|string $uri = '',
UriFactoryInterface|null $uriFactory = null,
UriInterface|string $uri = '',
) {
if ($uriFactory === null && $uri === '') {
$uri = null;
} elseif (is_string($uri)) {
if ($uriFactory === null) {
throw new InvalidArgumentException('UriFactoryInterface is required when `$uri` is string');
}

$uri = $uriFactory->createUri($uri);
}

$this->uri = $uri;
}

public function prepareRequest(RequestOptions $requestOptions): RequestInterface
Expand All @@ -34,20 +50,32 @@ public function prepareRequest(RequestOptions $requestOptions): RequestInterface
PHP_QUERY_RFC3986,
);

if ($this->uriFactory === null) {
return $this->requestFactory->createRequest('POST', $query === '' ? '' : '?' . $query)
->withBody($this->streamFactory->createStream($requestOptions->sql));
$body = $this->streamFactory->createStream($requestOptions->sql);

if ($this->uri === null) {
$uri = $query === '' ? '' : '?' . $query;
} else {
$uriQuery = $this->uri->getQuery();
try {
$uri = $this->uri->withQuery($uriQuery . ($uriQuery !== '' && $query !== '' ? '&' : '') . $query);
} catch (InvalidArgumentException) {
$this->absurd();
}
}

$uri = $this->uri;
if (is_string($uri)) {
$uri = $this->uriFactory->createUri($uri);
$request = $this->requestFactory->createRequest('POST', $uri);
try {
$request = $request->withBody($body);
} catch (InvalidArgumentException) {
$this->absurd();
}

$uriQuery = $uri->getQuery();
$uri = $uri->withQuery($uriQuery . ($uriQuery !== '' && $query !== '' ? '&' : '') . $query);
return $request;
}

return $this->requestFactory->createRequest('POST', $uri)
->withBody($this->streamFactory->createStream($requestOptions->sql));
/** @psalm-return never */
private function absurd(): void
{
throw new RuntimeException('Called `absurd` function which should never be called');
}
}
38 changes: 27 additions & 11 deletions src/Client/PsrClickHouseAsyncClient.php
Expand Up @@ -5,6 +5,7 @@
namespace SimPod\ClickHouseClient\Client;

use DateTimeZone;
use Exception;
use GuzzleHttp\Promise\Create;
use GuzzleHttp\Promise\PromiseInterface;
use Http\Client\HttpAsyncClient;
Expand All @@ -31,6 +32,11 @@ public function __construct(
$this->sqlFactory = new SqlFactory(new ValueFormatter($clickHouseTimeZone));
}

/**
* {@inheritDoc}
*
* @throws Exception
*/
public function select(string $sql, Format $outputFormat, array $settings = []): PromiseInterface
{
$formatClause = $outputFormat::toSql();
Expand All @@ -45,6 +51,11 @@ public function select(string $sql, Format $outputFormat, array $settings = []):
);
}

/**
* {@inheritDoc}
*
* @throws Exception
*/
public function selectWithParams(
string $sql,
array $params,
Expand All @@ -61,6 +72,8 @@ public function selectWithParams(
/**
* @param array<string, float|int|string> $settings
* @param (callable(ResponseInterface):mixed)|null $processResponse
*
* @throws Exception
*/
private function executeRequest(
string $sql,
Expand All @@ -75,18 +88,21 @@ private function executeRequest(
),
);

return Create::promiseFor($this->asyncClient->sendAsyncRequest($request))->then(
static function (ResponseInterface $response) use ($processResponse) {
if ($response->getStatusCode() !== 200) {
throw ServerError::fromResponse($response);
}
return Create::promiseFor(
$this->asyncClient->sendAsyncRequest($request),
)
->then(
static function (ResponseInterface $response) use ($processResponse) {
if ($response->getStatusCode() !== 200) {
throw ServerError::fromResponse($response);
}

if ($processResponse === null) {
return $response;
}
if ($processResponse === null) {
return $response;
}

return $processResponse($response);
},
);
return $processResponse($response);
},
);
}
}
8 changes: 7 additions & 1 deletion src/Client/PsrClickHouseClient.php
Expand Up @@ -5,6 +5,7 @@
namespace SimPod\ClickHouseClient\Client;

use DateTimeZone;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\ResponseInterface;
use SimPod\ClickHouseClient\Client\Http\RequestFactory;
Expand Down Expand Up @@ -128,7 +129,12 @@ public function insertWithFormat(string $table, Format $inputFormat, string $dat
);
}

/** @param array<string, float|int|string> $settings */
/**
* @param array<string, float|int|string> $settings
*
* @throws ServerError
* @throws ClientExceptionInterface
*/
private function executeRequest(string $sql, array $settings = []): ResponseInterface
{
$request = $this->requestFactory->prepareRequest(
Expand Down
2 changes: 2 additions & 0 deletions src/Format/Json.php
Expand Up @@ -4,6 +4,7 @@

namespace SimPod\ClickHouseClient\Format;

use Safe\Exceptions\JsonException;
use SimPod\ClickHouseClient\Output\Output;

/**
Expand All @@ -12,6 +13,7 @@
*/
final class Json implements Format
{
/** @throws JsonException */
public static function output(string $contents): Output
{
return new \SimPod\ClickHouseClient\Output\Json($contents);
Expand Down
2 changes: 2 additions & 0 deletions src/Format/JsonCompact.php
Expand Up @@ -4,6 +4,7 @@

namespace SimPod\ClickHouseClient\Format;

use Safe\Exceptions\JsonException;
use SimPod\ClickHouseClient\Output\Output;

/**
Expand All @@ -12,6 +13,7 @@
*/
final class JsonCompact implements Format
{
/** @throws JsonException */
public static function output(string $contents): Output
{
return new \SimPod\ClickHouseClient\Output\JsonCompact($contents);
Expand Down
2 changes: 2 additions & 0 deletions src/Format/JsonEachRow.php
Expand Up @@ -4,6 +4,7 @@

namespace SimPod\ClickHouseClient\Format;

use Safe\Exceptions\JsonException;
use SimPod\ClickHouseClient\Output\Output;

/**
Expand All @@ -12,6 +13,7 @@
*/
final class JsonEachRow implements Format
{
/** @throws JsonException */
public static function output(string $contents): Output
{
return new \SimPod\ClickHouseClient\Output\JsonEachRow($contents);
Expand Down
3 changes: 3 additions & 0 deletions src/Output/Json.php
Expand Up @@ -4,6 +4,8 @@

namespace SimPod\ClickHouseClient\Output;

use Safe\Exceptions\JsonException;

use function Safe\json_decode;

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

/** @throws JsonException */
public function __construct(string $contentsJson)
{
// phpcs:ignore SlevomatCodingStandard.Files.LineLength.LineTooLong
Expand Down
3 changes: 3 additions & 0 deletions src/Output/JsonCompact.php
Expand Up @@ -4,6 +4,8 @@

namespace SimPod\ClickHouseClient\Output;

use Safe\Exceptions\JsonException;

use function Safe\json_decode;

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

/** @throws JsonException */
public function __construct(string $contentsJson)
{
/**
Expand Down
3 changes: 3 additions & 0 deletions src/Output/JsonEachRow.php
Expand Up @@ -4,6 +4,8 @@

namespace SimPod\ClickHouseClient\Output;

use Safe\Exceptions\JsonException;

use function Safe\json_decode;
use function sprintf;
use function str_replace;
Expand All @@ -18,6 +20,7 @@ final class JsonEachRow implements Output
/** @var list<T> */
public array $data;

/** @throws JsonException */
public function __construct(string $contentsJson)
{
/**
Expand Down

0 comments on commit fbf718a

Please sign in to comment.