From b8969bb06c1b5c4575ad2f0a25af16f2bb0f5c5f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 04:22:20 +0000 Subject: [PATCH 01/29] feat(client): improve error handling --- README.md | 4 ++-- src/Core/BaseClient.php | 8 ++++---- src/Errors/APIError.php | 2 +- src/Errors/APIStatusError.php | 26 +++++++++++++++++--------- src/Errors/Error.php | 2 +- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index eb8a46a..699909c 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,8 @@ $client = new Client(apiKey: getenv("CAS_PARSER_API_KEY") ?: "My API Key"); $params = CasParserSmartParseParams::with( password: "ABCDF", pdfURL: "https://your-cas-pdf-url-here.com" ); -$unifiedResponse = $client->casParser->smartParse($params); +$unifiedResponse = $client->casParser->smartParse($params); var_dump($unifiedResponse->demat_accounts); ``` @@ -77,7 +77,7 @@ try { echo "A 429 status code was received; we should back off a bit.", PHP_EOL; } catch (APIStatusError $e) { echo "Another non-200-range status code was received", PHP_EOL; - var_dump($e->status); + echo $e->getMessage(); } ``` diff --git a/src/Core/BaseClient.php b/src/Core/BaseClient.php index a6b3e04..afc48be 100644 --- a/src/Core/BaseClient.php +++ b/src/Core/BaseClient.php @@ -26,7 +26,7 @@ class BaseClient protected RequestFactoryInterface $requestFactory; - protected ClientInterface $requester; + protected ClientInterface $transporter; /** * @param array|string> $headers @@ -41,7 +41,7 @@ public function __construct( $this->requestFactory = Psr17FactoryDiscovery::findRequestFactory(); $this->baseUrl = $this->uriFactory->createUri($baseUrl); - $this->requester = Psr18ClientDiscovery::find(); + $this->transporter = Psr18ClientDiscovery::find(); } /** @@ -158,7 +158,7 @@ protected function sendRequest( int $redirectCount, ): ResponseInterface { $req = Util::withSetBody($this->streamFactory, req: $req, body: $data); - $rsp = $this->requester->sendRequest($req); + $rsp = $this->transporter->sendRequest($req); $code = $rsp->getStatusCode(); if ($code >= 300 && $code < 400) { @@ -172,7 +172,7 @@ protected function sendRequest( } if ($code >= 400 && $code < 500) { - throw APIStatusError::from(null, request: $req, response: $rsp); + throw APIStatusError::from(request: $req, response: $rsp); } if ($code >= 500 && $retryCount < $opts->maxRetries) { diff --git a/src/Errors/APIError.php b/src/Errors/APIError.php index 829d541..9ae459e 100644 --- a/src/Errors/APIError.php +++ b/src/Errors/APIError.php @@ -18,6 +18,6 @@ public function __construct( ?\Throwable $previous = null, string $message = '', ) { - parent::__construct(message: 'response: '.$message.PHP_EOL.'request: '.$request->getBody()->__toString(), previous: $previous); + parent::__construct(message: $message, previous: $previous); } } diff --git a/src/Errors/APIStatusError.php b/src/Errors/APIStatusError.php index cdb1ec7..4c8c721 100644 --- a/src/Errors/APIStatusError.php +++ b/src/Errors/APIStatusError.php @@ -2,9 +2,9 @@ namespace CasParser\Errors; -use CasParser\Core\Util; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamInterface; class APIStatusError extends APIError { @@ -14,7 +14,6 @@ class APIStatusError extends APIError public ?int $status; public function __construct( - public mixed $body, public RequestInterface $request, ResponseInterface $response, ?\Throwable $previous = null, @@ -22,15 +21,19 @@ public function __construct( ) { $this->response = $response; $this->status = $response->getStatusCode(); - $message |= json_encode( - ['status' => $this->status, 'body' => $body], - flags: Util::JSON_ENCODE_FLAGS, - ); - parent::__construct(request: $request, message: $message, previous: $previous); + + $summary = 'Status: '.$this->status.PHP_EOL + .'Response Body: '.self::fmtBody($response->getBody()).PHP_EOL + .'Request Body: '.self::fmtBody($request->getBody()).PHP_EOL; + + if ('' != $message) { + $summary .= $message.PHP_EOL.$summary; + } + + parent::__construct(request: $request, message: $summary, previous: $previous); } public static function from( - mixed $body, RequestInterface $request, ResponseInterface $response ): self { @@ -48,6 +51,11 @@ public static function from( default => APIStatusError::class }; - return new $cls(body: $body, request: $request, response: $response); + return new $cls(request: $request, response: $response); + } + + private static function fmtBody(StreamInterface $body): string + { + return json_encode(json_decode($body->__toString() ?: ''), JSON_PRETTY_PRINT) ?: ''; } } diff --git a/src/Errors/Error.php b/src/Errors/Error.php index 1fe7489..0e34a88 100644 --- a/src/Errors/Error.php +++ b/src/Errors/Error.php @@ -9,6 +9,6 @@ class Error extends \Exception public function __construct(string $message, int $code = 0, ?\Throwable $previous = null) { - parent::__construct($this::DESC.' '.$message, $code, $previous); + parent::__construct($this::DESC.PHP_EOL.$message, $code, $previous); } } From 8a3649ac38d283238cb78f183d88388d2220350f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 04:32:28 +0000 Subject: [PATCH 02/29] feat(client): add streaming --- src/Core/Adapters/GeneratorStream.php | 107 ------------------------- src/Core/Contracts/CloseableStream.php | 19 +++++ src/Core/Util.php | 4 +- 3 files changed, 21 insertions(+), 109 deletions(-) delete mode 100644 src/Core/Adapters/GeneratorStream.php create mode 100644 src/Core/Contracts/CloseableStream.php diff --git a/src/Core/Adapters/GeneratorStream.php b/src/Core/Adapters/GeneratorStream.php deleted file mode 100644 index b909028..0000000 --- a/src/Core/Adapters/GeneratorStream.php +++ /dev/null @@ -1,107 +0,0 @@ - $st - */ - public function __construct(private \Generator $st) {} - - public function __toString(): string - { - try { - return $this->getContents(); - } catch (\Throwable) { - return ''; - } - } - - public function getSize(): ?int - { - return null; - } - - public function eof(): bool - { - return !strlen($this->buf) && !$this->st->valid(); - } - - public function close(): void - { - $ex = new class() extends \Exception {}; - - try { - $this->st->throw(new $ex); - } catch (\Throwable) { - } - } - - public function detach(): null - { - $this->buf = ''; - $this->close(); - - return null; - } - - public function tell(): int - { - return $this->pos; - } - - public function rewind(): void - { - $this->buf = ''; - $this->st->rewind(); - } - - public function isSeekable(): bool - { - return false; - } - - public function seek(int $offset, int $whence = SEEK_SET): void {} - - public function isWritable(): bool - { - return false; - } - - public function write(string $string): int - { - return 0; - } - - public function isReadable(): bool - { - return !$this->eof(); - } - - public function read(int $length): string - { - return ''; - } - - public function getContents(): string - { - foreach ($this->st as $chunk) { - $this->buf .= $chunk; - } - - return $this->buf; - } - - public function getMetadata(?string $key = null): mixed - { - return null; - } -} diff --git a/src/Core/Contracts/CloseableStream.php b/src/Core/Contracts/CloseableStream.php new file mode 100644 index 0000000..ef685cf --- /dev/null +++ b/src/Core/Contracts/CloseableStream.php @@ -0,0 +1,19 @@ + + */ +interface CloseableStream extends \IteratorAggregate +{ + /** + * Manually force the stream to close early. + * Iterating through will automatically close as well. + */ + public function close(): void; +} diff --git a/src/Core/Util.php b/src/Core/Util.php index 738ab26..6ec7f5d 100644 --- a/src/Core/Util.php +++ b/src/Core/Util.php @@ -265,13 +265,13 @@ public static function decodeLines(\Iterator $stream): \Iterator /** * @param \Iterator $lines * - * @return \Iterator< + * @return \Generator< * array{ * event?: null|string, data?: null|string, id?: null|string, retry?: null|int * }, * > */ - public static function decodeSSE(\Iterator $lines): \Iterator + public static function decodeSSE(\Iterator $lines): \Generator { $blank = ['event' => null, 'data' => null, 'id' => null, 'retry' => null]; $acc = []; From f977b9a00b4f72c4d0add8e637baf699339b3707 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 04:38:12 +0000 Subject: [PATCH 03/29] feat(client): use named parameters in methods --- README.md | 54 ++++++++++----------- src/CasGenerator/CasGeneratorService.php | 32 ++++++++----- src/CasParser/CasParserService.php | 60 ++++++++++++++---------- src/Contracts/CasGeneratorContract.php | 22 +++++---- src/Contracts/CasParserContract.php | 44 +++++++++-------- tests/Resources/CasGeneratorTest.php | 7 +-- tests/Resources/CasParserTest.php | 16 ++----- 7 files changed, 123 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index 699909c..2fcf1c2 100644 --- a/README.md +++ b/README.md @@ -39,22 +39,30 @@ To use this package, install via Composer by adding the following to your applic ## Usage +This library uses named parameters to specify optional arguments. +Parameters with a default value must be set by name. + ```php casParser->smartParse( password: "ABCDF", pdfURL: "https://your-cas-pdf-url-here.com" ); -$unifiedResponse = $client->casParser->smartParse($params); var_dump($unifiedResponse->demat_accounts); ``` +## Value Objects + +It is recommended to use the `with` constructor `Dog::with(name: "Joey")` +and named parameters to initialize value objects. + +However builders are provided as well `(new Dog)->withName("Joey")`. + ### Handling errors When the library is unable to connect to the API, or if the API returns a non-success status code (i.e., 4xx or 5xx response), a subclass of `CasParser\Errors\APIError` will be thrown: @@ -62,22 +70,18 @@ When the library is unable to connect to the API, or if the API returns a non-su ```php casParser->smartParse($params); + $unifiedResponse = $client->casParser->smartParse(); } catch (APIConnectionError $e) { - echo "The server could not be reached", PHP_EOL; - var_dump($e->getPrevious()); + echo "The server could not be reached", PHP_EOL; + var_dump($e->getPrevious()); } catch (RateLimitError $_) { - echo "A 429 status code was received; we should back off a bit.", PHP_EOL; + echo "A 429 status code was received; we should back off a bit.", PHP_EOL; } catch (APIStatusError $e) { - echo "Another non-200-range status code was received", PHP_EOL; - echo $e->getMessage(); + echo "Another non-200-range status code was received", PHP_EOL; + echo $e->getMessage(); } ``` @@ -110,17 +114,16 @@ You can use the `max_retries` option to configure or disable this: use CasParser\Client; use CasParser\RequestOptions; -use CasParser\CasParser\CasParserSmartParseParams; // Configure the default for all requests: $client = new Client(maxRetries: 0); -$params = CasParserSmartParseParams::with( - password: "ABCDF", pdfURL: "https://you-cas-pdf-url-here.com" -); -// Or, configure per-request:$result = $client - ->casParser - ->smartParse($params, new RequestOptions(maxRetries: 5)); +// Or, configure per-request: +$result = $client->casParser->smartParse( + password: "ABCDF", + pdfURL: "https://you-cas-pdf-url-here.com", + new RequestOptions(maxRetries: 5), +); ``` ## Advanced concepts @@ -137,15 +140,10 @@ Note: the `extra_` parameters of the same name overrides the documented paramete casParser - ->smartParse( - $params, +$unifiedResponse = $client->casParser->smartParse( + password: "ABCDF", + pdfURL: "https://you-cas-pdf-url-here.com", new RequestOptions( extraQueryParams: ["my_query_parameter" => "value"], extraBodyParams: ["my_body_parameter" => "value"], diff --git a/src/CasGenerator/CasGeneratorService.php b/src/CasGenerator/CasGeneratorService.php index e5edde5..c54f077 100644 --- a/src/CasGenerator/CasGeneratorService.php +++ b/src/CasGenerator/CasGeneratorService.php @@ -19,22 +19,32 @@ public function __construct(private Client $client) {} * This endpoint generates CAS (Consolidated Account Statement) documents by submitting a mailback request to the specified CAS authority. * Currently only supports KFintech, with plans to support CAMS, CDSL, and NSDL in the future. * - * @param array{ - * email: string, - * fromDate: string, - * password: string, - * toDate: string, - * casAuthority?: CasAuthority::*, - * panNo?: string, - * }|CasGeneratorGenerateCasParams $params + * @param string $email Email address to receive the CAS document + * @param string $fromDate Start date for the CAS period (format YYYY-MM-DD) + * @param string $password Password to protect the generated CAS PDF + * @param string $toDate End date for the CAS period (format YYYY-MM-DD) + * @param CasAuthority::* $casAuthority CAS authority to generate the document from (currently only kfintech is supported) + * @param string $panNo PAN number (optional for some CAS authorities) */ public function generateCas( - array|CasGeneratorGenerateCasParams $params, + $email, + $fromDate, + $password, + $toDate, + $casAuthority = null, + $panNo = null, ?RequestOptions $requestOptions = null, ): CasGeneratorGenerateCasResponse { [$parsed, $options] = CasGeneratorGenerateCasParams::parseRequest( - $params, - $requestOptions + [ + 'email' => $email, + 'fromDate' => $fromDate, + 'password' => $password, + 'toDate' => $toDate, + 'casAuthority' => $casAuthority, + 'panNo' => $panNo, + ], + $requestOptions, ); $resp = $this->client->request( method: 'post', diff --git a/src/CasParser/CasParserService.php b/src/CasParser/CasParserService.php index 318face..8ffd4e9 100644 --- a/src/CasParser/CasParserService.php +++ b/src/CasParser/CasParserService.php @@ -17,17 +17,19 @@ public function __construct(private Client $client) {} * This endpoint specifically parses CAMS/KFintech CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from CAMS or KFintech. * - * @param array{ - * password?: string, pdfFile?: string, pdfURL?: string - * }|CasParserCamsKfintechParams $params + * @param string $password Password for the PDF file (if required) + * @param string $pdfFile Base64 encoded CAS PDF file + * @param string $pdfURL URL to the CAS PDF file */ public function camsKfintech( - array|CasParserCamsKfintechParams $params, + $password = null, + $pdfFile = null, + $pdfURL = null, ?RequestOptions $requestOptions = null, ): UnifiedResponse { [$parsed, $options] = CasParserCamsKfintechParams::parseRequest( - $params, - $requestOptions + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], + $requestOptions, ); $resp = $this->client->request( method: 'post', @@ -44,17 +46,19 @@ public function camsKfintech( * This endpoint specifically parses CDSL CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from CDSL. * - * @param array{ - * password?: string, pdfFile?: string, pdfURL?: string - * }|CasParserCdslParams $params + * @param string $password Password for the PDF file (if required) + * @param string $pdfFile Base64 encoded CAS PDF file + * @param string $pdfURL URL to the CAS PDF file */ public function cdsl( - array|CasParserCdslParams $params, - ?RequestOptions $requestOptions = null + $password = null, + $pdfFile = null, + $pdfURL = null, + ?RequestOptions $requestOptions = null, ): UnifiedResponse { [$parsed, $options] = CasParserCdslParams::parseRequest( - $params, - $requestOptions + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], + $requestOptions, ); $resp = $this->client->request( method: 'post', @@ -71,17 +75,19 @@ public function cdsl( * This endpoint specifically parses NSDL CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from NSDL. * - * @param array{ - * password?: string, pdfFile?: string, pdfURL?: string - * }|CasParserNsdlParams $params + * @param string $password Password for the PDF file (if required) + * @param string $pdfFile Base64 encoded CAS PDF file + * @param string $pdfURL URL to the CAS PDF file */ public function nsdl( - array|CasParserNsdlParams $params, - ?RequestOptions $requestOptions = null + $password = null, + $pdfFile = null, + $pdfURL = null, + ?RequestOptions $requestOptions = null, ): UnifiedResponse { [$parsed, $options] = CasParserNsdlParams::parseRequest( - $params, - $requestOptions + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], + $requestOptions, ); $resp = $this->client->request( method: 'post', @@ -98,17 +104,19 @@ public function nsdl( * This endpoint parses CAS (Consolidated Account Statement) PDF files from NSDL, CDSL, or CAMS/KFintech and returns data in a unified format. * It auto-detects the CAS type and transforms the data into a consistent structure regardless of the source. * - * @param array{ - * password?: string, pdfFile?: string, pdfURL?: string - * }|CasParserSmartParseParams $params + * @param string $password Password for the PDF file (if required) + * @param string $pdfFile Base64 encoded CAS PDF file + * @param string $pdfURL URL to the CAS PDF file */ public function smartParse( - array|CasParserSmartParseParams $params, + $password = null, + $pdfFile = null, + $pdfURL = null, ?RequestOptions $requestOptions = null, ): UnifiedResponse { [$parsed, $options] = CasParserSmartParseParams::parseRequest( - $params, - $requestOptions + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], + $requestOptions, ); $resp = $this->client->request( method: 'post', diff --git a/src/Contracts/CasGeneratorContract.php b/src/Contracts/CasGeneratorContract.php index 5b7a6ec..09ebc93 100644 --- a/src/Contracts/CasGeneratorContract.php +++ b/src/Contracts/CasGeneratorContract.php @@ -4,7 +4,6 @@ namespace CasParser\Contracts; -use CasParser\CasGenerator\CasGeneratorGenerateCasParams; use CasParser\CasGenerator\CasGeneratorGenerateCasParams\CasAuthority; use CasParser\RequestOptions; use CasParser\Responses\CasGenerator\CasGeneratorGenerateCasResponse; @@ -12,17 +11,20 @@ interface CasGeneratorContract { /** - * @param array{ - * email: string, - * fromDate: string, - * password: string, - * toDate: string, - * casAuthority?: CasAuthority::*, - * panNo?: string, - * }|CasGeneratorGenerateCasParams $params + * @param string $email Email address to receive the CAS document + * @param string $fromDate Start date for the CAS period (format YYYY-MM-DD) + * @param string $password Password to protect the generated CAS PDF + * @param string $toDate End date for the CAS period (format YYYY-MM-DD) + * @param CasAuthority::* $casAuthority CAS authority to generate the document from (currently only kfintech is supported) + * @param string $panNo PAN number (optional for some CAS authorities) */ public function generateCas( - array|CasGeneratorGenerateCasParams $params, + $email, + $fromDate, + $password, + $toDate, + $casAuthority = null, + $panNo = null, ?RequestOptions $requestOptions = null, ): CasGeneratorGenerateCasResponse; } diff --git a/src/Contracts/CasParserContract.php b/src/Contracts/CasParserContract.php index b8c94bd..e30bd89 100644 --- a/src/Contracts/CasParserContract.php +++ b/src/Contracts/CasParserContract.php @@ -4,52 +4,56 @@ namespace CasParser\Contracts; -use CasParser\CasParser\CasParserCamsKfintechParams; -use CasParser\CasParser\CasParserCdslParams; -use CasParser\CasParser\CasParserNsdlParams; -use CasParser\CasParser\CasParserSmartParseParams; use CasParser\CasParser\UnifiedResponse; use CasParser\RequestOptions; interface CasParserContract { /** - * @param array{ - * password?: string, pdfFile?: string, pdfURL?: string - * }|CasParserCamsKfintechParams $params + * @param string $password Password for the PDF file (if required) + * @param string $pdfFile Base64 encoded CAS PDF file + * @param string $pdfURL URL to the CAS PDF file */ public function camsKfintech( - array|CasParserCamsKfintechParams $params, + $password = null, + $pdfFile = null, + $pdfURL = null, ?RequestOptions $requestOptions = null, ): UnifiedResponse; /** - * @param array{ - * password?: string, pdfFile?: string, pdfURL?: string - * }|CasParserCdslParams $params + * @param string $password Password for the PDF file (if required) + * @param string $pdfFile Base64 encoded CAS PDF file + * @param string $pdfURL URL to the CAS PDF file */ public function cdsl( - array|CasParserCdslParams $params, + $password = null, + $pdfFile = null, + $pdfURL = null, ?RequestOptions $requestOptions = null, ): UnifiedResponse; /** - * @param array{ - * password?: string, pdfFile?: string, pdfURL?: string - * }|CasParserNsdlParams $params + * @param string $password Password for the PDF file (if required) + * @param string $pdfFile Base64 encoded CAS PDF file + * @param string $pdfURL URL to the CAS PDF file */ public function nsdl( - array|CasParserNsdlParams $params, + $password = null, + $pdfFile = null, + $pdfURL = null, ?RequestOptions $requestOptions = null, ): UnifiedResponse; /** - * @param array{ - * password?: string, pdfFile?: string, pdfURL?: string - * }|CasParserSmartParseParams $params + * @param string $password Password for the PDF file (if required) + * @param string $pdfFile Base64 encoded CAS PDF file + * @param string $pdfURL URL to the CAS PDF file */ public function smartParse( - array|CasParserSmartParseParams $params, + $password = null, + $pdfFile = null, + $pdfURL = null, ?RequestOptions $requestOptions = null, ): UnifiedResponse; } diff --git a/tests/Resources/CasGeneratorTest.php b/tests/Resources/CasGeneratorTest.php index 32e6826..47c61b2 100644 --- a/tests/Resources/CasGeneratorTest.php +++ b/tests/Resources/CasGeneratorTest.php @@ -2,7 +2,6 @@ namespace Tests\Resources; -use CasParser\CasGenerator\CasGeneratorGenerateCasParams; use CasParser\Client; use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\Attributes\Test; @@ -34,13 +33,12 @@ public function testGenerateCas(): void $this->markTestSkipped('Prism tests are disabled'); } - $params = CasGeneratorGenerateCasParams::with( + $result = $this->client->casGenerator->generateCas( email: 'user@example.com', fromDate: '2023-01-01', password: 'Abcdefghi12$', toDate: '2023-12-31', ); - $result = $this->client->casGenerator->generateCas($params); $this->assertTrue(true); // @phpstan-ignore-line } @@ -52,7 +50,7 @@ public function testGenerateCasWithOptionalParams(): void $this->markTestSkipped('Prism tests are disabled'); } - $params = CasGeneratorGenerateCasParams::with( + $result = $this->client->casGenerator->generateCas( email: 'user@example.com', fromDate: '2023-01-01', password: 'Abcdefghi12$', @@ -60,7 +58,6 @@ public function testGenerateCasWithOptionalParams(): void casAuthority: 'kfintech', panNo: 'ABCDE1234F', ); - $result = $this->client->casGenerator->generateCas($params); $this->assertTrue(true); // @phpstan-ignore-line } diff --git a/tests/Resources/CasParserTest.php b/tests/Resources/CasParserTest.php index 61d9206..feb5aae 100644 --- a/tests/Resources/CasParserTest.php +++ b/tests/Resources/CasParserTest.php @@ -2,10 +2,6 @@ namespace Tests\Resources; -use CasParser\CasParser\CasParserCamsKfintechParams; -use CasParser\CasParser\CasParserCdslParams; -use CasParser\CasParser\CasParserNsdlParams; -use CasParser\CasParser\CasParserSmartParseParams; use CasParser\Client; use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\Attributes\Test; @@ -37,8 +33,7 @@ public function testCamsKfintech(): void $this->markTestSkipped('Prism tests are disabled'); } - $params = (new CasParserCamsKfintechParams); - $result = $this->client->casParser->camsKfintech($params); + $result = $this->client->casParser->camsKfintech(); $this->assertTrue(true); // @phpstan-ignore-line } @@ -50,8 +45,7 @@ public function testCdsl(): void $this->markTestSkipped('Prism tests are disabled'); } - $params = (new CasParserCdslParams); - $result = $this->client->casParser->cdsl($params); + $result = $this->client->casParser->cdsl(); $this->assertTrue(true); // @phpstan-ignore-line } @@ -63,8 +57,7 @@ public function testNsdl(): void $this->markTestSkipped('Prism tests are disabled'); } - $params = (new CasParserNsdlParams); - $result = $this->client->casParser->nsdl($params); + $result = $this->client->casParser->nsdl(); $this->assertTrue(true); // @phpstan-ignore-line } @@ -76,8 +69,7 @@ public function testSmartParse(): void $this->markTestSkipped('Prism tests are disabled'); } - $params = (new CasParserSmartParseParams); - $result = $this->client->casParser->smartParse($params); + $result = $this->client->casParser->smartParse(); $this->assertTrue(true); // @phpstan-ignore-line } From ac665402fa3edc84c13de25248d0b901e54aaab5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 04:12:54 +0000 Subject: [PATCH 04/29] chore: readme improvements --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2fcf1c2..965e6c5 100644 --- a/README.md +++ b/README.md @@ -58,10 +58,10 @@ var_dump($unifiedResponse->demat_accounts); ## Value Objects -It is recommended to use the `with` constructor `Dog::with(name: "Joey")` +It is recommended to use the static `with` constructor `Dog::with(name: "Joey")` and named parameters to initialize value objects. -However builders are provided as well `(new Dog)->withName("Joey")`. +However, builders are also provided `(new Dog)->withName("Joey")`. ### Handling errors From d8b47a27256c7429058623b48624698804778320 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 04:21:20 +0000 Subject: [PATCH 05/29] feat(php): rename internal types --- README.md | 2 +- .../CasGeneratorGenerateCasParams.php | 8 +++---- .../CasAuthority.php | 4 ++-- src/CasParser/CasParserCamsKfintechParams.php | 8 +++---- src/CasParser/CasParserCdslParams.php | 8 +++---- src/CasParser/CasParserNsdlParams.php | 8 +++---- src/CasParser/CasParserSmartParseParams.php | 8 +++---- src/CasParser/UnifiedResponse.php | 4 ++-- .../UnifiedResponse/DematAccount.php | 4 ++-- .../DematAccount/AdditionalInfo.php | 4 ++-- .../DematAccount/DematType.php | 4 ++-- .../UnifiedResponse/DematAccount/Holdings.php | 4 ++-- .../DematAccount/Holdings/Aif.php | 4 ++-- .../DematAccount/Holdings/CorporateBond.php | 4 ++-- .../DematAccount/Holdings/DematMutualFund.php | 4 ++-- .../DematAccount/Holdings/Equity.php | 4 ++-- .../Holdings/GovernmentSecurity.php | 4 ++-- src/CasParser/UnifiedResponse/Insurance.php | 4 ++-- .../Insurance/LifeInsurancePolicy.php | 4 ++-- src/CasParser/UnifiedResponse/Investor.php | 4 ++-- src/CasParser/UnifiedResponse/Meta.php | 4 ++-- .../UnifiedResponse/Meta/CasType.php | 4 ++-- .../UnifiedResponse/Meta/StatementPeriod.php | 4 ++-- src/CasParser/UnifiedResponse/MutualFund.php | 4 ++-- .../MutualFund/AdditionalInfo.php | 4 ++-- .../UnifiedResponse/MutualFund/Scheme.php | 4 ++-- .../MutualFund/Scheme/AdditionalInfo.php | 4 ++-- .../MutualFund/Scheme/Gain.php | 4 ++-- .../MutualFund/Scheme/Transaction.php | 4 ++-- .../MutualFund/Scheme/Type.php | 4 ++-- src/CasParser/UnifiedResponse/Summary.php | 4 ++-- .../UnifiedResponse/Summary/Accounts.php | 4 ++-- .../Summary/Accounts/Demat.php | 4 ++-- .../Summary/Accounts/Insurance.php | 4 ++-- .../Summary/Accounts/MutualFunds.php | 4 ++-- src/Core/Concerns/Page.php | 22 ------------------- src/Core/Concerns/{Enum.php => SdkEnum.php} | 5 ++++- src/Core/Concerns/{Model.php => SdkModel.php} | 2 +- .../Concerns/{Params.php => SdkParams.php} | 2 +- src/Core/Concerns/{Union.php => SdkUnion.php} | 2 +- src/Core/Contracts/BasePage.php | 16 +++++++++----- src/Core/Pagination/AbstractPage.php | 4 ++-- .../CasGeneratorGenerateCasResponse.php | 4 ++-- tests/Core/TestModel.php | 4 ++-- 44 files changed, 103 insertions(+), 116 deletions(-) delete mode 100644 src/Core/Concerns/Page.php rename src/Core/Concerns/{Enum.php => SdkEnum.php} (95%) rename src/Core/Concerns/{Model.php => SdkModel.php} (99%) rename src/Core/Concerns/{Params.php => SdkParams.php} (98%) rename src/Core/Concerns/{Union.php => SdkUnion.php} (98%) diff --git a/README.md b/README.md index 965e6c5..b910e36 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ $unifiedResponse = $client->casParser->smartParse( var_dump($unifiedResponse->demat_accounts); ``` -## Value Objects +### Value Objects It is recommended to use the static `with` constructor `Dog::with(name: "Joey")` and named parameters to initialize value objects. diff --git a/src/CasGenerator/CasGeneratorGenerateCasParams.php b/src/CasGenerator/CasGeneratorGenerateCasParams.php index 5de37b0..ba6bf84 100644 --- a/src/CasGenerator/CasGeneratorGenerateCasParams.php +++ b/src/CasGenerator/CasGeneratorGenerateCasParams.php @@ -6,8 +6,8 @@ use CasParser\CasGenerator\CasGeneratorGenerateCasParams\CasAuthority; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; -use CasParser\Core\Concerns\Params; +use CasParser\Core\Concerns\SdkModel; +use CasParser\Core\Concerns\SdkParams; use CasParser\Core\Contracts\BaseModel; /** @@ -25,8 +25,8 @@ */ final class CasGeneratorGenerateCasParams implements BaseModel { - use Model; - use Params; + use SdkModel; + use SdkParams; /** * Email address to receive the CAS document. diff --git a/src/CasGenerator/CasGeneratorGenerateCasParams/CasAuthority.php b/src/CasGenerator/CasGeneratorGenerateCasParams/CasAuthority.php index 160ff36..eb2eef7 100644 --- a/src/CasGenerator/CasGeneratorGenerateCasParams/CasAuthority.php +++ b/src/CasGenerator/CasGeneratorGenerateCasParams/CasAuthority.php @@ -4,7 +4,7 @@ namespace CasParser\CasGenerator\CasGeneratorGenerateCasParams; -use CasParser\Core\Concerns\Enum; +use CasParser\Core\Concerns\SdkEnum; use CasParser\Core\Conversion\Contracts\ConverterSource; /** @@ -14,7 +14,7 @@ */ final class CasAuthority implements ConverterSource { - use Enum; + use SdkEnum; public const KFINTECH = 'kfintech'; diff --git a/src/CasParser/CasParserCamsKfintechParams.php b/src/CasParser/CasParserCamsKfintechParams.php index dc45ccd..71d566f 100644 --- a/src/CasParser/CasParserCamsKfintechParams.php +++ b/src/CasParser/CasParserCamsKfintechParams.php @@ -5,8 +5,8 @@ namespace CasParser\CasParser; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; -use CasParser\Core\Concerns\Params; +use CasParser\Core\Concerns\SdkModel; +use CasParser\Core\Concerns\SdkParams; use CasParser\Core\Contracts\BaseModel; /** @@ -19,8 +19,8 @@ */ final class CasParserCamsKfintechParams implements BaseModel { - use Model; - use Params; + use SdkModel; + use SdkParams; /** * Password for the PDF file (if required). diff --git a/src/CasParser/CasParserCdslParams.php b/src/CasParser/CasParserCdslParams.php index 7496a29..5b74efa 100644 --- a/src/CasParser/CasParserCdslParams.php +++ b/src/CasParser/CasParserCdslParams.php @@ -5,8 +5,8 @@ namespace CasParser\CasParser; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; -use CasParser\Core\Concerns\Params; +use CasParser\Core\Concerns\SdkModel; +use CasParser\Core\Concerns\SdkParams; use CasParser\Core\Contracts\BaseModel; /** @@ -19,8 +19,8 @@ */ final class CasParserCdslParams implements BaseModel { - use Model; - use Params; + use SdkModel; + use SdkParams; /** * Password for the PDF file (if required). diff --git a/src/CasParser/CasParserNsdlParams.php b/src/CasParser/CasParserNsdlParams.php index 9c782fe..aef081e 100644 --- a/src/CasParser/CasParserNsdlParams.php +++ b/src/CasParser/CasParserNsdlParams.php @@ -5,8 +5,8 @@ namespace CasParser\CasParser; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; -use CasParser\Core\Concerns\Params; +use CasParser\Core\Concerns\SdkModel; +use CasParser\Core\Concerns\SdkParams; use CasParser\Core\Contracts\BaseModel; /** @@ -19,8 +19,8 @@ */ final class CasParserNsdlParams implements BaseModel { - use Model; - use Params; + use SdkModel; + use SdkParams; /** * Password for the PDF file (if required). diff --git a/src/CasParser/CasParserSmartParseParams.php b/src/CasParser/CasParserSmartParseParams.php index 6626658..44016e2 100644 --- a/src/CasParser/CasParserSmartParseParams.php +++ b/src/CasParser/CasParserSmartParseParams.php @@ -5,8 +5,8 @@ namespace CasParser\CasParser; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; -use CasParser\Core\Concerns\Params; +use CasParser\Core\Concerns\SdkModel; +use CasParser\Core\Concerns\SdkParams; use CasParser\Core\Contracts\BaseModel; /** @@ -19,8 +19,8 @@ */ final class CasParserSmartParseParams implements BaseModel { - use Model; - use Params; + use SdkModel; + use SdkParams; /** * Password for the PDF file (if required). diff --git a/src/CasParser/UnifiedResponse.php b/src/CasParser/UnifiedResponse.php index b5736a5..1512127 100644 --- a/src/CasParser/UnifiedResponse.php +++ b/src/CasParser/UnifiedResponse.php @@ -11,7 +11,7 @@ use CasParser\CasParser\UnifiedResponse\MutualFund; use CasParser\CasParser\UnifiedResponse\Summary; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; use CasParser\Core\Conversion\ListOf; @@ -27,7 +27,7 @@ */ final class UnifiedResponse implements BaseModel { - use Model; + use SdkModel; /** @var null|list $dematAccounts */ #[Api( diff --git a/src/CasParser/UnifiedResponse/DematAccount.php b/src/CasParser/UnifiedResponse/DematAccount.php index 055c941..8042c37 100644 --- a/src/CasParser/UnifiedResponse/DematAccount.php +++ b/src/CasParser/UnifiedResponse/DematAccount.php @@ -8,7 +8,7 @@ use CasParser\CasParser\UnifiedResponse\DematAccount\DematType; use CasParser\CasParser\UnifiedResponse\DematAccount\Holdings; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -25,7 +25,7 @@ */ final class DematAccount implements BaseModel { - use Model; + use SdkModel; /** * Additional information specific to the demat account type. diff --git a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php index 41c11da..539d04e 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\DematAccount; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; use CasParser\Core\Conversion\ListOf; @@ -25,7 +25,7 @@ */ final class AdditionalInfo implements BaseModel { - use Model; + use SdkModel; /** * Beneficiary Owner status (CDSL). diff --git a/src/CasParser/UnifiedResponse/DematAccount/DematType.php b/src/CasParser/UnifiedResponse/DematAccount/DematType.php index 441209e..149bfb5 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/DematType.php +++ b/src/CasParser/UnifiedResponse/DematAccount/DematType.php @@ -4,7 +4,7 @@ namespace CasParser\CasParser\UnifiedResponse\DematAccount; -use CasParser\Core\Concerns\Enum; +use CasParser\Core\Concerns\SdkEnum; use CasParser\Core\Conversion\Contracts\ConverterSource; /** @@ -14,7 +14,7 @@ */ final class DematType implements ConverterSource { - use Enum; + use SdkEnum; public const NSDL = 'NSDL'; diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php index 61a4d5c..36587c5 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php @@ -10,7 +10,7 @@ use CasParser\CasParser\UnifiedResponse\DematAccount\Holdings\Equity; use CasParser\CasParser\UnifiedResponse\DematAccount\Holdings\GovernmentSecurity; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; use CasParser\Core\Conversion\ListOf; @@ -25,7 +25,7 @@ */ final class Holdings implements BaseModel { - use Model; + use SdkModel; /** @var null|list $aifs */ #[Api(type: new ListOf(Aif::class), optional: true)] diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php index cda73f1..205e809 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\DematAccount\Holdings; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -19,7 +19,7 @@ */ final class Aif implements BaseModel { - use Model; + use SdkModel; /** * Additional information specific to the AIF. diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php index 2d3da3e..10f5a73 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\DematAccount\Holdings; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -19,7 +19,7 @@ */ final class CorporateBond implements BaseModel { - use Model; + use SdkModel; /** * Additional information specific to the corporate bond. diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php index bc52212..79089e5 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\DematAccount\Holdings; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -19,7 +19,7 @@ */ final class DematMutualFund implements BaseModel { - use Model; + use SdkModel; /** * Additional information specific to the mutual fund. diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php index b2bf602..2fa7e52 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\DematAccount\Holdings; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -19,7 +19,7 @@ */ final class Equity implements BaseModel { - use Model; + use SdkModel; /** * Additional information specific to the equity. diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php index f2b0e1a..483f54f 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\DematAccount\Holdings; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -19,7 +19,7 @@ */ final class GovernmentSecurity implements BaseModel { - use Model; + use SdkModel; /** * Additional information specific to the government security. diff --git a/src/CasParser/UnifiedResponse/Insurance.php b/src/CasParser/UnifiedResponse/Insurance.php index 9e113a5..328801f 100644 --- a/src/CasParser/UnifiedResponse/Insurance.php +++ b/src/CasParser/UnifiedResponse/Insurance.php @@ -6,7 +6,7 @@ use CasParser\CasParser\UnifiedResponse\Insurance\LifeInsurancePolicy; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; use CasParser\Core\Conversion\ListOf; @@ -17,7 +17,7 @@ */ final class Insurance implements BaseModel { - use Model; + use SdkModel; /** @var null|list $lifeInsurancePolicies */ #[Api( diff --git a/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php b/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php index 872b340..c2c652a 100644 --- a/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php +++ b/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\Insurance; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -23,7 +23,7 @@ */ final class LifeInsurancePolicy implements BaseModel { - use Model; + use SdkModel; /** * Additional information specific to the policy. diff --git a/src/CasParser/UnifiedResponse/Investor.php b/src/CasParser/UnifiedResponse/Investor.php index e74c610..a86bd0f 100644 --- a/src/CasParser/UnifiedResponse/Investor.php +++ b/src/CasParser/UnifiedResponse/Investor.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -21,7 +21,7 @@ */ final class Investor implements BaseModel { - use Model; + use SdkModel; /** * Address of the investor. diff --git a/src/CasParser/UnifiedResponse/Meta.php b/src/CasParser/UnifiedResponse/Meta.php index ca1b4b2..c951e3a 100644 --- a/src/CasParser/UnifiedResponse/Meta.php +++ b/src/CasParser/UnifiedResponse/Meta.php @@ -7,7 +7,7 @@ use CasParser\CasParser\UnifiedResponse\Meta\CasType; use CasParser\CasParser\UnifiedResponse\Meta\StatementPeriod; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -19,7 +19,7 @@ */ final class Meta implements BaseModel { - use Model; + use SdkModel; /** * Type of CAS detected and processed. diff --git a/src/CasParser/UnifiedResponse/Meta/CasType.php b/src/CasParser/UnifiedResponse/Meta/CasType.php index d6b39d6..8cf6683 100644 --- a/src/CasParser/UnifiedResponse/Meta/CasType.php +++ b/src/CasParser/UnifiedResponse/Meta/CasType.php @@ -4,7 +4,7 @@ namespace CasParser\CasParser\UnifiedResponse\Meta; -use CasParser\Core\Concerns\Enum; +use CasParser\Core\Concerns\SdkEnum; use CasParser\Core\Conversion\Contracts\ConverterSource; /** @@ -14,7 +14,7 @@ */ final class CasType implements ConverterSource { - use Enum; + use SdkEnum; public const NSDL = 'NSDL'; diff --git a/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php b/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php index 925da8e..8999d8f 100644 --- a/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php +++ b/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\Meta; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -15,7 +15,7 @@ */ final class StatementPeriod implements BaseModel { - use Model; + use SdkModel; /** * Start date of the statement period. diff --git a/src/CasParser/UnifiedResponse/MutualFund.php b/src/CasParser/UnifiedResponse/MutualFund.php index 56bc838..7df1bb2 100644 --- a/src/CasParser/UnifiedResponse/MutualFund.php +++ b/src/CasParser/UnifiedResponse/MutualFund.php @@ -7,7 +7,7 @@ use CasParser\CasParser\UnifiedResponse\MutualFund\AdditionalInfo; use CasParser\CasParser\UnifiedResponse\MutualFund\Scheme; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; use CasParser\Core\Conversion\ListOf; @@ -23,7 +23,7 @@ */ final class MutualFund implements BaseModel { - use Model; + use SdkModel; /** * Additional folio information. diff --git a/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php b/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php index cb11684..59e816d 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\MutualFund; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -17,7 +17,7 @@ */ final class AdditionalInfo implements BaseModel { - use Model; + use SdkModel; /** * KYC status of the folio. diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php index 451cb56..8459ac4 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php @@ -9,7 +9,7 @@ use CasParser\CasParser\UnifiedResponse\MutualFund\Scheme\Transaction; use CasParser\CasParser\UnifiedResponse\MutualFund\Scheme\Type; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; use CasParser\Core\Conversion\ListOf; @@ -30,7 +30,7 @@ */ final class Scheme implements BaseModel { - use Model; + use SdkModel; /** * Additional information specific to the scheme. diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php index 6102c30..1c9d62e 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\MutualFund\Scheme; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -21,7 +21,7 @@ */ final class AdditionalInfo implements BaseModel { - use Model; + use SdkModel; /** * Financial advisor name (CAMS/KFintech). diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php index 4b495bf..cafe167 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\MutualFund\Scheme; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -13,7 +13,7 @@ */ final class Gain implements BaseModel { - use Model; + use SdkModel; /** * Absolute gain or loss. diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php index 9efe4da..2786cc9 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\MutualFund\Scheme; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -22,7 +22,7 @@ */ final class Transaction implements BaseModel { - use Model; + use SdkModel; /** * Transaction amount. diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Type.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Type.php index a19aa3b..81bd8d8 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Type.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Type.php @@ -4,7 +4,7 @@ namespace CasParser\CasParser\UnifiedResponse\MutualFund\Scheme; -use CasParser\Core\Concerns\Enum; +use CasParser\Core\Concerns\SdkEnum; use CasParser\Core\Conversion\Contracts\ConverterSource; /** @@ -14,7 +14,7 @@ */ final class Type implements ConverterSource { - use Enum; + use SdkEnum; public const EQUITY = 'Equity'; diff --git a/src/CasParser/UnifiedResponse/Summary.php b/src/CasParser/UnifiedResponse/Summary.php index cae3f14..b4d0c23 100644 --- a/src/CasParser/UnifiedResponse/Summary.php +++ b/src/CasParser/UnifiedResponse/Summary.php @@ -6,7 +6,7 @@ use CasParser\CasParser\UnifiedResponse\Summary\Accounts; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -14,7 +14,7 @@ */ final class Summary implements BaseModel { - use Model; + use SdkModel; #[Api(optional: true)] public ?Accounts $accounts; diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts.php b/src/CasParser/UnifiedResponse/Summary/Accounts.php index 2da5c85..02ebba6 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts.php @@ -8,7 +8,7 @@ use CasParser\CasParser\UnifiedResponse\Summary\Accounts\Insurance; use CasParser\CasParser\UnifiedResponse\Summary\Accounts\MutualFunds; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -18,7 +18,7 @@ */ final class Accounts implements BaseModel { - use Model; + use SdkModel; #[Api(optional: true)] public ?Demat $demat; diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php b/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php index 2bce5bc..8fce55d 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\Summary\Accounts; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -13,7 +13,7 @@ */ final class Demat implements BaseModel { - use Model; + use SdkModel; /** * Number of demat accounts. diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php b/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php index c8751f1..0de65b0 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\Summary\Accounts; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -13,7 +13,7 @@ */ final class Insurance implements BaseModel { - use Model; + use SdkModel; /** * Number of insurance policies. diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php b/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php index 1abd59f..5704d22 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php @@ -5,7 +5,7 @@ namespace CasParser\CasParser\UnifiedResponse\Summary\Accounts; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -13,7 +13,7 @@ */ final class MutualFunds implements BaseModel { - use Model; + use SdkModel; /** * Number of mutual fund folios. diff --git a/src/Core/Concerns/Page.php b/src/Core/Concerns/Page.php deleted file mode 100644 index dacd050..0000000 --- a/src/Core/Concerns/Page.php +++ /dev/null @@ -1,22 +0,0 @@ -|self $params diff --git a/src/Core/Concerns/Union.php b/src/Core/Concerns/SdkUnion.php similarity index 98% rename from src/Core/Concerns/Union.php rename to src/Core/Concerns/SdkUnion.php index 62a2aec..6a073f8 100644 --- a/src/Core/Concerns/Union.php +++ b/src/Core/Concerns/SdkUnion.php @@ -11,7 +11,7 @@ /** * @internal */ -trait Union +trait SdkUnion { private static Converter $converter; diff --git a/src/Core/Contracts/BasePage.php b/src/Core/Contracts/BasePage.php index d08f732..93a441b 100644 --- a/src/Core/Contracts/BasePage.php +++ b/src/Core/Contracts/BasePage.php @@ -4,13 +4,19 @@ namespace CasParser\Core\Contracts; +use CasParser\Core\BaseClient; +use CasParser\Core\Pagination\PageRequestOptions; +use Psr\Http\Message\ResponseInterface; + /** * @internal */ -interface BasePage extends \Stringable +interface BasePage { - /** - * @return \Traversable - */ - public function pagingEachItem(): \Traversable; + public function __construct( + BaseClient $client, + PageRequestOptions $options, + ResponseInterface $response, + mixed $body, + ); } diff --git a/src/Core/Pagination/AbstractPage.php b/src/Core/Pagination/AbstractPage.php index 3cde27e..a8d30c0 100644 --- a/src/Core/Pagination/AbstractPage.php +++ b/src/Core/Pagination/AbstractPage.php @@ -5,7 +5,7 @@ namespace CasParser\Core\Pagination; use CasParser\Core\BaseClient; -use CasParser\Core\Concerns\Page; +use CasParser\Core\Contracts\BasePage; use CasParser\Errors\Error; use Psr\Http\Message\ResponseInterface; @@ -16,7 +16,7 @@ * * @implements \IteratorAggregate */ -abstract class AbstractPage implements \IteratorAggregate, Page +abstract class AbstractPage implements \IteratorAggregate, BasePage { public function __construct( protected BaseClient $client, diff --git a/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php b/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php index d960ad6..cf086d7 100644 --- a/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php +++ b/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php @@ -5,7 +5,7 @@ namespace CasParser\Responses\CasGenerator; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; /** @@ -15,7 +15,7 @@ */ final class CasGeneratorGenerateCasResponse implements BaseModel { - use Model; + use SdkModel; #[Api(optional: true)] public ?string $msg; diff --git a/tests/Core/TestModel.php b/tests/Core/TestModel.php index 8e03729..1818daa 100644 --- a/tests/Core/TestModel.php +++ b/tests/Core/TestModel.php @@ -3,7 +3,7 @@ namespace Tests\Core; use CasParser\Core\Attributes\Api; -use CasParser\Core\Concerns\Model; +use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; use PHPUnit\Framework\Attributes\CoversNothing; use PHPUnit\Framework\Attributes\Test; @@ -11,7 +11,7 @@ class TestModel implements BaseModel { - use Model; + use SdkModel; #[Api] public string $name; From 47e28244783dd47d03f095cf0015aa947c0db5b8 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 04:22:21 +0000 Subject: [PATCH 06/29] fix(client): elide null named parameters --- README.md | 13 +----- src/Client.php | 4 +- src/Core/Util.php | 17 ++++++++ .../CasGeneratorService.php | 24 ++++++----- .../CasParserService.php | 40 ++++++++++++++----- tests/Resources/CasGeneratorTest.php | 2 - 6 files changed, 66 insertions(+), 34 deletions(-) rename src/{CasGenerator => Services}/CasGeneratorService.php (79%) rename src/{CasParser => Services}/CasParserService.php (77%) diff --git a/README.md b/README.md index b910e36..f859fcb 100644 --- a/README.md +++ b/README.md @@ -49,10 +49,7 @@ use CasParser\Client; $client = new Client(apiKey: getenv("CAS_PARSER_API_KEY") ?: "My API Key"); -$unifiedResponse = $client->casParser->smartParse( - password: "ABCDF", pdfURL: "https://your-cas-pdf-url-here.com" -); - +$unifiedResponse = $client->casParser->smartParse(); var_dump($unifiedResponse->demat_accounts); ``` @@ -119,11 +116,7 @@ use CasParser\RequestOptions; $client = new Client(maxRetries: 0); // Or, configure per-request: -$result = $client->casParser->smartParse( - password: "ABCDF", - pdfURL: "https://you-cas-pdf-url-here.com", - new RequestOptions(maxRetries: 5), -); +$result = $client->casParser->smartParse(new RequestOptions(maxRetries: 5)); ``` ## Advanced concepts @@ -142,8 +135,6 @@ Note: the `extra_` parameters of the same name overrides the documented paramete use CasParser\RequestOptions; $unifiedResponse = $client->casParser->smartParse( - password: "ABCDF", - pdfURL: "https://you-cas-pdf-url-here.com", new RequestOptions( extraQueryParams: ["my_query_parameter" => "value"], extraBodyParams: ["my_body_parameter" => "value"], diff --git a/src/Client.php b/src/Client.php index 90fb9a9..7182ad1 100644 --- a/src/Client.php +++ b/src/Client.php @@ -4,9 +4,9 @@ namespace CasParser; -use CasParser\CasGenerator\CasGeneratorService; -use CasParser\CasParser\CasParserService; use CasParser\Core\BaseClient; +use CasParser\Services\CasGeneratorService; +use CasParser\Services\CasParserService; class Client extends BaseClient { diff --git a/src/Core/Util.php b/src/Core/Util.php index 6ec7f5d..f6dfae2 100644 --- a/src/Core/Util.php +++ b/src/Core/Util.php @@ -349,6 +349,23 @@ public static function decodeContent(MessageInterface $rsp): mixed return self::streamIterator($body); } + /** + * @param array $arr + * @param list $keys + * + * @return array + */ + public static function array_filter_null(array $arr, array $keys): array + { + foreach ($keys as $key) { + if (array_key_exists($key, $arr) && is_null($arr[$key])) { + unset($arr[$key]); + } + } + + return $arr; + } + /** * @param list $closing * diff --git a/src/CasGenerator/CasGeneratorService.php b/src/Services/CasGeneratorService.php similarity index 79% rename from src/CasGenerator/CasGeneratorService.php rename to src/Services/CasGeneratorService.php index c54f077..3609dad 100644 --- a/src/CasGenerator/CasGeneratorService.php +++ b/src/Services/CasGeneratorService.php @@ -2,12 +2,14 @@ declare(strict_types=1); -namespace CasParser\CasGenerator; +namespace CasParser\Services; +use CasParser\CasGenerator\CasGeneratorGenerateCasParams; use CasParser\CasGenerator\CasGeneratorGenerateCasParams\CasAuthority; use CasParser\Client; use CasParser\Contracts\CasGeneratorContract; use CasParser\Core\Conversion; +use CasParser\Core\Util; use CasParser\RequestOptions; use CasParser\Responses\CasGenerator\CasGeneratorGenerateCasResponse; @@ -35,16 +37,18 @@ public function generateCas( $panNo = null, ?RequestOptions $requestOptions = null, ): CasGeneratorGenerateCasResponse { + $args = [ + 'email' => $email, + 'fromDate' => $fromDate, + 'password' => $password, + 'toDate' => $toDate, + 'casAuthority' => $casAuthority, + 'panNo' => $panNo, + ]; + $args = Util::array_filter_null($args, ['casAuthority', 'panNo']); [$parsed, $options] = CasGeneratorGenerateCasParams::parseRequest( - [ - 'email' => $email, - 'fromDate' => $fromDate, - 'password' => $password, - 'toDate' => $toDate, - 'casAuthority' => $casAuthority, - 'panNo' => $panNo, - ], - $requestOptions, + $args, + $requestOptions ); $resp = $this->client->request( method: 'post', diff --git a/src/CasParser/CasParserService.php b/src/Services/CasParserService.php similarity index 77% rename from src/CasParser/CasParserService.php rename to src/Services/CasParserService.php index 8ffd4e9..3e6e5f8 100644 --- a/src/CasParser/CasParserService.php +++ b/src/Services/CasParserService.php @@ -2,11 +2,17 @@ declare(strict_types=1); -namespace CasParser\CasParser; +namespace CasParser\Services; +use CasParser\CasParser\CasParserCamsKfintechParams; +use CasParser\CasParser\CasParserCdslParams; +use CasParser\CasParser\CasParserNsdlParams; +use CasParser\CasParser\CasParserSmartParseParams; +use CasParser\CasParser\UnifiedResponse; use CasParser\Client; use CasParser\Contracts\CasParserContract; use CasParser\Core\Conversion; +use CasParser\Core\Util; use CasParser\RequestOptions; final class CasParserService implements CasParserContract @@ -27,9 +33,13 @@ public function camsKfintech( $pdfURL = null, ?RequestOptions $requestOptions = null, ): UnifiedResponse { + $args = [ + 'password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL, + ]; + $args = Util::array_filter_null($args, ['password', 'pdfFile', 'pdfURL']); [$parsed, $options] = CasParserCamsKfintechParams::parseRequest( - ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], - $requestOptions, + $args, + $requestOptions ); $resp = $this->client->request( method: 'post', @@ -56,9 +66,13 @@ public function cdsl( $pdfURL = null, ?RequestOptions $requestOptions = null, ): UnifiedResponse { + $args = [ + 'password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL, + ]; + $args = Util::array_filter_null($args, ['password', 'pdfFile', 'pdfURL']); [$parsed, $options] = CasParserCdslParams::parseRequest( - ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], - $requestOptions, + $args, + $requestOptions ); $resp = $this->client->request( method: 'post', @@ -85,9 +99,13 @@ public function nsdl( $pdfURL = null, ?RequestOptions $requestOptions = null, ): UnifiedResponse { + $args = [ + 'password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL, + ]; + $args = Util::array_filter_null($args, ['password', 'pdfFile', 'pdfURL']); [$parsed, $options] = CasParserNsdlParams::parseRequest( - ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], - $requestOptions, + $args, + $requestOptions ); $resp = $this->client->request( method: 'post', @@ -114,9 +132,13 @@ public function smartParse( $pdfURL = null, ?RequestOptions $requestOptions = null, ): UnifiedResponse { + $args = [ + 'password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL, + ]; + $args = Util::array_filter_null($args, ['password', 'pdfFile', 'pdfURL']); [$parsed, $options] = CasParserSmartParseParams::parseRequest( - ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], - $requestOptions, + $args, + $requestOptions ); $resp = $this->client->request( method: 'post', diff --git a/tests/Resources/CasGeneratorTest.php b/tests/Resources/CasGeneratorTest.php index 47c61b2..27c281a 100644 --- a/tests/Resources/CasGeneratorTest.php +++ b/tests/Resources/CasGeneratorTest.php @@ -55,8 +55,6 @@ public function testGenerateCasWithOptionalParams(): void fromDate: '2023-01-01', password: 'Abcdefghi12$', toDate: '2023-12-31', - casAuthority: 'kfintech', - panNo: 'ABCDE1234F', ); $this->assertTrue(true); // @phpstan-ignore-line From 150660ae58f257d5faaa6f8cf7c4f9093e0bae2c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 04:39:40 +0000 Subject: [PATCH 07/29] chore: intuitively order union types --- .php-cs-fixer.dist.php | 8 +++- .../CasGeneratorGenerateCasParams.php | 4 +- src/CasParser/UnifiedResponse.php | 8 ++-- .../UnifiedResponse/DematAccount.php | 4 +- .../DematAccount/AdditionalInfo.php | 4 +- .../UnifiedResponse/DematAccount/Holdings.php | 20 +++++----- src/CasParser/UnifiedResponse/Insurance.php | 4 +- src/CasParser/UnifiedResponse/Meta.php | 4 +- src/CasParser/UnifiedResponse/MutualFund.php | 4 +- .../UnifiedResponse/MutualFund/Scheme.php | 12 +++--- src/Core/Attributes/Api.php | 16 ++++---- src/Core/BaseClient.php | 38 +++++++++---------- src/Core/Concerns/SdkParams.php | 6 +-- src/Core/Conversion/Concerns/ArrayOf.php | 8 ++-- src/Core/Conversion/EnumOf.php | 2 +- src/Core/Conversion/PropertyInfo.php | 4 +- src/Core/Conversion/UnionOf.php | 2 +- src/Core/Util.php | 20 +++++----- src/RequestOptions.php | 34 ++++++++--------- tests/Core/TestModel.php | 4 +- 20 files changed, 106 insertions(+), 100 deletions(-) diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 1e5b181..a2b062b 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -7,5 +7,11 @@ return (new Config()) ->setParallelConfig(ParallelConfigFactory::detect()) ->setFinder(Finder::create()->in([__DIR__.'/src', __DIR__.'/tests'])) - ->setRules(['@PhpCsFixer' => true, 'phpdoc_align' => false, 'new_with_parentheses' => ['named_class' => false]]) + ->setRules([ + '@PhpCsFixer' => true, + 'phpdoc_align' => false, + 'new_with_parentheses' => ['named_class' => false], + 'ordered_types' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'], + 'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'], + ]) ; diff --git a/src/CasGenerator/CasGeneratorGenerateCasParams.php b/src/CasGenerator/CasGeneratorGenerateCasParams.php index ba6bf84..bbe85b7 100644 --- a/src/CasGenerator/CasGeneratorGenerateCasParams.php +++ b/src/CasGenerator/CasGeneratorGenerateCasParams.php @@ -55,7 +55,7 @@ final class CasGeneratorGenerateCasParams implements BaseModel /** * CAS authority to generate the document from (currently only kfintech is supported). * - * @var null|CasAuthority::* $casAuthority + * @var CasAuthority::*|null $casAuthority */ #[Api('cas_authority', enum: CasAuthority::class, optional: true)] public ?string $casAuthority; @@ -97,7 +97,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param null|CasAuthority::* $casAuthority + * @param CasAuthority::*|null $casAuthority */ public static function with( string $email, diff --git a/src/CasParser/UnifiedResponse.php b/src/CasParser/UnifiedResponse.php index 1512127..1d1f43f 100644 --- a/src/CasParser/UnifiedResponse.php +++ b/src/CasParser/UnifiedResponse.php @@ -29,7 +29,7 @@ final class UnifiedResponse implements BaseModel { use SdkModel; - /** @var null|list $dematAccounts */ + /** @var list|null $dematAccounts */ #[Api( 'demat_accounts', type: new ListOf(DematAccount::class), @@ -46,7 +46,7 @@ final class UnifiedResponse implements BaseModel #[Api(optional: true)] public ?Meta $meta; - /** @var null|list $mutualFunds */ + /** @var list|null $mutualFunds */ #[Api('mutual_funds', type: new ListOf(MutualFund::class), optional: true)] public ?array $mutualFunds; @@ -64,8 +64,8 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param null|list $dematAccounts - * @param null|list $mutualFunds + * @param list|null $dematAccounts + * @param list|null $mutualFunds */ public static function with( ?array $dematAccounts = null, diff --git a/src/CasParser/UnifiedResponse/DematAccount.php b/src/CasParser/UnifiedResponse/DematAccount.php index 8042c37..0cbe628 100644 --- a/src/CasParser/UnifiedResponse/DematAccount.php +++ b/src/CasParser/UnifiedResponse/DematAccount.php @@ -48,7 +48,7 @@ final class DematAccount implements BaseModel /** * Type of demat account. * - * @var null|DematType::* $dematType + * @var DematType::*|null $dematType */ #[Api('demat_type', enum: DematType::class, optional: true)] public ?string $dematType; @@ -85,7 +85,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param null|DematType::* $dematType + * @param DematType::*|null $dematType */ public static function with( ?AdditionalInfo $additionalInfo = null, diff --git a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php index 539d04e..afcf4d8 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php @@ -60,7 +60,7 @@ final class AdditionalInfo implements BaseModel /** * List of linked PAN numbers (NSDL). * - * @var null|list $linkedPans + * @var list|null $linkedPans */ #[Api('linked_pans', type: new ListOf('string'), optional: true)] public ?array $linkedPans; @@ -88,7 +88,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param null|list $linkedPans + * @param list|null $linkedPans */ public static function with( ?string $boStatus = null, diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php index 36587c5..72be137 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php @@ -27,11 +27,11 @@ final class Holdings implements BaseModel { use SdkModel; - /** @var null|list $aifs */ + /** @var list|null $aifs */ #[Api(type: new ListOf(Aif::class), optional: true)] public ?array $aifs; - /** @var null|list $corporateBonds */ + /** @var list|null $corporateBonds */ #[Api( 'corporate_bonds', type: new ListOf(CorporateBond::class), @@ -39,7 +39,7 @@ final class Holdings implements BaseModel )] public ?array $corporateBonds; - /** @var null|list $dematMutualFunds */ + /** @var list|null $dematMutualFunds */ #[Api( 'demat_mutual_funds', type: new ListOf(DematMutualFund::class), @@ -47,11 +47,11 @@ final class Holdings implements BaseModel )] public ?array $dematMutualFunds; - /** @var null|list $equities */ + /** @var list|null $equities */ #[Api(type: new ListOf(Equity::class), optional: true)] public ?array $equities; - /** @var null|list $governmentSecurities */ + /** @var list|null $governmentSecurities */ #[Api( 'government_securities', type: new ListOf(GovernmentSecurity::class), @@ -70,11 +70,11 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param null|list $aifs - * @param null|list $corporateBonds - * @param null|list $dematMutualFunds - * @param null|list $equities - * @param null|list $governmentSecurities + * @param list|null $aifs + * @param list|null $corporateBonds + * @param list|null $dematMutualFunds + * @param list|null $equities + * @param list|null $governmentSecurities */ public static function with( ?array $aifs = null, diff --git a/src/CasParser/UnifiedResponse/Insurance.php b/src/CasParser/UnifiedResponse/Insurance.php index 328801f..4f072e4 100644 --- a/src/CasParser/UnifiedResponse/Insurance.php +++ b/src/CasParser/UnifiedResponse/Insurance.php @@ -19,7 +19,7 @@ final class Insurance implements BaseModel { use SdkModel; - /** @var null|list $lifeInsurancePolicies */ + /** @var list|null $lifeInsurancePolicies */ #[Api( 'life_insurance_policies', type: new ListOf(LifeInsurancePolicy::class), @@ -38,7 +38,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param null|list $lifeInsurancePolicies + * @param list|null $lifeInsurancePolicies */ public static function with(?array $lifeInsurancePolicies = null): self { diff --git a/src/CasParser/UnifiedResponse/Meta.php b/src/CasParser/UnifiedResponse/Meta.php index c951e3a..5e20d4e 100644 --- a/src/CasParser/UnifiedResponse/Meta.php +++ b/src/CasParser/UnifiedResponse/Meta.php @@ -24,7 +24,7 @@ final class Meta implements BaseModel /** * Type of CAS detected and processed. * - * @var null|CasType::* $casType + * @var CasType::*|null $casType */ #[Api('cas_type', enum: CasType::class, optional: true)] public ?string $casType; @@ -49,7 +49,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param null|CasType::* $casType + * @param CasType::*|null $casType */ public static function with( ?string $casType = null, diff --git a/src/CasParser/UnifiedResponse/MutualFund.php b/src/CasParser/UnifiedResponse/MutualFund.php index 7df1bb2..89cd4f8 100644 --- a/src/CasParser/UnifiedResponse/MutualFund.php +++ b/src/CasParser/UnifiedResponse/MutualFund.php @@ -49,7 +49,7 @@ final class MutualFund implements BaseModel #[Api(optional: true)] public ?string $registrar; - /** @var null|list $schemes */ + /** @var list|null $schemes */ #[Api(type: new ListOf(Scheme::class), optional: true)] public ?array $schemes; @@ -70,7 +70,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param null|list $schemes + * @param list|null $schemes */ public static function with( ?AdditionalInfo $additionalInfo = null, diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php index 8459ac4..eaea7ba 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php @@ -68,19 +68,19 @@ final class Scheme implements BaseModel /** * List of nominees. * - * @var null|list $nominees + * @var list|null $nominees */ #[Api(type: new ListOf('string'), optional: true)] public ?array $nominees; - /** @var null|list $transactions */ + /** @var list|null $transactions */ #[Api(type: new ListOf(Transaction::class), optional: true)] public ?array $transactions; /** * Type of mutual fund scheme. * - * @var null|Type::* $type + * @var Type::*|null $type */ #[Api(enum: Type::class, optional: true)] public ?string $type; @@ -108,9 +108,9 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param null|list $nominees - * @param null|list $transactions - * @param null|Type::* $type + * @param list|null $nominees + * @param list|null $transactions + * @param Type::*|null $type */ public static function with( ?AdditionalInfo $additionalInfo = null, diff --git a/src/Core/Attributes/Api.php b/src/Core/Attributes/Api.php index d6aa88a..47f5cd2 100644 --- a/src/Core/Attributes/Api.php +++ b/src/Core/Attributes/Api.php @@ -14,20 +14,20 @@ final class Api { /** - * @var null|class-string|Converter|string + * @var class-string|Converter|string|null */ - public readonly null|Converter|string $type; + public readonly Converter|string|null $type; /** - * @param null|class-string|Converter|string $type - * @param null|class-string|Converter $enum - * @param null|class-string|Converter|string $union + * @param class-string|Converter|string|null $type + * @param class-string|Converter|null $enum + * @param class-string|Converter|string|null $union */ public function __construct( public readonly ?string $apiName = null, - null|Converter|string $type = null, - null|Converter|string $enum = null, - null|Converter|string $union = null, + Converter|string|null $type = null, + Converter|string|null $enum = null, + Converter|string|null $union = null, public readonly bool $nullable = false, public readonly bool $optional = false, ) { diff --git a/src/Core/BaseClient.php b/src/Core/BaseClient.php index afc48be..bc56b0d 100644 --- a/src/Core/BaseClient.php +++ b/src/Core/BaseClient.php @@ -29,7 +29,7 @@ class BaseClient protected ClientInterface $transporter; /** - * @param array|string> $headers + * @param array|null> $headers */ public function __construct( protected array $headers, @@ -45,13 +45,13 @@ public function __construct( } /** - * @param list|string $path + * @param string|list $path * @param array $query * @param array $headers */ public function request( string $method, - array|string $path, + string|array $path, array $query = [], array $headers = [], mixed $body = null, @@ -88,27 +88,27 @@ protected function authHeaders(): array } /** - * @param list|string $path + * @param string|list $path * @param array $query - * @param array|string> $headers - * @param null|array{ - * timeout?: null|float, - * maxRetries?: null|int, - * initialRetryDelay?: null|float, - * maxRetryDelay?: null|float, - * extraHeaders?: null|list, - * extraQueryParams?: null|list, - * extraBodyParams?: null|list, - * }|RequestOptions $opts + * @param array|null> $headers + * @param array{ + * timeout?: float|null, + * maxRetries?: int|null, + * initialRetryDelay?: float|null, + * maxRetryDelay?: float|null, + * extraHeaders?: list|null, + * extraQueryParams?: list|null, + * extraBodyParams?: list|null, + * }|RequestOptions|null $opts * * @return array{RequestInterface, RequestOptions} */ protected function buildRequest( string $method, - array|string $path, + string|array $path, array $query, array $headers, - null|array|RequestOptions $opts, + array|RequestOptions|null $opts, ): array { $opts = [...$this->options->__serialize(), ...RequestOptions::parse($opts)->__serialize()]; $options = new RequestOptions(...$opts); @@ -119,7 +119,7 @@ protected function buildRequest( $mergedQuery = array_merge_recursive($query, $options->extraQueryParams); $uri = Util::joinUri($this->baseUrl, path: $parsedPath, query: $mergedQuery); - /** @var array|string> $mergedHeaders */ + /** @var array> $mergedHeaders */ $mergedHeaders = [...$this->headers, ...$this->authHeaders(), ...$headers, @@ -146,9 +146,9 @@ protected function followRedirect( } /** - * @param null|array|bool|float|int|resource|string|\Traversable< + * @param bool|int|float|string|array|resource|\Traversable< * mixed - * > $data + * >|null $data */ protected function sendRequest( RequestInterface $req, diff --git a/src/Core/Concerns/SdkParams.php b/src/Core/Concerns/SdkParams.php index c754488..8338148 100644 --- a/src/Core/Concerns/SdkParams.php +++ b/src/Core/Concerns/SdkParams.php @@ -14,8 +14,8 @@ trait SdkParams { /** - * @param null|array|self $params - * @param null|array|RequestOptions $options + * @param array|self|null $params + * @param array|RequestOptions|null $options * * @return array{array, array{ * timeout: float, @@ -27,7 +27,7 @@ trait SdkParams * extraBodyParams: list, * }} */ - public static function parseRequest(null|array|self $params, null|array|RequestOptions $options): array + public static function parseRequest(array|self|null $params, array|RequestOptions|null $options): array { $converter = self::converter(); $state = new DumpState; diff --git a/src/Core/Conversion/Concerns/ArrayOf.php b/src/Core/Conversion/Concerns/ArrayOf.php index c283cd3..e711b11 100644 --- a/src/Core/Conversion/Concerns/ArrayOf.php +++ b/src/Core/Conversion/Concerns/ArrayOf.php @@ -15,12 +15,12 @@ */ trait ArrayOf { - private readonly null|Converter|ConverterSource|string $type; + private readonly Converter|ConverterSource|string|null $type; public function __construct( - null|Converter|ConverterSource|string $type = null, - null|Converter|ConverterSource|string $enum = null, - null|Converter|ConverterSource|string $union = null, + Converter|ConverterSource|string|null $type = null, + Converter|ConverterSource|string|null $enum = null, + Converter|ConverterSource|string|null $union = null, private readonly bool $nullable = false, ) { $this->type = $type ?? $enum ?? $union; diff --git a/src/Core/Conversion/EnumOf.php b/src/Core/Conversion/EnumOf.php index 05065ac..aaed8da 100644 --- a/src/Core/Conversion/EnumOf.php +++ b/src/Core/Conversion/EnumOf.php @@ -15,7 +15,7 @@ final class EnumOf implements Converter private readonly string $type; /** - * @param list $members + * @param list $members */ public function __construct(private readonly array $members) { diff --git a/src/Core/Conversion/PropertyInfo.php b/src/Core/Conversion/PropertyInfo.php index 5eec48e..385627b 100644 --- a/src/Core/Conversion/PropertyInfo.php +++ b/src/Core/Conversion/PropertyInfo.php @@ -46,9 +46,9 @@ public function __construct(public readonly \ReflectionProperty $property) } /** - * @param null|array|Converter|ConverterSource|\ReflectionType|string $type + * @param array|Converter|ConverterSource|\ReflectionType|string|null $type */ - private static function parse(null|array|Converter|ConverterSource|\ReflectionType|string $type): Converter|ConverterSource|string + private static function parse(array|Converter|ConverterSource|\ReflectionType|string|null $type): Converter|ConverterSource|string { if (is_string($type) || $type instanceof Converter) { return $type; diff --git a/src/Core/Conversion/UnionOf.php b/src/Core/Conversion/UnionOf.php index 525fdac..7937f98 100644 --- a/src/Core/Conversion/UnionOf.php +++ b/src/Core/Conversion/UnionOf.php @@ -77,7 +77,7 @@ public function dump(mixed $value, DumpState $state): mixed private function resolveVariant( mixed $value, - ): null|Converter|ConverterSource|string { + ): Converter|ConverterSource|string|null { if ($value instanceof BaseModel) { return $value::class; } diff --git a/src/Core/Util.php b/src/Core/Util.php index f6dfae2..2af6769 100644 --- a/src/Core/Util.php +++ b/src/Core/Util.php @@ -45,11 +45,11 @@ public static function array_transform_keys(array $array, array $map): array } /** - * @param callable|int|list|string $key + * @param string|int|list|callable $key */ public static function dig( mixed $array, - array|callable|int|string $key + string|int|array|callable $key ): mixed { if (is_callable($key)) { return $key($array); @@ -71,9 +71,9 @@ public static function dig( } /** - * @param list|string $path + * @param string|list $path */ - public static function parsePath(array|string $path): string + public static function parsePath(string|array $path): string { if (is_string($path)) { return $path; @@ -124,7 +124,7 @@ public static function joinUri( } /** - * @param array|string> $headers + * @param array|null> $headers */ public static function withSetHeaders( RequestInterface $req, @@ -165,9 +165,9 @@ public static function streamIterator(StreamInterface $stream): \Iterator } /** - * @param null|array|bool|float|int|resource|string|\Traversable< + * @param bool|int|float|string|array|resource|\Traversable< * mixed - * > $body + * >|null $body * * @return array{string, \Generator} */ @@ -202,9 +202,9 @@ public static function encodeMultipartStreaming(mixed $body): array } /** - * @param null|array|bool|float|int|resource|string|\Traversable< + * @param bool|int|float|string|array|resource|\Traversable< * mixed - * > $body + * >|null $body */ public static function withSetBody( StreamFactoryInterface $factory, @@ -267,7 +267,7 @@ public static function decodeLines(\Iterator $stream): \Iterator * * @return \Generator< * array{ - * event?: null|string, data?: null|string, id?: null|string, retry?: null|int + * event?: string|null, data?: string|null, id?: string|null, retry?: int|null * }, * > */ diff --git a/src/RequestOptions.php b/src/RequestOptions.php index 305cf24..0609a2a 100644 --- a/src/RequestOptions.php +++ b/src/RequestOptions.php @@ -55,13 +55,13 @@ public function __serialize(): array /** * @param array{ - * timeout?: null|float, - * maxRetries?: null|int, - * initialRetryDelay?: null|float, - * maxRetryDelay?: null|float, - * extraHeaders?: null|list, - * extraQueryParams?: null|list, - * extraBodyParams?: null|list, + * timeout?: float|null, + * maxRetries?: int|null, + * initialRetryDelay?: float|null, + * maxRetryDelay?: float|null, + * extraHeaders?: list|null, + * extraQueryParams?: list|null, + * extraBodyParams?: list|null, * } $data */ public function __unserialize(array $data): void @@ -88,17 +88,17 @@ public function __unserialize(array $data): void } /** - * @param null|array{ - * timeout?: null|float, - * maxRetries?: null|int, - * initialRetryDelay?: null|float, - * maxRetryDelay?: null|float, - * extraHeaders?: null|list, - * extraQueryParams?: null|list, - * extraBodyParams?: null|list, - * }|RequestOptions $options + * @param array{ + * timeout?: float|null, + * maxRetries?: int|null, + * initialRetryDelay?: float|null, + * maxRetryDelay?: float|null, + * extraHeaders?: list|null, + * extraQueryParams?: list|null, + * extraBodyParams?: list|null, + * }|RequestOptions|null $options */ - public static function parse(null|array|RequestOptions $options): self + public static function parse(array|RequestOptions|null $options): self { if (is_null($options)) { return new self; diff --git a/tests/Core/TestModel.php b/tests/Core/TestModel.php index 1818daa..dea7648 100644 --- a/tests/Core/TestModel.php +++ b/tests/Core/TestModel.php @@ -19,7 +19,7 @@ class TestModel implements BaseModel #[Api('age_years')] public int $ageYears; - /** @var null|list */ + /** @var list|null */ #[Api(optional: true)] public ?array $friends; @@ -27,7 +27,7 @@ class TestModel implements BaseModel public ?string $owner; /** - * @param null|list $friends + * @param list|null $friends */ public function __construct( string $name, From ff4989246f4c361a99aa1240db0b7c956fa5161d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 23 Aug 2025 03:51:22 +0000 Subject: [PATCH 08/29] chore: remove type aliases --- .../CasGeneratorGenerateCasParams.php | 9 --------- .../CasAuthority.php | 2 -- src/CasParser/CasParserCamsKfintechParams.php | 4 ---- src/CasParser/CasParserCdslParams.php | 4 ---- src/CasParser/CasParserNsdlParams.php | 4 ---- src/CasParser/CasParserSmartParseParams.php | 4 ---- src/CasParser/UnifiedResponse.php | 10 ---------- src/CasParser/UnifiedResponse/DematAccount.php | 12 ------------ .../DematAccount/AdditionalInfo.php | 11 ----------- .../UnifiedResponse/DematAccount/DematType.php | 2 -- .../UnifiedResponse/DematAccount/Holdings.php | 9 --------- .../UnifiedResponse/DematAccount/Holdings/Aif.php | 9 --------- .../DematAccount/Holdings/CorporateBond.php | 9 --------- .../DematAccount/Holdings/DematMutualFund.php | 9 --------- .../DematAccount/Holdings/Equity.php | 9 --------- .../DematAccount/Holdings/GovernmentSecurity.php | 9 --------- src/CasParser/UnifiedResponse/Insurance.php | 5 ----- .../Insurance/LifeInsurancePolicy.php | 13 ------------- src/CasParser/UnifiedResponse/Investor.php | 11 ----------- src/CasParser/UnifiedResponse/Meta.php | 7 ------- src/CasParser/UnifiedResponse/Meta/CasType.php | 2 -- .../UnifiedResponse/Meta/StatementPeriod.php | 5 ----- src/CasParser/UnifiedResponse/MutualFund.php | 10 ---------- .../UnifiedResponse/MutualFund/AdditionalInfo.php | 4 ---- .../UnifiedResponse/MutualFund/Scheme.php | 15 --------------- .../MutualFund/Scheme/AdditionalInfo.php | 8 -------- .../UnifiedResponse/MutualFund/Scheme/Gain.php | 3 --- .../MutualFund/Scheme/Transaction.php | 12 ------------ .../UnifiedResponse/MutualFund/Scheme/Type.php | 2 -- src/CasParser/UnifiedResponse/Summary.php | 3 --- .../UnifiedResponse/Summary/Accounts.php | 5 ----- .../UnifiedResponse/Summary/Accounts/Demat.php | 3 --- .../Summary/Accounts/Insurance.php | 3 --- .../Summary/Accounts/MutualFunds.php | 3 --- .../CasGeneratorGenerateCasResponse.php | 5 ----- 35 files changed, 235 deletions(-) diff --git a/src/CasGenerator/CasGeneratorGenerateCasParams.php b/src/CasGenerator/CasGeneratorGenerateCasParams.php index bbe85b7..ee8d0bb 100644 --- a/src/CasGenerator/CasGeneratorGenerateCasParams.php +++ b/src/CasGenerator/CasGeneratorGenerateCasParams.php @@ -13,15 +13,6 @@ /** * This endpoint generates CAS (Consolidated Account Statement) documents by submitting a mailback request to the specified CAS authority. * Currently only supports KFintech, with plans to support CAMS, CDSL, and NSDL in the future. - * - * @phpstan-type generate_cas_params = array{ - * email: string, - * fromDate: string, - * password: string, - * toDate: string, - * casAuthority?: CasAuthority::*, - * panNo?: string, - * } */ final class CasGeneratorGenerateCasParams implements BaseModel { diff --git a/src/CasGenerator/CasGeneratorGenerateCasParams/CasAuthority.php b/src/CasGenerator/CasGeneratorGenerateCasParams/CasAuthority.php index eb2eef7..8c5f709 100644 --- a/src/CasGenerator/CasGeneratorGenerateCasParams/CasAuthority.php +++ b/src/CasGenerator/CasGeneratorGenerateCasParams/CasAuthority.php @@ -9,8 +9,6 @@ /** * CAS authority to generate the document from (currently only kfintech is supported). - * - * @phpstan-type cas_authority_alias = CasAuthority::* */ final class CasAuthority implements ConverterSource { diff --git a/src/CasParser/CasParserCamsKfintechParams.php b/src/CasParser/CasParserCamsKfintechParams.php index 71d566f..6da7d26 100644 --- a/src/CasParser/CasParserCamsKfintechParams.php +++ b/src/CasParser/CasParserCamsKfintechParams.php @@ -12,10 +12,6 @@ /** * This endpoint specifically parses CAMS/KFintech CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from CAMS or KFintech. - * - * @phpstan-type cams_kfintech_params = array{ - * password?: string, pdfFile?: string, pdfURL?: string - * } */ final class CasParserCamsKfintechParams implements BaseModel { diff --git a/src/CasParser/CasParserCdslParams.php b/src/CasParser/CasParserCdslParams.php index 5b74efa..f802a3b 100644 --- a/src/CasParser/CasParserCdslParams.php +++ b/src/CasParser/CasParserCdslParams.php @@ -12,10 +12,6 @@ /** * This endpoint specifically parses CDSL CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from CDSL. - * - * @phpstan-type cdsl_params = array{ - * password?: string, pdfFile?: string, pdfURL?: string - * } */ final class CasParserCdslParams implements BaseModel { diff --git a/src/CasParser/CasParserNsdlParams.php b/src/CasParser/CasParserNsdlParams.php index aef081e..3123079 100644 --- a/src/CasParser/CasParserNsdlParams.php +++ b/src/CasParser/CasParserNsdlParams.php @@ -12,10 +12,6 @@ /** * This endpoint specifically parses NSDL CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from NSDL. - * - * @phpstan-type nsdl_params = array{ - * password?: string, pdfFile?: string, pdfURL?: string - * } */ final class CasParserNsdlParams implements BaseModel { diff --git a/src/CasParser/CasParserSmartParseParams.php b/src/CasParser/CasParserSmartParseParams.php index 44016e2..998aea8 100644 --- a/src/CasParser/CasParserSmartParseParams.php +++ b/src/CasParser/CasParserSmartParseParams.php @@ -12,10 +12,6 @@ /** * This endpoint parses CAS (Consolidated Account Statement) PDF files from NSDL, CDSL, or CAMS/KFintech and returns data in a unified format. * It auto-detects the CAS type and transforms the data into a consistent structure regardless of the source. - * - * @phpstan-type smart_parse_params = array{ - * password?: string, pdfFile?: string, pdfURL?: string - * } */ final class CasParserSmartParseParams implements BaseModel { diff --git a/src/CasParser/UnifiedResponse.php b/src/CasParser/UnifiedResponse.php index 1d1f43f..91bca01 100644 --- a/src/CasParser/UnifiedResponse.php +++ b/src/CasParser/UnifiedResponse.php @@ -15,16 +15,6 @@ use CasParser\Core\Contracts\BaseModel; use CasParser\Core\Conversion\ListOf; -/** - * @phpstan-type unified_response_alias = array{ - * dematAccounts?: list, - * insurance?: Insurance, - * investor?: Investor, - * meta?: Meta, - * mutualFunds?: list, - * summary?: Summary, - * } - */ final class UnifiedResponse implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/DematAccount.php b/src/CasParser/UnifiedResponse/DematAccount.php index 0cbe628..bb7b45b 100644 --- a/src/CasParser/UnifiedResponse/DematAccount.php +++ b/src/CasParser/UnifiedResponse/DematAccount.php @@ -11,18 +11,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type demat_account_alias = array{ - * additionalInfo?: AdditionalInfo, - * boID?: string, - * clientID?: string, - * dematType?: DematType::*, - * dpID?: string, - * dpName?: string, - * holdings?: Holdings, - * value?: float, - * } - */ final class DematAccount implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php index afcf4d8..a1cdfb3 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php @@ -11,17 +11,6 @@ /** * Additional information specific to the demat account type. - * - * @phpstan-type additional_info_alias = array{ - * boStatus?: string, - * boSubStatus?: string, - * boType?: string, - * bsda?: string, - * email?: string, - * linkedPans?: list, - * nominee?: string, - * status?: string, - * } */ final class AdditionalInfo implements BaseModel { diff --git a/src/CasParser/UnifiedResponse/DematAccount/DematType.php b/src/CasParser/UnifiedResponse/DematAccount/DematType.php index 149bfb5..0cc1284 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/DematType.php +++ b/src/CasParser/UnifiedResponse/DematAccount/DematType.php @@ -9,8 +9,6 @@ /** * Type of demat account. - * - * @phpstan-type demat_type_alias = DematType::* */ final class DematType implements ConverterSource { diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php index 72be137..cc9f3af 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php @@ -14,15 +14,6 @@ use CasParser\Core\Contracts\BaseModel; use CasParser\Core\Conversion\ListOf; -/** - * @phpstan-type holdings_alias = array{ - * aifs?: list, - * corporateBonds?: list, - * dematMutualFunds?: list, - * equities?: list, - * governmentSecurities?: list, - * } - */ final class Holdings implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php index 205e809..13d9c18 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php @@ -8,15 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type aif_alias = array{ - * additionalInfo?: mixed, - * isin?: string, - * name?: string, - * units?: float, - * value?: float, - * } - */ final class Aif implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php index 10f5a73..50956db 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php @@ -8,15 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type corporate_bond_alias = array{ - * additionalInfo?: mixed, - * isin?: string, - * name?: string, - * units?: float, - * value?: float, - * } - */ final class CorporateBond implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php index 79089e5..17d9af3 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php @@ -8,15 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type demat_mutual_fund_alias = array{ - * additionalInfo?: mixed, - * isin?: string, - * name?: string, - * units?: float, - * value?: float, - * } - */ final class DematMutualFund implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php index 2fa7e52..9a1251f 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php @@ -8,15 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type equity_alias = array{ - * additionalInfo?: mixed, - * isin?: string, - * name?: string, - * units?: float, - * value?: float, - * } - */ final class Equity implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php index 483f54f..e9d3148 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php @@ -8,15 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type government_security_alias = array{ - * additionalInfo?: mixed, - * isin?: string, - * name?: string, - * units?: float, - * value?: float, - * } - */ final class GovernmentSecurity implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/Insurance.php b/src/CasParser/UnifiedResponse/Insurance.php index 4f072e4..34c4349 100644 --- a/src/CasParser/UnifiedResponse/Insurance.php +++ b/src/CasParser/UnifiedResponse/Insurance.php @@ -10,11 +10,6 @@ use CasParser\Core\Contracts\BaseModel; use CasParser\Core\Conversion\ListOf; -/** - * @phpstan-type insurance_alias = array{ - * lifeInsurancePolicies?: list - * } - */ final class Insurance implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php b/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php index c2c652a..a154862 100644 --- a/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php +++ b/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php @@ -8,19 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type life_insurance_policy_alias = array{ - * additionalInfo?: mixed, - * lifeAssured?: string, - * policyName?: string, - * policyNumber?: string, - * premiumAmount?: float, - * premiumFrequency?: string, - * provider?: string, - * status?: string, - * sumAssured?: float, - * } - */ final class LifeInsurancePolicy implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/Investor.php b/src/CasParser/UnifiedResponse/Investor.php index a86bd0f..4541bdb 100644 --- a/src/CasParser/UnifiedResponse/Investor.php +++ b/src/CasParser/UnifiedResponse/Investor.php @@ -8,17 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type investor_alias = array{ - * address?: string, - * casID?: string, - * email?: string, - * mobile?: string, - * name?: string, - * pan?: string, - * pincode?: string, - * } - */ final class Investor implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/Meta.php b/src/CasParser/UnifiedResponse/Meta.php index 5e20d4e..444c914 100644 --- a/src/CasParser/UnifiedResponse/Meta.php +++ b/src/CasParser/UnifiedResponse/Meta.php @@ -10,13 +10,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type meta_alias = array{ - * casType?: CasType::*, - * generatedAt?: \DateTimeInterface, - * statementPeriod?: StatementPeriod, - * } - */ final class Meta implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/Meta/CasType.php b/src/CasParser/UnifiedResponse/Meta/CasType.php index 8cf6683..6fc6c8e 100644 --- a/src/CasParser/UnifiedResponse/Meta/CasType.php +++ b/src/CasParser/UnifiedResponse/Meta/CasType.php @@ -9,8 +9,6 @@ /** * Type of CAS detected and processed. - * - * @phpstan-type cas_type_alias = CasType::* */ final class CasType implements ConverterSource { diff --git a/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php b/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php index 8999d8f..4eedc9c 100644 --- a/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php +++ b/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php @@ -8,11 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type statement_period_alias = array{ - * from?: \DateTimeInterface, to?: \DateTimeInterface - * } - */ final class StatementPeriod implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/MutualFund.php b/src/CasParser/UnifiedResponse/MutualFund.php index 89cd4f8..21fdbc1 100644 --- a/src/CasParser/UnifiedResponse/MutualFund.php +++ b/src/CasParser/UnifiedResponse/MutualFund.php @@ -11,16 +11,6 @@ use CasParser\Core\Contracts\BaseModel; use CasParser\Core\Conversion\ListOf; -/** - * @phpstan-type mutual_fund_alias = array{ - * additionalInfo?: AdditionalInfo, - * amc?: string, - * folioNumber?: string, - * registrar?: string, - * schemes?: list, - * value?: float, - * } - */ final class MutualFund implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php b/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php index 59e816d..901e899 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php @@ -10,10 +10,6 @@ /** * Additional folio information. - * - * @phpstan-type additional_info_alias = array{ - * kyc?: string, pan?: string, pankyc?: string - * } */ final class AdditionalInfo implements BaseModel { diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php index eaea7ba..a3ec40c 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php @@ -13,21 +13,6 @@ use CasParser\Core\Contracts\BaseModel; use CasParser\Core\Conversion\ListOf; -/** - * @phpstan-type scheme_alias = array{ - * additionalInfo?: AdditionalInfo, - * cost?: float, - * gain?: Gain, - * isin?: string, - * name?: string, - * nav?: float, - * nominees?: list, - * transactions?: list, - * type?: Type::*, - * units?: float, - * value?: float, - * } - */ final class Scheme implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php index 1c9d62e..4326b2e 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php @@ -10,14 +10,6 @@ /** * Additional information specific to the scheme. - * - * @phpstan-type additional_info_alias = array{ - * advisor?: string, - * amfi?: string, - * closeUnits?: float, - * openUnits?: float, - * rtaCode?: string, - * } */ final class AdditionalInfo implements BaseModel { diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php index cafe167..a535725 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php @@ -8,9 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type gain_alias = array{absolute?: float, percentage?: float} - */ final class Gain implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php index 2786cc9..a8ecf41 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php @@ -8,18 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type transaction_alias = array{ - * amount?: float, - * balance?: float, - * date?: \DateTimeInterface, - * description?: string, - * dividendRate?: float, - * nav?: float, - * type?: string, - * units?: float, - * } - */ final class Transaction implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Type.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Type.php index 81bd8d8..ad08d86 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Type.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Type.php @@ -9,8 +9,6 @@ /** * Type of mutual fund scheme. - * - * @phpstan-type type_alias = Type::* */ final class Type implements ConverterSource { diff --git a/src/CasParser/UnifiedResponse/Summary.php b/src/CasParser/UnifiedResponse/Summary.php index b4d0c23..588f740 100644 --- a/src/CasParser/UnifiedResponse/Summary.php +++ b/src/CasParser/UnifiedResponse/Summary.php @@ -9,9 +9,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type summary_alias = array{accounts?: Accounts, totalValue?: float} - */ final class Summary implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts.php b/src/CasParser/UnifiedResponse/Summary/Accounts.php index 02ebba6..ccaf08c 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts.php @@ -11,11 +11,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type accounts_alias = array{ - * demat?: Demat, insurance?: Insurance, mutualFunds?: MutualFunds - * } - */ final class Accounts implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php b/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php index 8fce55d..0502ecb 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php @@ -8,9 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type demat_alias = array{count?: int, totalValue?: float} - */ final class Demat implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php b/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php index 0de65b0..fe063c9 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php @@ -8,9 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type insurance_alias = array{count?: int, totalValue?: float} - */ final class Insurance implements BaseModel { use SdkModel; diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php b/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php index 5704d22..42a3af2 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php @@ -8,9 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type mutual_funds_alias = array{count?: int, totalValue?: float} - */ final class MutualFunds implements BaseModel { use SdkModel; diff --git a/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php b/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php index cf086d7..4a896b1 100644 --- a/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php +++ b/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php @@ -8,11 +8,6 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -/** - * @phpstan-type cas_generator_generate_cas_response_alias = array{ - * msg?: string, status?: string - * } - */ final class CasGeneratorGenerateCasResponse implements BaseModel { use SdkModel; From b45934c91c079bc8eff6a3cfb53e8adab8927034 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 02:17:11 +0000 Subject: [PATCH 09/29] chore: improve model annotations --- .../CasGeneratorGenerateCasParams.php | 2 +- src/CasParser/UnifiedResponse.php | 13 +++----- .../UnifiedResponse/DematAccount.php | 2 +- .../DematAccount/AdditionalInfo.php | 5 ++- .../UnifiedResponse/DematAccount/Holdings.php | 31 +++++++------------ src/CasParser/UnifiedResponse/Insurance.php | 7 ++--- src/CasParser/UnifiedResponse/Meta.php | 2 +- src/CasParser/UnifiedResponse/MutualFund.php | 5 ++- .../UnifiedResponse/MutualFund/Scheme.php | 11 +++---- src/Core/Attributes/Api.php | 7 ++++- src/Core/BaseClient.php | 5 ++- src/Core/Util.php | 10 +++--- .../CasGeneratorGenerateCasResponse.php | 6 ++-- 13 files changed, 44 insertions(+), 62 deletions(-) diff --git a/src/CasGenerator/CasGeneratorGenerateCasParams.php b/src/CasGenerator/CasGeneratorGenerateCasParams.php index ee8d0bb..7d4512f 100644 --- a/src/CasGenerator/CasGeneratorGenerateCasParams.php +++ b/src/CasGenerator/CasGeneratorGenerateCasParams.php @@ -88,7 +88,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param CasAuthority::*|null $casAuthority + * @param CasAuthority::* $casAuthority */ public static function with( string $email, diff --git a/src/CasParser/UnifiedResponse.php b/src/CasParser/UnifiedResponse.php index 91bca01..43d2b13 100644 --- a/src/CasParser/UnifiedResponse.php +++ b/src/CasParser/UnifiedResponse.php @@ -13,18 +13,13 @@ use CasParser\Core\Attributes\Api; use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -use CasParser\Core\Conversion\ListOf; final class UnifiedResponse implements BaseModel { use SdkModel; /** @var list|null $dematAccounts */ - #[Api( - 'demat_accounts', - type: new ListOf(DematAccount::class), - optional: true - )] + #[Api('demat_accounts', list: DematAccount::class, optional: true)] public ?array $dematAccounts; #[Api(optional: true)] @@ -37,7 +32,7 @@ final class UnifiedResponse implements BaseModel public ?Meta $meta; /** @var list|null $mutualFunds */ - #[Api('mutual_funds', type: new ListOf(MutualFund::class), optional: true)] + #[Api('mutual_funds', list: MutualFund::class, optional: true)] public ?array $mutualFunds; #[Api(optional: true)] @@ -54,8 +49,8 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param list|null $dematAccounts - * @param list|null $mutualFunds + * @param list $dematAccounts + * @param list $mutualFunds */ public static function with( ?array $dematAccounts = null, diff --git a/src/CasParser/UnifiedResponse/DematAccount.php b/src/CasParser/UnifiedResponse/DematAccount.php index bb7b45b..e23e721 100644 --- a/src/CasParser/UnifiedResponse/DematAccount.php +++ b/src/CasParser/UnifiedResponse/DematAccount.php @@ -73,7 +73,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param DematType::*|null $dematType + * @param DematType::* $dematType */ public static function with( ?AdditionalInfo $additionalInfo = null, diff --git a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php index a1cdfb3..647b305 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php @@ -7,7 +7,6 @@ use CasParser\Core\Attributes\Api; use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -use CasParser\Core\Conversion\ListOf; /** * Additional information specific to the demat account type. @@ -51,7 +50,7 @@ final class AdditionalInfo implements BaseModel * * @var list|null $linkedPans */ - #[Api('linked_pans', type: new ListOf('string'), optional: true)] + #[Api('linked_pans', list: 'string', optional: true)] public ?array $linkedPans; /** @@ -77,7 +76,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param list|null $linkedPans + * @param list $linkedPans */ public static function with( ?string $boStatus = null, diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php index cc9f3af..998f00d 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php @@ -12,41 +12,32 @@ use CasParser\Core\Attributes\Api; use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -use CasParser\Core\Conversion\ListOf; final class Holdings implements BaseModel { use SdkModel; /** @var list|null $aifs */ - #[Api(type: new ListOf(Aif::class), optional: true)] + #[Api(list: Aif::class, optional: true)] public ?array $aifs; /** @var list|null $corporateBonds */ - #[Api( - 'corporate_bonds', - type: new ListOf(CorporateBond::class), - optional: true - )] + #[Api('corporate_bonds', list: CorporateBond::class, optional: true)] public ?array $corporateBonds; /** @var list|null $dematMutualFunds */ - #[Api( - 'demat_mutual_funds', - type: new ListOf(DematMutualFund::class), - optional: true, - )] + #[Api('demat_mutual_funds', list: DematMutualFund::class, optional: true)] public ?array $dematMutualFunds; /** @var list|null $equities */ - #[Api(type: new ListOf(Equity::class), optional: true)] + #[Api(list: Equity::class, optional: true)] public ?array $equities; /** @var list|null $governmentSecurities */ #[Api( 'government_securities', - type: new ListOf(GovernmentSecurity::class), - optional: true, + list: GovernmentSecurity::class, + optional: true )] public ?array $governmentSecurities; @@ -61,11 +52,11 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param list|null $aifs - * @param list|null $corporateBonds - * @param list|null $dematMutualFunds - * @param list|null $equities - * @param list|null $governmentSecurities + * @param list $aifs + * @param list $corporateBonds + * @param list $dematMutualFunds + * @param list $equities + * @param list $governmentSecurities */ public static function with( ?array $aifs = null, diff --git a/src/CasParser/UnifiedResponse/Insurance.php b/src/CasParser/UnifiedResponse/Insurance.php index 34c4349..e290895 100644 --- a/src/CasParser/UnifiedResponse/Insurance.php +++ b/src/CasParser/UnifiedResponse/Insurance.php @@ -8,7 +8,6 @@ use CasParser\Core\Attributes\Api; use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -use CasParser\Core\Conversion\ListOf; final class Insurance implements BaseModel { @@ -17,8 +16,8 @@ final class Insurance implements BaseModel /** @var list|null $lifeInsurancePolicies */ #[Api( 'life_insurance_policies', - type: new ListOf(LifeInsurancePolicy::class), - optional: true, + list: LifeInsurancePolicy::class, + optional: true )] public ?array $lifeInsurancePolicies; @@ -33,7 +32,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param list|null $lifeInsurancePolicies + * @param list $lifeInsurancePolicies */ public static function with(?array $lifeInsurancePolicies = null): self { diff --git a/src/CasParser/UnifiedResponse/Meta.php b/src/CasParser/UnifiedResponse/Meta.php index 444c914..4fd4dcb 100644 --- a/src/CasParser/UnifiedResponse/Meta.php +++ b/src/CasParser/UnifiedResponse/Meta.php @@ -42,7 +42,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param CasType::*|null $casType + * @param CasType::* $casType */ public static function with( ?string $casType = null, diff --git a/src/CasParser/UnifiedResponse/MutualFund.php b/src/CasParser/UnifiedResponse/MutualFund.php index 21fdbc1..57b4a87 100644 --- a/src/CasParser/UnifiedResponse/MutualFund.php +++ b/src/CasParser/UnifiedResponse/MutualFund.php @@ -9,7 +9,6 @@ use CasParser\Core\Attributes\Api; use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -use CasParser\Core\Conversion\ListOf; final class MutualFund implements BaseModel { @@ -40,7 +39,7 @@ final class MutualFund implements BaseModel public ?string $registrar; /** @var list|null $schemes */ - #[Api(type: new ListOf(Scheme::class), optional: true)] + #[Api(list: Scheme::class, optional: true)] public ?array $schemes; /** @@ -60,7 +59,7 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param list|null $schemes + * @param list $schemes */ public static function with( ?AdditionalInfo $additionalInfo = null, diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php index a3ec40c..2b36434 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php @@ -11,7 +11,6 @@ use CasParser\Core\Attributes\Api; use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -use CasParser\Core\Conversion\ListOf; final class Scheme implements BaseModel { @@ -55,11 +54,11 @@ final class Scheme implements BaseModel * * @var list|null $nominees */ - #[Api(type: new ListOf('string'), optional: true)] + #[Api(list: 'string', optional: true)] public ?array $nominees; /** @var list|null $transactions */ - #[Api(type: new ListOf(Transaction::class), optional: true)] + #[Api(list: Transaction::class, optional: true)] public ?array $transactions; /** @@ -93,9 +92,9 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. * - * @param list|null $nominees - * @param list|null $transactions - * @param Type::*|null $type + * @param list $nominees + * @param list $transactions + * @param Type::* $type */ public static function with( ?AdditionalInfo $additionalInfo = null, diff --git a/src/Core/Attributes/Api.php b/src/Core/Attributes/Api.php index 47f5cd2..d98d284 100644 --- a/src/Core/Attributes/Api.php +++ b/src/Core/Attributes/Api.php @@ -6,6 +6,8 @@ use CasParser\Core\Conversion\Contracts\Converter; use CasParser\Core\Conversion\Contracts\ConverterSource; +use CasParser\Core\Conversion\ListOf; +use CasParser\Core\Conversion\MapOf; /** * @internal @@ -22,15 +24,18 @@ final class Api * @param class-string|Converter|string|null $type * @param class-string|Converter|null $enum * @param class-string|Converter|string|null $union + * @param class-string|Converter|string|null $list */ public function __construct( public readonly ?string $apiName = null, Converter|string|null $type = null, Converter|string|null $enum = null, Converter|string|null $union = null, + Converter|string|null $list = null, + Converter|string|null $map = null, public readonly bool $nullable = false, public readonly bool $optional = false, ) { - $this->type = $type ?? $enum ?? $union; + $this->type = $type ?? $enum ?? $union ?? ($list ? new ListOf($list) : ($map ? new MapOf($map) : null)); } } diff --git a/src/Core/BaseClient.php b/src/Core/BaseClient.php index bc56b0d..5ed95fb 100644 --- a/src/Core/BaseClient.php +++ b/src/Core/BaseClient.php @@ -146,9 +146,8 @@ protected function followRedirect( } /** - * @param bool|int|float|string|array|resource|\Traversable< - * mixed - * >|null $data + * @param bool|int|float|string|resource|\Traversable|array|null $data */ protected function sendRequest( RequestInterface $req, diff --git a/src/Core/Util.php b/src/Core/Util.php index 2af6769..d20c973 100644 --- a/src/Core/Util.php +++ b/src/Core/Util.php @@ -165,9 +165,8 @@ public static function streamIterator(StreamInterface $stream): \Iterator } /** - * @param bool|int|float|string|array|resource|\Traversable< - * mixed - * >|null $body + * @param bool|int|float|string|resource|\Traversable|array|null $body * * @return array{string, \Generator} */ @@ -202,9 +201,8 @@ public static function encodeMultipartStreaming(mixed $body): array } /** - * @param bool|int|float|string|array|resource|\Traversable< - * mixed - * >|null $body + * @param bool|int|float|string|resource|\Traversable|array|null $body */ public static function withSetBody( StreamFactoryInterface $factory, diff --git a/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php b/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php index 4a896b1..6807b50 100644 --- a/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php +++ b/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php @@ -29,10 +29,8 @@ public function __construct() * * You must use named parameters to construct any parameters with a default value. */ - public static function with( - ?string $msg = null, - ?string $status = null - ): self { + public static function with(?string $msg = null, ?string $status = null): self + { $obj = new self; null !== $msg && $obj->msg = $msg; From d5736cf656dd266165e262c6fe85a6bc0a12e5d9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 03:47:10 +0000 Subject: [PATCH 10/29] feat(php): differentiate null and omit --- composer.json | 1 + src/Contracts/CasGeneratorContract.php | 6 ++- src/Contracts/CasParserContract.php | 26 +++++++------ src/Core/Omit.php | 7 ++++ src/Core/Omittable.php | 13 +++++++ src/Core/Util.php | 11 +----- src/Services/CasGeneratorService.php | 25 ++++++------ src/Services/CasParserService.php | 54 +++++++++++++------------- 8 files changed, 81 insertions(+), 62 deletions(-) create mode 100644 src/Core/Omit.php create mode 100644 src/Core/Omittable.php diff --git a/composer.json b/composer.json index 77eee58..cdfd99a 100644 --- a/composer.json +++ b/composer.json @@ -2,6 +2,7 @@ "$schema": "https://getcomposer.org/schema.json", "autoload": { "files": [ + "src/Core/Omit.php", "src/Client.php" ], "psr-4": { diff --git a/src/Contracts/CasGeneratorContract.php b/src/Contracts/CasGeneratorContract.php index 09ebc93..5294867 100644 --- a/src/Contracts/CasGeneratorContract.php +++ b/src/Contracts/CasGeneratorContract.php @@ -8,6 +8,8 @@ use CasParser\RequestOptions; use CasParser\Responses\CasGenerator\CasGeneratorGenerateCasResponse; +use const CasParser\Core\OMIT as omit; + interface CasGeneratorContract { /** @@ -23,8 +25,8 @@ public function generateCas( $fromDate, $password, $toDate, - $casAuthority = null, - $panNo = null, + $casAuthority = omit, + $panNo = omit, ?RequestOptions $requestOptions = null, ): CasGeneratorGenerateCasResponse; } diff --git a/src/Contracts/CasParserContract.php b/src/Contracts/CasParserContract.php index e30bd89..da5b3f1 100644 --- a/src/Contracts/CasParserContract.php +++ b/src/Contracts/CasParserContract.php @@ -7,6 +7,8 @@ use CasParser\CasParser\UnifiedResponse; use CasParser\RequestOptions; +use const CasParser\Core\OMIT as omit; + interface CasParserContract { /** @@ -15,9 +17,9 @@ interface CasParserContract * @param string $pdfURL URL to the CAS PDF file */ public function camsKfintech( - $password = null, - $pdfFile = null, - $pdfURL = null, + $password = omit, + $pdfFile = omit, + $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse; @@ -27,9 +29,9 @@ public function camsKfintech( * @param string $pdfURL URL to the CAS PDF file */ public function cdsl( - $password = null, - $pdfFile = null, - $pdfURL = null, + $password = omit, + $pdfFile = omit, + $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse; @@ -39,9 +41,9 @@ public function cdsl( * @param string $pdfURL URL to the CAS PDF file */ public function nsdl( - $password = null, - $pdfFile = null, - $pdfURL = null, + $password = omit, + $pdfFile = omit, + $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse; @@ -51,9 +53,9 @@ public function nsdl( * @param string $pdfURL URL to the CAS PDF file */ public function smartParse( - $password = null, - $pdfFile = null, - $pdfURL = null, + $password = omit, + $pdfFile = omit, + $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse; } diff --git a/src/Core/Omit.php b/src/Core/Omit.php new file mode 100644 index 0000000..908ff71 --- /dev/null +++ b/src/Core/Omit.php @@ -0,0 +1,7 @@ + $arr - * @param list $keys * * @return array */ - public static function array_filter_null(array $arr, array $keys): array + public static function array_filter_omit(array $arr): array { - foreach ($keys as $key) { - if (array_key_exists($key, $arr) && is_null($arr[$key])) { - unset($arr[$key]); - } - } - - return $arr; + return array_filter($arr, fn ($v, $_) => OMIT !== $v, ARRAY_FILTER_USE_BOTH); } /** diff --git a/src/Services/CasGeneratorService.php b/src/Services/CasGeneratorService.php index 3609dad..e75f440 100644 --- a/src/Services/CasGeneratorService.php +++ b/src/Services/CasGeneratorService.php @@ -13,6 +13,8 @@ use CasParser\RequestOptions; use CasParser\Responses\CasGenerator\CasGeneratorGenerateCasResponse; +use const CasParser\Core\OMIT as omit; + final class CasGeneratorService implements CasGeneratorContract { public function __construct(private Client $client) {} @@ -33,19 +35,20 @@ public function generateCas( $fromDate, $password, $toDate, - $casAuthority = null, - $panNo = null, + $casAuthority = omit, + $panNo = omit, ?RequestOptions $requestOptions = null, ): CasGeneratorGenerateCasResponse { - $args = [ - 'email' => $email, - 'fromDate' => $fromDate, - 'password' => $password, - 'toDate' => $toDate, - 'casAuthority' => $casAuthority, - 'panNo' => $panNo, - ]; - $args = Util::array_filter_null($args, ['casAuthority', 'panNo']); + $args = Util::array_filter_omit( + [ + 'email' => $email, + 'fromDate' => $fromDate, + 'password' => $password, + 'toDate' => $toDate, + 'casAuthority' => $casAuthority, + 'panNo' => $panNo, + ], + ); [$parsed, $options] = CasGeneratorGenerateCasParams::parseRequest( $args, $requestOptions diff --git a/src/Services/CasParserService.php b/src/Services/CasParserService.php index 3e6e5f8..6dec974 100644 --- a/src/Services/CasParserService.php +++ b/src/Services/CasParserService.php @@ -15,6 +15,8 @@ use CasParser\Core\Util; use CasParser\RequestOptions; +use const CasParser\Core\OMIT as omit; + final class CasParserService implements CasParserContract { public function __construct(private Client $client) {} @@ -28,15 +30,14 @@ public function __construct(private Client $client) {} * @param string $pdfURL URL to the CAS PDF file */ public function camsKfintech( - $password = null, - $pdfFile = null, - $pdfURL = null, + $password = omit, + $pdfFile = omit, + $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse { - $args = [ - 'password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL, - ]; - $args = Util::array_filter_null($args, ['password', 'pdfFile', 'pdfURL']); + $args = Util::array_filter_omit( + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL] + ); [$parsed, $options] = CasParserCamsKfintechParams::parseRequest( $args, $requestOptions @@ -61,15 +62,14 @@ public function camsKfintech( * @param string $pdfURL URL to the CAS PDF file */ public function cdsl( - $password = null, - $pdfFile = null, - $pdfURL = null, + $password = omit, + $pdfFile = omit, + $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse { - $args = [ - 'password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL, - ]; - $args = Util::array_filter_null($args, ['password', 'pdfFile', 'pdfURL']); + $args = Util::array_filter_omit( + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL] + ); [$parsed, $options] = CasParserCdslParams::parseRequest( $args, $requestOptions @@ -94,15 +94,14 @@ public function cdsl( * @param string $pdfURL URL to the CAS PDF file */ public function nsdl( - $password = null, - $pdfFile = null, - $pdfURL = null, + $password = omit, + $pdfFile = omit, + $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse { - $args = [ - 'password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL, - ]; - $args = Util::array_filter_null($args, ['password', 'pdfFile', 'pdfURL']); + $args = Util::array_filter_omit( + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL] + ); [$parsed, $options] = CasParserNsdlParams::parseRequest( $args, $requestOptions @@ -127,15 +126,14 @@ public function nsdl( * @param string $pdfURL URL to the CAS PDF file */ public function smartParse( - $password = null, - $pdfFile = null, - $pdfURL = null, + $password = omit, + $pdfFile = omit, + $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse { - $args = [ - 'password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL, - ]; - $args = Util::array_filter_null($args, ['password', 'pdfFile', 'pdfURL']); + $args = Util::array_filter_omit( + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL] + ); [$parsed, $options] = CasParserSmartParseParams::parseRequest( $args, $requestOptions From 39bcd29aa689ba5505428c732b63d07fc049c010 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 03:53:00 +0000 Subject: [PATCH 11/29] fix: streaming internals --- src/Core/Attributes/Api.php | 3 ++- src/Core/Contracts/{CloseableStream.php => BaseStream.php} | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) rename src/Core/Contracts/{CloseableStream.php => BaseStream.php} (73%) diff --git a/src/Core/Attributes/Api.php b/src/Core/Attributes/Api.php index d98d284..fd2b006 100644 --- a/src/Core/Attributes/Api.php +++ b/src/Core/Attributes/Api.php @@ -23,8 +23,9 @@ final class Api /** * @param class-string|Converter|string|null $type * @param class-string|Converter|null $enum - * @param class-string|Converter|string|null $union + * @param class-string|Converter|null $union * @param class-string|Converter|string|null $list + * @param class-string|Converter|string|null $map */ public function __construct( public readonly ?string $apiName = null, diff --git a/src/Core/Contracts/CloseableStream.php b/src/Core/Contracts/BaseStream.php similarity index 73% rename from src/Core/Contracts/CloseableStream.php rename to src/Core/Contracts/BaseStream.php index ef685cf..f235665 100644 --- a/src/Core/Contracts/CloseableStream.php +++ b/src/Core/Contracts/BaseStream.php @@ -7,9 +7,9 @@ /** * @template TInner * - * @extends \IteratorAggregate + * @extends \IteratorAggregate */ -interface CloseableStream extends \IteratorAggregate +interface BaseStream extends \IteratorAggregate { /** * Manually force the stream to close early. From 26437b521827b8cf5393feee1e8af173e18d7a22 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 03:56:17 +0000 Subject: [PATCH 12/29] feat(refactor)!: clean up pagination, errors, as well as request methods --- README.md | 2 +- composer.json | 2 +- src/{Core/Omittable.php => Core.php} | 2 + src/Core/BaseClient.php | 2 +- src/Core/Concerns/SdkParams.php | 4 +- src/Core/Contracts/BasePage.php | 21 +++++- src/{ => Core}/Errors/APIConnectionError.php | 2 +- src/{ => Core}/Errors/APIError.php | 4 +- src/{ => Core}/Errors/APIStatusError.php | 2 +- src/{ => Core}/Errors/APITimeoutError.php | 2 +- src/{ => Core}/Errors/AuthenticationError.php | 2 +- src/{ => Core}/Errors/BadRequestError.php | 2 +- .../Errors/CasParserError.php} | 4 +- src/{ => Core}/Errors/ConflictError.php | 2 +- src/{ => Core}/Errors/InternalServerError.php | 2 +- src/{ => Core}/Errors/NotFoundError.php | 2 +- .../Errors/PermissionDeniedError.php | 2 +- src/{ => Core}/Errors/RateLimitError.php | 2 +- .../Errors/UnprocessableEntityError.php | 2 +- src/Core/Omit.php | 7 -- src/Core/Pagination/AbstractPage.php | 20 +++--- src/Core/Util.php | 72 +++++++++---------- src/Services/CasGeneratorService.php | 8 +-- src/Services/CasParserService.php | 29 +++----- 24 files changed, 98 insertions(+), 101 deletions(-) rename src/{Core/Omittable.php => Core.php} (78%) rename src/{ => Core}/Errors/APIConnectionError.php (80%) rename src/{ => Core}/Errors/APIError.php (85%) rename src/{ => Core}/Errors/APIStatusError.php (98%) rename src/{ => Core}/Errors/APITimeoutError.php (93%) rename src/{ => Core}/Errors/AuthenticationError.php (81%) rename src/{ => Core}/Errors/BadRequestError.php (80%) rename src/{Errors/Error.php => Core/Errors/CasParserError.php} (78%) rename src/{ => Core}/Errors/ConflictError.php (80%) rename src/{ => Core}/Errors/InternalServerError.php (81%) rename src/{ => Core}/Errors/NotFoundError.php (80%) rename src/{ => Core}/Errors/PermissionDeniedError.php (81%) rename src/{ => Core}/Errors/RateLimitError.php (80%) rename src/{ => Core}/Errors/UnprocessableEntityError.php (82%) delete mode 100644 src/Core/Omit.php diff --git a/README.md b/README.md index f859fcb..990e035 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ When the library is unable to connect to the API, or if the API returns a non-su ```php casParser->smartParse(); diff --git a/composer.json b/composer.json index cdfd99a..d9e0bf6 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "$schema": "https://getcomposer.org/schema.json", "autoload": { "files": [ - "src/Core/Omit.php", + "src/Core.php", "src/Client.php" ], "psr-4": { diff --git a/src/Core/Omittable.php b/src/Core.php similarity index 78% rename from src/Core/Omittable.php rename to src/Core.php index b58026b..55b7777 100644 --- a/src/Core/Omittable.php +++ b/src/Core.php @@ -11,3 +11,5 @@ enum Omittable { case OMIT; } + +const OMIT = Omittable::OMIT; diff --git a/src/Core/BaseClient.php b/src/Core/BaseClient.php index 5ed95fb..03dcf51 100644 --- a/src/Core/BaseClient.php +++ b/src/Core/BaseClient.php @@ -4,7 +4,7 @@ namespace CasParser\Core; -use CasParser\Errors\APIStatusError; +use CasParser\Core\Errors\APIStatusError; use CasParser\RequestOptions; use Http\Discovery\Psr17FactoryDiscovery; use Http\Discovery\Psr18ClientDiscovery; diff --git a/src/Core/Concerns/SdkParams.php b/src/Core/Concerns/SdkParams.php index 8338148..71d2e80 100644 --- a/src/Core/Concerns/SdkParams.php +++ b/src/Core/Concerns/SdkParams.php @@ -6,6 +6,7 @@ use CasParser\Core\Conversion; use CasParser\Core\Conversion\DumpState; +use CasParser\Core\Util; use CasParser\RequestOptions; /** @@ -29,9 +30,10 @@ trait SdkParams */ public static function parseRequest(array|self|null $params, array|RequestOptions|null $options): array { + $value = is_array($params) ? Util::array_filter_omit($params) : $params; $converter = self::converter(); $state = new DumpState; - $dumped = (array) Conversion::dump($converter, value: $params, state: $state); + $dumped = (array) Conversion::dump($converter, value: $value, state: $state); $opts = RequestOptions::parse($options); // @phpstan-ignore-line if (!$state->canRetry) { diff --git a/src/Core/Contracts/BasePage.php b/src/Core/Contracts/BasePage.php index 93a441b..61b4b1c 100644 --- a/src/Core/Contracts/BasePage.php +++ b/src/Core/Contracts/BasePage.php @@ -9,14 +9,31 @@ use Psr\Http\Message\ResponseInterface; /** - * @internal + * @template Item + * + * @extends \IteratorAggregate */ -interface BasePage +interface BasePage extends \IteratorAggregate { + /** + * @internal + */ public function __construct( BaseClient $client, PageRequestOptions $options, ResponseInterface $response, mixed $body, ); + + public function hasNextPage(): bool; + + /** + * @return list + */ + public function getPaginatedItems(): array; + + /** + * @return static + */ + public function getNextPage(): static; } diff --git a/src/Errors/APIConnectionError.php b/src/Core/Errors/APIConnectionError.php similarity index 80% rename from src/Errors/APIConnectionError.php rename to src/Core/Errors/APIConnectionError.php index 7e0af87..c5b0580 100644 --- a/src/Errors/APIConnectionError.php +++ b/src/Core/Errors/APIConnectionError.php @@ -1,6 +1,6 @@ + * @implements BasePage */ -abstract class AbstractPage implements \IteratorAggregate, BasePage +abstract class AbstractPage implements BasePage { public function __construct( protected BaseClient $client, @@ -25,8 +25,6 @@ public function __construct( protected mixed $body, ) {} - abstract public function nextPageRequestOptions(): ?PageRequestOptions; - /** * @return list */ @@ -49,13 +47,13 @@ public function hasNextPage(): bool * * @return static of AbstractPage * - * @throws Error + * @throws APIStatusError */ public function getNextPage(): static { $nextOptions = $this->nextPageRequestOptions(); if (!$nextOptions) { - throw new Error( + throw new \RuntimeException( 'No next page expected; please check `.hasNextPage()` before calling `.getNextPage()`.' ); } @@ -78,7 +76,7 @@ public function getNextPage(): static * * @return \Generator */ - public function iterPages(): \Generator + public function getIterator(): \Generator { $page = $this; @@ -95,12 +93,14 @@ public function iterPages(): \Generator * * @return \Generator */ - public function getIterator(): \Generator + public function pagingEachItem(): \Generator { - foreach ($this->iterPages() as $page) { + foreach ($this as $page) { foreach ($page->getPaginatedItems() as $item) { yield $item; } } } + + abstract protected function nextPageRequestOptions(): ?PageRequestOptions; } diff --git a/src/Core/Util.php b/src/Core/Util.php index b0a32a9..3262615 100644 --- a/src/Core/Util.php +++ b/src/Core/Util.php @@ -164,42 +164,6 @@ public static function streamIterator(StreamInterface $stream): \Iterator } } - /** - * @param bool|int|float|string|resource|\Traversable|array|null $body - * - * @return array{string, \Generator} - */ - public static function encodeMultipartStreaming(mixed $body): array - { - $boundary = rtrim(strtr(base64_encode(random_bytes(60)), '+/', '-_'), '='); - $gen = (function () use ($boundary, $body) { - $closing = []; - - try { - if (is_array($body) || is_object($body)) { - foreach ((array) $body as $key => $val) { - foreach (static::writeMultipartChunk(boundary: $boundary, key: $key, val: $val, closing: $closing) as $chunk) { - yield $chunk; - } - } - } else { - foreach (static::writeMultipartChunk(boundary: $boundary, key: null, val: $body, closing: $closing) as $chunk) { - yield $chunk; - } - } - - yield "--{$boundary}--\r\n"; - } finally { - foreach ($closing as $c) { - $c(); - } - } - })(); - - return [$boundary, $gen]; - } - /** * @param bool|int|float|string|resource|\Traversable|array|null $body @@ -415,4 +379,40 @@ private static function writeMultipartChunk( yield $chunk; } } + + /** + * @param bool|int|float|string|resource|\Traversable|array|null $body + * + * @return array{string, \Generator} + */ + private static function encodeMultipartStreaming(mixed $body): array + { + $boundary = rtrim(strtr(base64_encode(random_bytes(60)), '+/', '-_'), '='); + $gen = (function () use ($boundary, $body) { + $closing = []; + + try { + if (is_array($body) || is_object($body)) { + foreach ((array) $body as $key => $val) { + foreach (static::writeMultipartChunk(boundary: $boundary, key: $key, val: $val, closing: $closing) as $chunk) { + yield $chunk; + } + } + } else { + foreach (static::writeMultipartChunk(boundary: $boundary, key: null, val: $body, closing: $closing) as $chunk) { + yield $chunk; + } + } + + yield "--{$boundary}--\r\n"; + } finally { + foreach ($closing as $c) { + $c(); + } + } + })(); + + return [$boundary, $gen]; + } } diff --git a/src/Services/CasGeneratorService.php b/src/Services/CasGeneratorService.php index e75f440..a42fe81 100644 --- a/src/Services/CasGeneratorService.php +++ b/src/Services/CasGeneratorService.php @@ -9,7 +9,6 @@ use CasParser\Client; use CasParser\Contracts\CasGeneratorContract; use CasParser\Core\Conversion; -use CasParser\Core\Util; use CasParser\RequestOptions; use CasParser\Responses\CasGenerator\CasGeneratorGenerateCasResponse; @@ -39,7 +38,7 @@ public function generateCas( $panNo = omit, ?RequestOptions $requestOptions = null, ): CasGeneratorGenerateCasResponse { - $args = Util::array_filter_omit( + [$parsed, $options] = CasGeneratorGenerateCasParams::parseRequest( [ 'email' => $email, 'fromDate' => $fromDate, @@ -48,10 +47,7 @@ public function generateCas( 'casAuthority' => $casAuthority, 'panNo' => $panNo, ], - ); - [$parsed, $options] = CasGeneratorGenerateCasParams::parseRequest( - $args, - $requestOptions + $requestOptions, ); $resp = $this->client->request( method: 'post', diff --git a/src/Services/CasParserService.php b/src/Services/CasParserService.php index 6dec974..bdfe2dd 100644 --- a/src/Services/CasParserService.php +++ b/src/Services/CasParserService.php @@ -12,7 +12,6 @@ use CasParser\Client; use CasParser\Contracts\CasParserContract; use CasParser\Core\Conversion; -use CasParser\Core\Util; use CasParser\RequestOptions; use const CasParser\Core\OMIT as omit; @@ -35,12 +34,9 @@ public function camsKfintech( $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse { - $args = Util::array_filter_omit( - ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL] - ); [$parsed, $options] = CasParserCamsKfintechParams::parseRequest( - $args, - $requestOptions + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], + $requestOptions, ); $resp = $this->client->request( method: 'post', @@ -67,12 +63,9 @@ public function cdsl( $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse { - $args = Util::array_filter_omit( - ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL] - ); [$parsed, $options] = CasParserCdslParams::parseRequest( - $args, - $requestOptions + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], + $requestOptions, ); $resp = $this->client->request( method: 'post', @@ -99,12 +92,9 @@ public function nsdl( $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse { - $args = Util::array_filter_omit( - ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL] - ); [$parsed, $options] = CasParserNsdlParams::parseRequest( - $args, - $requestOptions + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], + $requestOptions, ); $resp = $this->client->request( method: 'post', @@ -131,12 +121,9 @@ public function smartParse( $pdfURL = omit, ?RequestOptions $requestOptions = null, ): UnifiedResponse { - $args = Util::array_filter_omit( - ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL] - ); [$parsed, $options] = CasParserSmartParseParams::parseRequest( - $args, - $requestOptions + ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], + $requestOptions, ); $resp = $this->client->request( method: 'post', From 666f37473888855fcc884cb9d851d43ce62aa75f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 26 Aug 2025 03:58:46 +0000 Subject: [PATCH 13/29] feat(refactor)!: namespacing cleanup --- .../CasGenerator/CasGeneratorGenerateCasResponse.php | 2 +- src/Client.php | 4 ++-- .../ServiceContracts}/CasGeneratorContract.php | 4 ++-- .../ServiceContracts}/CasParserContract.php | 2 +- src/{ => Core}/Services/CasGeneratorService.php | 6 +++--- src/{ => Core}/Services/CasParserService.php | 4 ++-- tests/{Resources => Services}/CasGeneratorTest.php | 2 +- tests/{Resources => Services}/CasParserTest.php | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) rename src/{Responses => }/CasGenerator/CasGeneratorGenerateCasResponse.php (96%) rename src/{Contracts => Core/ServiceContracts}/CasGeneratorContract.php (90%) rename src/{Contracts => Core/ServiceContracts}/CasParserContract.php (97%) rename src/{ => Core}/Services/CasGeneratorService.php (93%) rename src/{ => Core}/Services/CasParserService.php (98%) rename tests/{Resources => Services}/CasGeneratorTest.php (98%) rename tests/{Resources => Services}/CasParserTest.php (98%) diff --git a/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php b/src/CasGenerator/CasGeneratorGenerateCasResponse.php similarity index 96% rename from src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php rename to src/CasGenerator/CasGeneratorGenerateCasResponse.php index 6807b50..36aea2a 100644 --- a/src/Responses/CasGenerator/CasGeneratorGenerateCasResponse.php +++ b/src/CasGenerator/CasGeneratorGenerateCasResponse.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace CasParser\Responses\CasGenerator; +namespace CasParser\CasGenerator; use CasParser\Core\Attributes\Api; use CasParser\Core\Concerns\SdkModel; diff --git a/src/Client.php b/src/Client.php index 7182ad1..ae5e493 100644 --- a/src/Client.php +++ b/src/Client.php @@ -5,8 +5,8 @@ namespace CasParser; use CasParser\Core\BaseClient; -use CasParser\Services\CasGeneratorService; -use CasParser\Services\CasParserService; +use CasParser\Core\Services\CasGeneratorService; +use CasParser\Core\Services\CasParserService; class Client extends BaseClient { diff --git a/src/Contracts/CasGeneratorContract.php b/src/Core/ServiceContracts/CasGeneratorContract.php similarity index 90% rename from src/Contracts/CasGeneratorContract.php rename to src/Core/ServiceContracts/CasGeneratorContract.php index 5294867..2ee3281 100644 --- a/src/Contracts/CasGeneratorContract.php +++ b/src/Core/ServiceContracts/CasGeneratorContract.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace CasParser\Contracts; +namespace CasParser\Core\ServiceContracts; use CasParser\CasGenerator\CasGeneratorGenerateCasParams\CasAuthority; +use CasParser\CasGenerator\CasGeneratorGenerateCasResponse; use CasParser\RequestOptions; -use CasParser\Responses\CasGenerator\CasGeneratorGenerateCasResponse; use const CasParser\Core\OMIT as omit; diff --git a/src/Contracts/CasParserContract.php b/src/Core/ServiceContracts/CasParserContract.php similarity index 97% rename from src/Contracts/CasParserContract.php rename to src/Core/ServiceContracts/CasParserContract.php index da5b3f1..e2fb15d 100644 --- a/src/Contracts/CasParserContract.php +++ b/src/Core/ServiceContracts/CasParserContract.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace CasParser\Contracts; +namespace CasParser\Core\ServiceContracts; use CasParser\CasParser\UnifiedResponse; use CasParser\RequestOptions; diff --git a/src/Services/CasGeneratorService.php b/src/Core/Services/CasGeneratorService.php similarity index 93% rename from src/Services/CasGeneratorService.php rename to src/Core/Services/CasGeneratorService.php index a42fe81..1d39f17 100644 --- a/src/Services/CasGeneratorService.php +++ b/src/Core/Services/CasGeneratorService.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace CasParser\Services; +namespace CasParser\Core\Services; use CasParser\CasGenerator\CasGeneratorGenerateCasParams; use CasParser\CasGenerator\CasGeneratorGenerateCasParams\CasAuthority; +use CasParser\CasGenerator\CasGeneratorGenerateCasResponse; use CasParser\Client; -use CasParser\Contracts\CasGeneratorContract; use CasParser\Core\Conversion; +use CasParser\Core\ServiceContracts\CasGeneratorContract; use CasParser\RequestOptions; -use CasParser\Responses\CasGenerator\CasGeneratorGenerateCasResponse; use const CasParser\Core\OMIT as omit; diff --git a/src/Services/CasParserService.php b/src/Core/Services/CasParserService.php similarity index 98% rename from src/Services/CasParserService.php rename to src/Core/Services/CasParserService.php index bdfe2dd..51f84d5 100644 --- a/src/Services/CasParserService.php +++ b/src/Core/Services/CasParserService.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace CasParser\Services; +namespace CasParser\Core\Services; use CasParser\CasParser\CasParserCamsKfintechParams; use CasParser\CasParser\CasParserCdslParams; @@ -10,8 +10,8 @@ use CasParser\CasParser\CasParserSmartParseParams; use CasParser\CasParser\UnifiedResponse; use CasParser\Client; -use CasParser\Contracts\CasParserContract; use CasParser\Core\Conversion; +use CasParser\Core\ServiceContracts\CasParserContract; use CasParser\RequestOptions; use const CasParser\Core\OMIT as omit; diff --git a/tests/Resources/CasGeneratorTest.php b/tests/Services/CasGeneratorTest.php similarity index 98% rename from tests/Resources/CasGeneratorTest.php rename to tests/Services/CasGeneratorTest.php index 27c281a..12120b4 100644 --- a/tests/Resources/CasGeneratorTest.php +++ b/tests/Services/CasGeneratorTest.php @@ -1,6 +1,6 @@ Date: Wed, 27 Aug 2025 04:58:44 +0000 Subject: [PATCH 14/29] chore(internal): refactored internal codepaths --- src/Core/BaseClient.php | 78 +++++++++++++++------- src/Core/Concerns/SdkModel.php | 2 +- src/Core/Contracts/BasePage.php | 18 +++-- src/Core/Contracts/BaseStream.php | 15 +++++ src/Core/Errors/APIStatusError.php | 16 ++--- src/Core/Pagination/AbstractPage.php | 41 ++++++------ src/Core/Pagination/PageRequestOptions.php | 73 -------------------- src/Core/Services/CasGeneratorService.php | 12 ++-- src/Core/Services/CasParserService.php | 33 +++++---- src/Core/Util.php | 55 +++++++++++---- src/RequestOptions.php | 6 +- 11 files changed, 173 insertions(+), 176 deletions(-) delete mode 100644 src/Core/Pagination/PageRequestOptions.php diff --git a/src/Core/BaseClient.php b/src/Core/BaseClient.php index 03dcf51..111545a 100644 --- a/src/Core/BaseClient.php +++ b/src/Core/BaseClient.php @@ -4,6 +4,10 @@ namespace CasParser\Core; +use CasParser\Core\Contracts\BasePage; +use CasParser\Core\Contracts\BaseStream; +use CasParser\Core\Conversion\Contracts\Converter; +use CasParser\Core\Conversion\Contracts\ConverterSource; use CasParser\Core\Errors\APIStatusError; use CasParser\RequestOptions; use Http\Discovery\Psr17FactoryDiscovery; @@ -16,6 +20,15 @@ use Psr\Http\Message\UriFactoryInterface; use Psr\Http\Message\UriInterface; +/** + * @phpstan-type normalized_request = array{ + * method: string, + * path: string, + * query: array, + * headers: array>, + * body: mixed, + * } + */ class BaseClient { protected UriInterface $baseUrl; @@ -48,6 +61,8 @@ public function __construct( * @param string|list $path * @param array $query * @param array $headers + * @param class-string> $page + * @param class-string> $stream */ public function request( string $method, @@ -55,30 +70,47 @@ public function request( array $query = [], array $headers = [], mixed $body = null, + string|Converter|ConverterSource|null $convert = null, + ?string $page = null, + ?string $stream = null, mixed $options = [], ): mixed { // @phpstan-ignore-next-line - [$req, $opts] = $this->buildRequest(method: $method, path: $path, query: $query, headers: $headers, opts: $options); + [$req, $opts] = $this->buildRequest(method: $method, path: $path, query: $query, headers: $headers, body: $body, opts: $options); + ['method' => $method, 'path' => $uri, 'headers' => $headers] = $req; + + $req = $this->requestFactory->createRequest($method, uri: $uri); + $req = Util::withSetHeaders($req, headers: $headers); // @phpstan-ignore-next-line $rsp = $this->sendRequest($req, data: $body, opts: $opts, redirectCount: 0, retryCount: 0); - if (204 == $rsp->getStatusCode()) { - return null; // Handle 204 No Content + + $decoded = Util::decodeContent($rsp); + + if (!is_null($stream)) { + return new $stream( + convert: $convert, + request: $req, + response: $rsp, + stream: $decoded + ); } - return Util::decodeContent($rsp); - } + if (!is_null($page)) { + return new $page( + convert: $convert, + client: $this, + request: $req, + options: $opts, + data: $decoded, + ); + } - /** - * @template Item - * @template T of Pagination\AbstractPage - * - * @param T $page - */ - public function requestApiList(object $page, RequestOptions $options): ResponseInterface - { - // @phpstan-ignore-next-line - return null; + if (!is_null($convert)) { + return Conversion::coerce($convert, value: $decoded); + } + + return $decoded; } /** @return array */ @@ -91,7 +123,7 @@ protected function authHeaders(): array * @param string|list $path * @param array $query * @param array|null> $headers - * @param array{ + * @param RequestOptions|array{ * timeout?: float|null, * maxRetries?: int|null, * initialRetryDelay?: float|null, @@ -99,16 +131,17 @@ protected function authHeaders(): array * extraHeaders?: list|null, * extraQueryParams?: list|null, * extraBodyParams?: list|null, - * }|RequestOptions|null $opts + * }|null $opts * - * @return array{RequestInterface, RequestOptions} + * @return array{normalized_request, RequestOptions} */ protected function buildRequest( string $method, string|array $path, array $query, array $headers, - array|RequestOptions|null $opts, + mixed $body, + RequestOptions|array|null $opts, ): array { $opts = [...$this->options->__serialize(), ...RequestOptions::parse($opts)->__serialize()]; $options = new RequestOptions(...$opts); @@ -117,16 +150,15 @@ protected function buildRequest( /** @var array $mergedQuery */ $mergedQuery = array_merge_recursive($query, $options->extraQueryParams); - $uri = Util::joinUri($this->baseUrl, path: $parsedPath, query: $mergedQuery); + $uri = Util::joinUri($this->baseUrl, path: $parsedPath, query: $mergedQuery)->__toString(); - /** @var array> $mergedHeaders */ + /** @var array|null> $mergedHeaders */ $mergedHeaders = [...$this->headers, ...$this->authHeaders(), ...$headers, ...$options->extraHeaders, ]; - $req = $this->requestFactory->createRequest(strtoupper($method), uri: $uri); - $req = Util::withSetHeaders($req, headers: $mergedHeaders); + $req = ['method' => strtoupper($method), 'path' => $uri, 'query' => $mergedQuery, 'headers' => $mergedHeaders, 'body' => $body]; return [$req, $options]; } diff --git a/src/Core/Concerns/SdkModel.php b/src/Core/Concerns/SdkModel.php index 8acf1dd..388d394 100644 --- a/src/Core/Concerns/SdkModel.php +++ b/src/Core/Concerns/SdkModel.php @@ -60,7 +60,7 @@ public function __debugInfo(): array */ public function __toString(): string { - return json_encode($this->__debugInfo(), flags: JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) ?: ''; + return Util::prettyEncodeJson($this->__debugInfo()); } /** diff --git a/src/Core/Contracts/BasePage.php b/src/Core/Contracts/BasePage.php index 61b4b1c..372cd75 100644 --- a/src/Core/Contracts/BasePage.php +++ b/src/Core/Contracts/BasePage.php @@ -4,9 +4,10 @@ namespace CasParser\Core\Contracts; -use CasParser\Core\BaseClient; -use CasParser\Core\Pagination\PageRequestOptions; -use Psr\Http\Message\ResponseInterface; +use CasParser\Client; +use CasParser\Core\Conversion\Contracts\Converter; +use CasParser\Core\Conversion\Contracts\ConverterSource; +use CasParser\RequestOptions; /** * @template Item @@ -17,12 +18,15 @@ interface BasePage extends \IteratorAggregate { /** * @internal + * + * @param array $request */ public function __construct( - BaseClient $client, - PageRequestOptions $options, - ResponseInterface $response, - mixed $body, + Converter|ConverterSource|string $convert, + Client $client, + array $request, + RequestOptions $options, + mixed $data, ); public function hasNextPage(): bool; diff --git a/src/Core/Contracts/BaseStream.php b/src/Core/Contracts/BaseStream.php index f235665..b37ce42 100644 --- a/src/Core/Contracts/BaseStream.php +++ b/src/Core/Contracts/BaseStream.php @@ -4,6 +4,11 @@ namespace CasParser\Core\Contracts; +use CasParser\Core\Conversion\Contracts\Converter; +use CasParser\Core\Conversion\Contracts\ConverterSource; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; + /** * @template TInner * @@ -11,6 +16,16 @@ */ interface BaseStream extends \IteratorAggregate { + /** + * @param \Generator $stream + */ + public function __construct( + Converter|ConverterSource|string $convert, + RequestInterface $request, + ResponseInterface $response, + \Generator $stream, + ); + /** * Manually force the stream to close early. * Iterating through will automatically close as well. diff --git a/src/Core/Errors/APIStatusError.php b/src/Core/Errors/APIStatusError.php index 373dbab..768d2c2 100644 --- a/src/Core/Errors/APIStatusError.php +++ b/src/Core/Errors/APIStatusError.php @@ -2,9 +2,9 @@ namespace CasParser\Core\Errors; +use CasParser\Core\Util; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\StreamInterface; class APIStatusError extends APIError { @@ -23,8 +23,8 @@ public function __construct( $this->status = $response->getStatusCode(); $summary = 'Status: '.$this->status.PHP_EOL - .'Response Body: '.self::fmtBody($response->getBody()).PHP_EOL - .'Request Body: '.self::fmtBody($request->getBody()).PHP_EOL; + .'Response Body: '.Util::prettyEncodeJson(Util::decodeJson($response->getBody())).PHP_EOL + .'Request Body: '.Util::prettyEncodeJson(Util::decodeJson($request->getBody())).PHP_EOL; if ('' != $message) { $summary .= $message.PHP_EOL.$summary; @@ -35,7 +35,8 @@ public function __construct( public static function from( RequestInterface $request, - ResponseInterface $response + ResponseInterface $response, + string $message = '' ): self { $status = $response->getStatusCode(); @@ -51,11 +52,6 @@ public static function from( default => APIStatusError::class }; - return new $cls(request: $request, response: $response); - } - - private static function fmtBody(StreamInterface $body): string - { - return json_encode(json_decode($body->__toString() ?: ''), JSON_PRETTY_PRINT) ?: ''; + return new $cls(request: $request, response: $response, message: $message); } } diff --git a/src/Core/Pagination/AbstractPage.php b/src/Core/Pagination/AbstractPage.php index 7796b75..9222737 100644 --- a/src/Core/Pagination/AbstractPage.php +++ b/src/Core/Pagination/AbstractPage.php @@ -4,10 +4,12 @@ namespace CasParser\Core\Pagination; -use CasParser\Core\BaseClient; +use CasParser\Client; use CasParser\Core\Contracts\BasePage; +use CasParser\Core\Conversion\Contracts\Converter; +use CasParser\Core\Conversion\Contracts\ConverterSource; use CasParser\Core\Errors\APIStatusError; -use Psr\Http\Message\ResponseInterface; +use CasParser\RequestOptions; /** * @internal @@ -15,14 +17,17 @@ * @template Item * * @implements BasePage + * + * @phpstan-import-type normalized_request from \CasParser\Core\BaseClient */ abstract class AbstractPage implements BasePage { public function __construct( - protected BaseClient $client, - protected PageRequestOptions $options, - protected ResponseInterface $response, - protected mixed $body, + protected Converter|ConverterSource|string $convert, + protected Client $client, + protected array $request, + protected RequestOptions $options, + protected mixed $data, ) {} /** @@ -37,7 +42,7 @@ public function hasNextPage(): bool return false; } - return null != $this->nextPageRequestOptions(); + return null != $this->nextRequest(); } /** @@ -51,24 +56,17 @@ public function hasNextPage(): bool */ public function getNextPage(): static { - $nextOptions = $this->nextPageRequestOptions(); - if (!$nextOptions) { + $next = $this->nextRequest(); + if (!$next) { throw new \RuntimeException( 'No next page expected; please check `.hasNextPage()` before calling `.getNextPage()`.' ); } - $response = $this->client->requestApiList($this, $nextOptions); - - /** @var static of AbstractPage $nextPage */ - $nextPage = new static( - client: $this->client, - options: $nextOptions, - response: $response, - body: $response->getBody() - ); + [$req, $opts] = $next; - return $nextPage; + // @phpstan-ignore-next-line + return $this->client->request(...$req, convert: $this->convert, page: $this, options: $opts); } /** @@ -102,5 +100,8 @@ public function pagingEachItem(): \Generator } } - abstract protected function nextPageRequestOptions(): ?PageRequestOptions; + /** + * @return array{normalized_request, RequestOptions} + */ + abstract protected function nextRequest(): ?array; } diff --git a/src/Core/Pagination/PageRequestOptions.php b/src/Core/Pagination/PageRequestOptions.php deleted file mode 100644 index 0c3b2b7..0000000 --- a/src/Core/Pagination/PageRequestOptions.php +++ /dev/null @@ -1,73 +0,0 @@ -client->request( + + // @phpstan-ignore-next-line; + return $this->client->request( method: 'post', path: 'v4/generate', body: (object) $parsed, options: $options, - ); - - // @phpstan-ignore-next-line; - return Conversion::coerce( - CasGeneratorGenerateCasResponse::class, - value: $resp + convert: CasGeneratorGenerateCasResponse::class, ); } } diff --git a/src/Core/Services/CasParserService.php b/src/Core/Services/CasParserService.php index 51f84d5..ae635e1 100644 --- a/src/Core/Services/CasParserService.php +++ b/src/Core/Services/CasParserService.php @@ -10,7 +10,6 @@ use CasParser\CasParser\CasParserSmartParseParams; use CasParser\CasParser\UnifiedResponse; use CasParser\Client; -use CasParser\Core\Conversion; use CasParser\Core\ServiceContracts\CasParserContract; use CasParser\RequestOptions; @@ -38,15 +37,15 @@ public function camsKfintech( ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], $requestOptions, ); - $resp = $this->client->request( + + // @phpstan-ignore-next-line; + return $this->client->request( method: 'post', path: 'v4/cams_kfintech/parse', body: (object) $parsed, options: $options, + convert: UnifiedResponse::class, ); - - // @phpstan-ignore-next-line; - return Conversion::coerce(UnifiedResponse::class, value: $resp); } /** @@ -67,15 +66,15 @@ public function cdsl( ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], $requestOptions, ); - $resp = $this->client->request( + + // @phpstan-ignore-next-line; + return $this->client->request( method: 'post', path: 'v4/cdsl/parse', body: (object) $parsed, options: $options, + convert: UnifiedResponse::class, ); - - // @phpstan-ignore-next-line; - return Conversion::coerce(UnifiedResponse::class, value: $resp); } /** @@ -96,15 +95,15 @@ public function nsdl( ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], $requestOptions, ); - $resp = $this->client->request( + + // @phpstan-ignore-next-line; + return $this->client->request( method: 'post', path: 'v4/nsdl/parse', body: (object) $parsed, options: $options, + convert: UnifiedResponse::class, ); - - // @phpstan-ignore-next-line; - return Conversion::coerce(UnifiedResponse::class, value: $resp); } /** @@ -125,14 +124,14 @@ public function smartParse( ['password' => $password, 'pdfFile' => $pdfFile, 'pdfURL' => $pdfURL], $requestOptions, ); - $resp = $this->client->request( + + // @phpstan-ignore-next-line; + return $this->client->request( method: 'post', path: 'v4/smart/parse', body: (object) $parsed, options: $options, + convert: UnifiedResponse::class, ); - - // @phpstan-ignore-next-line; - return Conversion::coerce(UnifiedResponse::class, value: $resp); } } diff --git a/src/Core/Util.php b/src/Core/Util.php index 3262615..68721b8 100644 --- a/src/Core/Util.php +++ b/src/Core/Util.php @@ -4,8 +4,8 @@ namespace CasParser\Core; -use Psr\Http\Message\MessageInterface; use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UriInterface; @@ -16,7 +16,9 @@ final class Util public const JSON_ENCODE_FLAGS = JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE; - public const JSON_CONTENT_TYPE = '/application\/json/'; + public const JSON_CONTENT_TYPE = '/^application\/(?:vnd(?:.[^.]+)*+)?json(?!l)/'; + + public const JSONL_CONTENT_TYPE = '/^application\/(:?x-(?:n|l)djson)|(:?(?:x-)?jsonl)/'; /** * @return array @@ -44,6 +46,16 @@ public static function array_transform_keys(array $array, array $map): array return $acc; } + /** + * @param array $arr + * + * @return array + */ + public static function array_filter_omit(array $arr): array + { + return array_filter($arr, fn ($v, $_) => OMIT !== $v, mode: ARRAY_FILTER_USE_BOTH); + } + /** * @param string|int|list|callable $key */ @@ -85,7 +97,7 @@ public static function parsePath(string|array $path): string [$template] = $path; - return sprintf($template, ...array_map('rawurlencode', array_slice($path, 1))); + return sprintf($template, ...array_map('rawurlencode', array: array_slice($path, 1))); } /** @@ -290,18 +302,38 @@ public static function decodeSSE(\Iterator $lines): \Generator } } - public static function decodeContent(MessageInterface $rsp): mixed + public static function decodeJson(string $json): mixed { + return json_decode($json, associative: true, flags: JSON_THROW_ON_ERROR); + } + + public static function decodeContent(ResponseInterface $rsp): mixed + { + if (204 == $rsp->getStatusCode()) { + return null; + } + $content_type = $rsp->getHeaderLine('Content-Type'); $body = $rsp->getBody(); - if (preg_match(self::JSON_CONTENT_TYPE, $content_type)) { + if (preg_match(self::JSON_CONTENT_TYPE, subject: $content_type)) { $json = $body->getContents(); - return json_decode($json, associative: true, flags: JSON_THROW_ON_ERROR); + return self::decodeJson($json); + } + + if (preg_match(self::JSONL_CONTENT_TYPE, subject: $content_type)) { + $it = self::streamIterator($body); + $lines = self::decodeLines($it); + + return (function () use ($lines) { + foreach ($lines as $line) { + yield static::decodeJson($line); + } + })(); } - if (str_contains($content_type, 'text/event-stream')) { + if (str_contains($content_type, needle: 'text/event-stream')) { $it = self::streamIterator($body); $lines = self::decodeLines($it); @@ -311,14 +343,9 @@ public static function decodeContent(MessageInterface $rsp): mixed return self::streamIterator($body); } - /** - * @param array $arr - * - * @return array - */ - public static function array_filter_omit(array $arr): array + public static function prettyEncodeJson(mixed $obj): string { - return array_filter($arr, fn ($v, $_) => OMIT !== $v, ARRAY_FILTER_USE_BOTH); + return json_encode($obj, flags: JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) ?: ''; } /** diff --git a/src/RequestOptions.php b/src/RequestOptions.php index 0609a2a..1c9b4ff 100644 --- a/src/RequestOptions.php +++ b/src/RequestOptions.php @@ -88,7 +88,7 @@ public function __unserialize(array $data): void } /** - * @param array{ + * @param RequestOptions|array{ * timeout?: float|null, * maxRetries?: int|null, * initialRetryDelay?: float|null, @@ -96,9 +96,9 @@ public function __unserialize(array $data): void * extraHeaders?: list|null, * extraQueryParams?: list|null, * extraBodyParams?: list|null, - * }|RequestOptions|null $options + * }|null $options */ - public static function parse(array|RequestOptions|null $options): self + public static function parse(RequestOptions|array|null $options): self { if (is_null($options)) { return new self; From 041e76c9f5db339bd9834e609058b3b4edc865d3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 27 Aug 2025 05:00:08 +0000 Subject: [PATCH 15/29] fix: basic pagination should work --- src/Core.php | 8 +------ src/Core/BaseClient.php | 8 +++---- src/Core/Concerns/SdkModel.php | 3 ++- .../AbstractPage.php => Concerns/SdkPage.php} | 24 +++++++++---------- src/Core/Contracts/BasePage.php | 18 +++++++++++++- src/Core/Conversion/ModelOf.php | 9 ++++--- src/Core/Implementation/Omittable.php | 13 ++++++++++ 7 files changed, 55 insertions(+), 28 deletions(-) rename src/Core/{Pagination/AbstractPage.php => Concerns/SdkPage.php} (84%) create mode 100644 src/Core/Implementation/Omittable.php diff --git a/src/Core.php b/src/Core.php index 55b7777..22b6470 100644 --- a/src/Core.php +++ b/src/Core.php @@ -4,12 +4,6 @@ namespace CasParser\Core; -/** - * @internal - */ -enum Omittable -{ - case OMIT; -} +use CasParser\Core\Implementation\Omittable; const OMIT = Omittable::OMIT; diff --git a/src/Core/BaseClient.php b/src/Core/BaseClient.php index 111545a..3676f37 100644 --- a/src/Core/BaseClient.php +++ b/src/Core/BaseClient.php @@ -79,18 +79,18 @@ public function request( [$req, $opts] = $this->buildRequest(method: $method, path: $path, query: $query, headers: $headers, body: $body, opts: $options); ['method' => $method, 'path' => $uri, 'headers' => $headers] = $req; - $req = $this->requestFactory->createRequest($method, uri: $uri); - $req = Util::withSetHeaders($req, headers: $headers); + $request = $this->requestFactory->createRequest($method, uri: $uri); + $request = Util::withSetHeaders($request, headers: $headers); // @phpstan-ignore-next-line - $rsp = $this->sendRequest($req, data: $body, opts: $opts, redirectCount: 0, retryCount: 0); + $rsp = $this->sendRequest($request, data: $body, opts: $opts, redirectCount: 0, retryCount: 0); $decoded = Util::decodeContent($rsp); if (!is_null($stream)) { return new $stream( convert: $convert, - request: $req, + request: $request, response: $rsp, stream: $decoded ); diff --git a/src/Core/Concerns/SdkModel.php b/src/Core/Concerns/SdkModel.php index 388d394..138a009 100644 --- a/src/Core/Concerns/SdkModel.php +++ b/src/Core/Concerns/SdkModel.php @@ -5,6 +5,7 @@ namespace CasParser\Core\Concerns; use CasParser\Core\Contracts\BaseModel; +use CasParser\Core\Contracts\BasePage; use CasParser\Core\Conversion; use CasParser\Core\Conversion\CoerceState; use CasParser\Core\Conversion\Contracts\Converter; @@ -244,7 +245,7 @@ private function unsetOptionalProperties(): void */ private static function serialize(mixed $value): mixed { - if ($value instanceof BaseModel) { + if ($value instanceof BaseModel || $value instanceof BasePage) { return $value->toArray(); } diff --git a/src/Core/Pagination/AbstractPage.php b/src/Core/Concerns/SdkPage.php similarity index 84% rename from src/Core/Pagination/AbstractPage.php rename to src/Core/Concerns/SdkPage.php index 9222737..3302816 100644 --- a/src/Core/Pagination/AbstractPage.php +++ b/src/Core/Concerns/SdkPage.php @@ -2,10 +2,9 @@ declare(strict_types=1); -namespace CasParser\Core\Pagination; +namespace CasParser\Core\Concerns; use CasParser\Client; -use CasParser\Core\Contracts\BasePage; use CasParser\Core\Conversion\Contracts\Converter; use CasParser\Core\Conversion\Contracts\ConverterSource; use CasParser\Core\Errors\APIStatusError; @@ -16,19 +15,20 @@ * * @template Item * - * @implements BasePage - * * @phpstan-import-type normalized_request from \CasParser\Core\BaseClient */ -abstract class AbstractPage implements BasePage +trait SdkPage { - public function __construct( - protected Converter|ConverterSource|string $convert, - protected Client $client, - protected array $request, - protected RequestOptions $options, - protected mixed $data, - ) {} + private Converter|ConverterSource|string $convert; + + private Client $client; + + /** + * normalized_request $request. + */ + private array $request; + + private RequestOptions $options; /** * @return list diff --git a/src/Core/Contracts/BasePage.php b/src/Core/Contracts/BasePage.php index 372cd75..61a72b7 100644 --- a/src/Core/Contracts/BasePage.php +++ b/src/Core/Contracts/BasePage.php @@ -10,11 +10,14 @@ use CasParser\RequestOptions; /** + * @internal + * * @template Item * + * @extends \ArrayAccess * @extends \IteratorAggregate */ -interface BasePage extends \IteratorAggregate +interface BasePage extends \ArrayAccess, \JsonSerializable, \Stringable, \IteratorAggregate { /** * @internal @@ -29,6 +32,14 @@ public function __construct( mixed $data, ); + /** + * @return static + */ + public static function fromArray(mixed $data): self; + + /** @return array */ + public function toArray(): array; + public function hasNextPage(): bool; /** @@ -40,4 +51,9 @@ public function getPaginatedItems(): array; * @return static */ public function getNextPage(): static; + + /** + * @return \Generator + */ + public function pagingEachItem(): \Generator; } diff --git a/src/Core/Conversion/ModelOf.php b/src/Core/Conversion/ModelOf.php index fbaeb42..f0103ca 100644 --- a/src/Core/Conversion/ModelOf.php +++ b/src/Core/Conversion/ModelOf.php @@ -6,6 +6,7 @@ use CasParser\Core\Attributes\Api; use CasParser\Core\Contracts\BaseModel; +use CasParser\Core\Contracts\BasePage; use CasParser\Core\Conversion; use CasParser\Core\Conversion\Contracts\Converter; @@ -20,7 +21,7 @@ final class ModelOf implements Converter public readonly array $properties; /** - * @param \ReflectionClass $class + * @param \ReflectionClass> $class */ public function __construct(public readonly \ReflectionClass $class) { @@ -93,8 +94,10 @@ public function coerce(mixed $value, CoerceState $state): mixed /** * @param array $data + * + * @return BaseModel|BasePage */ - public function from(array $data): BaseModel + public function from(array $data): BaseModel|BasePage { $instance = $this->class->newInstanceWithoutConstructor(); $instance->__unserialize($data); // @phpstan-ignore-line @@ -104,7 +107,7 @@ public function from(array $data): BaseModel public function dump(mixed $value, DumpState $state): mixed { - if ($value instanceof BaseModel) { + if ($value instanceof BaseModel || $value instanceof BasePage) { $value = $value->toArray(); } diff --git a/src/Core/Implementation/Omittable.php b/src/Core/Implementation/Omittable.php new file mode 100644 index 0000000..3dac9c1 --- /dev/null +++ b/src/Core/Implementation/Omittable.php @@ -0,0 +1,13 @@ + Date: Wed, 27 Aug 2025 05:02:04 +0000 Subject: [PATCH 16/29] fix: minor bugs --- src/Core/Errors/APIStatusError.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Core/Errors/APIStatusError.php b/src/Core/Errors/APIStatusError.php index 768d2c2..ba23a66 100644 --- a/src/Core/Errors/APIStatusError.php +++ b/src/Core/Errors/APIStatusError.php @@ -22,9 +22,7 @@ public function __construct( $this->response = $response; $this->status = $response->getStatusCode(); - $summary = 'Status: '.$this->status.PHP_EOL - .'Response Body: '.Util::prettyEncodeJson(Util::decodeJson($response->getBody())).PHP_EOL - .'Request Body: '.Util::prettyEncodeJson(Util::decodeJson($request->getBody())).PHP_EOL; + $summary = Util::prettyEncodeJson(['status' => $this->status, 'body' => Util::decodeJson($response->getBody())]); if ('' != $message) { $summary .= $message.PHP_EOL.$summary; From 961e54000c4f7e50b2c2bdbed82ac08b4862450f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 27 Aug 2025 05:11:16 +0000 Subject: [PATCH 17/29] feat!: pagination field rename, and basic streaming docs --- README.md | 2 ++ src/Core/Concerns/SdkPage.php | 8 ++++---- src/Core/Contracts/BasePage.php | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 990e035..85cec47 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ use CasParser\Client; $client = new Client(apiKey: getenv("CAS_PARSER_API_KEY") ?: "My API Key"); $unifiedResponse = $client->casParser->smartParse(); + var_dump($unifiedResponse->demat_accounts); ``` @@ -116,6 +117,7 @@ use CasParser\RequestOptions; $client = new Client(maxRetries: 0); // Or, configure per-request: + $result = $client->casParser->smartParse(new RequestOptions(maxRetries: 5)); ``` diff --git a/src/Core/Concerns/SdkPage.php b/src/Core/Concerns/SdkPage.php index 3302816..3233e06 100644 --- a/src/Core/Concerns/SdkPage.php +++ b/src/Core/Concerns/SdkPage.php @@ -33,11 +33,11 @@ trait SdkPage /** * @return list */ - abstract public function getPaginatedItems(): array; + abstract public function getItems(): array; public function hasNextPage(): bool { - $items = $this->getPaginatedItems(); + $items = $this->getItems(); if (empty($items)) { return false; } @@ -66,7 +66,7 @@ public function getNextPage(): static [$req, $opts] = $next; // @phpstan-ignore-next-line - return $this->client->request(...$req, convert: $this->convert, page: $this, options: $opts); + return $this->client->request(...$req, convert: $this->convert, page: $this::class, options: $opts); } /** @@ -94,7 +94,7 @@ public function getIterator(): \Generator public function pagingEachItem(): \Generator { foreach ($this as $page) { - foreach ($page->getPaginatedItems() as $item) { + foreach ($page->getItems() as $item) { yield $item; } } diff --git a/src/Core/Contracts/BasePage.php b/src/Core/Contracts/BasePage.php index 61a72b7..f0a26c0 100644 --- a/src/Core/Contracts/BasePage.php +++ b/src/Core/Contracts/BasePage.php @@ -45,7 +45,7 @@ public function hasNextPage(): bool; /** * @return list */ - public function getPaginatedItems(): array; + public function getItems(): array; /** * @return static From 16e7c9593a3216a1af73bd62b8c3d9561d2a05ad Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 27 Aug 2025 05:21:07 +0000 Subject: [PATCH 18/29] feat: ensure `->toArray()` benefits from structural typing --- .../CasGeneratorGenerateCasParams.php | 10 ++++++++++ .../CasGeneratorGenerateCasResponse.php | 6 ++++++ src/CasParser/CasParserCamsKfintechParams.php | 5 +++++ src/CasParser/CasParserCdslParams.php | 5 +++++ src/CasParser/CasParserNsdlParams.php | 5 +++++ src/CasParser/CasParserSmartParseParams.php | 5 +++++ src/CasParser/UnifiedResponse.php | 11 +++++++++++ src/CasParser/UnifiedResponse/DematAccount.php | 13 +++++++++++++ .../DematAccount/AdditionalInfo.php | 12 ++++++++++++ .../UnifiedResponse/DematAccount/Holdings.php | 10 ++++++++++ .../DematAccount/Holdings/Aif.php | 10 ++++++++++ .../DematAccount/Holdings/CorporateBond.php | 10 ++++++++++ .../DematAccount/Holdings/DematMutualFund.php | 10 ++++++++++ .../DematAccount/Holdings/Equity.php | 10 ++++++++++ .../Holdings/GovernmentSecurity.php | 10 ++++++++++ src/CasParser/UnifiedResponse/Insurance.php | 6 ++++++ .../Insurance/LifeInsurancePolicy.php | 14 ++++++++++++++ src/CasParser/UnifiedResponse/Investor.php | 12 ++++++++++++ src/CasParser/UnifiedResponse/Meta.php | 8 ++++++++ .../UnifiedResponse/Meta/StatementPeriod.php | 6 ++++++ src/CasParser/UnifiedResponse/MutualFund.php | 11 +++++++++++ .../MutualFund/AdditionalInfo.php | 5 +++++ .../UnifiedResponse/MutualFund/Scheme.php | 16 ++++++++++++++++ .../MutualFund/Scheme/AdditionalInfo.php | 9 +++++++++ .../UnifiedResponse/MutualFund/Scheme/Gain.php | 4 ++++ .../MutualFund/Scheme/Transaction.php | 13 +++++++++++++ src/CasParser/UnifiedResponse/Summary.php | 6 ++++++ .../UnifiedResponse/Summary/Accounts.php | 6 ++++++ .../UnifiedResponse/Summary/Accounts/Demat.php | 4 ++++ .../Summary/Accounts/Insurance.php | 4 ++++ .../Summary/Accounts/MutualFunds.php | 4 ++++ src/Core/Concerns/SdkModel.php | 18 ++++++++++++------ src/Core/Concerns/SdkPage.php | 2 +- src/Core/Contracts/BasePage.php | 4 +++- tests/Core/TestModel.php | 1 + 35 files changed, 277 insertions(+), 8 deletions(-) diff --git a/src/CasGenerator/CasGeneratorGenerateCasParams.php b/src/CasGenerator/CasGeneratorGenerateCasParams.php index 7d4512f..10d952f 100644 --- a/src/CasGenerator/CasGeneratorGenerateCasParams.php +++ b/src/CasGenerator/CasGeneratorGenerateCasParams.php @@ -13,9 +13,19 @@ /** * This endpoint generates CAS (Consolidated Account Statement) documents by submitting a mailback request to the specified CAS authority. * Currently only supports KFintech, with plans to support CAMS, CDSL, and NSDL in the future. + * + * @phpstan-type cas_generator_generate_cas_params = array{ + * email: string, + * fromDate: string, + * password: string, + * toDate: string, + * casAuthority?: CasAuthority::*, + * panNo?: string, + * } */ final class CasGeneratorGenerateCasParams implements BaseModel { + /** @use SdkModel */ use SdkModel; use SdkParams; diff --git a/src/CasGenerator/CasGeneratorGenerateCasResponse.php b/src/CasGenerator/CasGeneratorGenerateCasResponse.php index 36aea2a..1d211cf 100644 --- a/src/CasGenerator/CasGeneratorGenerateCasResponse.php +++ b/src/CasGenerator/CasGeneratorGenerateCasResponse.php @@ -8,8 +8,14 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type cas_generator_generate_cas_response = array{ + * msg?: string|null, status?: string|null + * } + */ final class CasGeneratorGenerateCasResponse implements BaseModel { + /** @use SdkModel */ use SdkModel; #[Api(optional: true)] diff --git a/src/CasParser/CasParserCamsKfintechParams.php b/src/CasParser/CasParserCamsKfintechParams.php index 6da7d26..b90c8b4 100644 --- a/src/CasParser/CasParserCamsKfintechParams.php +++ b/src/CasParser/CasParserCamsKfintechParams.php @@ -12,9 +12,14 @@ /** * This endpoint specifically parses CAMS/KFintech CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from CAMS or KFintech. + * + * @phpstan-type cas_parser_cams_kfintech_params = array{ + * password?: string, pdfFile?: string, pdfURL?: string + * } */ final class CasParserCamsKfintechParams implements BaseModel { + /** @use SdkModel */ use SdkModel; use SdkParams; diff --git a/src/CasParser/CasParserCdslParams.php b/src/CasParser/CasParserCdslParams.php index f802a3b..eb3d930 100644 --- a/src/CasParser/CasParserCdslParams.php +++ b/src/CasParser/CasParserCdslParams.php @@ -12,9 +12,14 @@ /** * This endpoint specifically parses CDSL CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from CDSL. + * + * @phpstan-type cas_parser_cdsl_params = array{ + * password?: string, pdfFile?: string, pdfURL?: string + * } */ final class CasParserCdslParams implements BaseModel { + /** @use SdkModel */ use SdkModel; use SdkParams; diff --git a/src/CasParser/CasParserNsdlParams.php b/src/CasParser/CasParserNsdlParams.php index 3123079..0aca4b1 100644 --- a/src/CasParser/CasParserNsdlParams.php +++ b/src/CasParser/CasParserNsdlParams.php @@ -12,9 +12,14 @@ /** * This endpoint specifically parses NSDL CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from NSDL. + * + * @phpstan-type cas_parser_nsdl_params = array{ + * password?: string, pdfFile?: string, pdfURL?: string + * } */ final class CasParserNsdlParams implements BaseModel { + /** @use SdkModel */ use SdkModel; use SdkParams; diff --git a/src/CasParser/CasParserSmartParseParams.php b/src/CasParser/CasParserSmartParseParams.php index 998aea8..1a0ba15 100644 --- a/src/CasParser/CasParserSmartParseParams.php +++ b/src/CasParser/CasParserSmartParseParams.php @@ -12,9 +12,14 @@ /** * This endpoint parses CAS (Consolidated Account Statement) PDF files from NSDL, CDSL, or CAMS/KFintech and returns data in a unified format. * It auto-detects the CAS type and transforms the data into a consistent structure regardless of the source. + * + * @phpstan-type cas_parser_smart_parse_params = array{ + * password?: string, pdfFile?: string, pdfURL?: string + * } */ final class CasParserSmartParseParams implements BaseModel { + /** @use SdkModel */ use SdkModel; use SdkParams; diff --git a/src/CasParser/UnifiedResponse.php b/src/CasParser/UnifiedResponse.php index 43d2b13..87eba69 100644 --- a/src/CasParser/UnifiedResponse.php +++ b/src/CasParser/UnifiedResponse.php @@ -14,8 +14,19 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type unified_response = array{ + * dematAccounts?: list|null, + * insurance?: Insurance|null, + * investor?: Investor|null, + * meta?: Meta|null, + * mutualFunds?: list|null, + * summary?: Summary|null, + * } + */ final class UnifiedResponse implements BaseModel { + /** @use SdkModel */ use SdkModel; /** @var list|null $dematAccounts */ diff --git a/src/CasParser/UnifiedResponse/DematAccount.php b/src/CasParser/UnifiedResponse/DematAccount.php index e23e721..e63c58e 100644 --- a/src/CasParser/UnifiedResponse/DematAccount.php +++ b/src/CasParser/UnifiedResponse/DematAccount.php @@ -11,8 +11,21 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type demat_account = array{ + * additionalInfo?: AdditionalInfo|null, + * boID?: string|null, + * clientID?: string|null, + * dematType?: DematType::*|null, + * dpID?: string|null, + * dpName?: string|null, + * holdings?: Holdings|null, + * value?: float|null, + * } + */ final class DematAccount implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php index 647b305..abfc204 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php @@ -10,9 +10,21 @@ /** * Additional information specific to the demat account type. + * + * @phpstan-type additional_info = array{ + * boStatus?: string|null, + * boSubStatus?: string|null, + * boType?: string|null, + * bsda?: string|null, + * email?: string|null, + * linkedPans?: list|null, + * nominee?: string|null, + * status?: string|null, + * } */ final class AdditionalInfo implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php index 998f00d..6742ece 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php @@ -13,8 +13,18 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type holdings_alias = array{ + * aifs?: list|null, + * corporateBonds?: list|null, + * dematMutualFunds?: list|null, + * equities?: list|null, + * governmentSecurities?: list|null, + * } + */ final class Holdings implements BaseModel { + /** @use SdkModel */ use SdkModel; /** @var list|null $aifs */ diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php index 13d9c18..6055554 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php @@ -8,8 +8,18 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type aif_alias = array{ + * additionalInfo?: mixed, + * isin?: string|null, + * name?: string|null, + * units?: float|null, + * value?: float|null, + * } + */ final class Aif implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php index 50956db..c7d479e 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php @@ -8,8 +8,18 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type corporate_bond = array{ + * additionalInfo?: mixed, + * isin?: string|null, + * name?: string|null, + * units?: float|null, + * value?: float|null, + * } + */ final class CorporateBond implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php index 17d9af3..b49eda3 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php @@ -8,8 +8,18 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type demat_mutual_fund = array{ + * additionalInfo?: mixed, + * isin?: string|null, + * name?: string|null, + * units?: float|null, + * value?: float|null, + * } + */ final class DematMutualFund implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php index 9a1251f..4220716 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php @@ -8,8 +8,18 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type equity_alias = array{ + * additionalInfo?: mixed, + * isin?: string|null, + * name?: string|null, + * units?: float|null, + * value?: float|null, + * } + */ final class Equity implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php index e9d3148..731f7d6 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php @@ -8,8 +8,18 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type government_security = array{ + * additionalInfo?: mixed, + * isin?: string|null, + * name?: string|null, + * units?: float|null, + * value?: float|null, + * } + */ final class GovernmentSecurity implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/Insurance.php b/src/CasParser/UnifiedResponse/Insurance.php index e290895..63d5579 100644 --- a/src/CasParser/UnifiedResponse/Insurance.php +++ b/src/CasParser/UnifiedResponse/Insurance.php @@ -9,8 +9,14 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type insurance_alias = array{ + * lifeInsurancePolicies?: list|null + * } + */ final class Insurance implements BaseModel { + /** @use SdkModel */ use SdkModel; /** @var list|null $lifeInsurancePolicies */ diff --git a/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php b/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php index a154862..710655c 100644 --- a/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php +++ b/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php @@ -8,8 +8,22 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type life_insurance_policy = array{ + * additionalInfo?: mixed, + * lifeAssured?: string|null, + * policyName?: string|null, + * policyNumber?: string|null, + * premiumAmount?: float|null, + * premiumFrequency?: string|null, + * provider?: string|null, + * status?: string|null, + * sumAssured?: float|null, + * } + */ final class LifeInsurancePolicy implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/Investor.php b/src/CasParser/UnifiedResponse/Investor.php index 4541bdb..178e313 100644 --- a/src/CasParser/UnifiedResponse/Investor.php +++ b/src/CasParser/UnifiedResponse/Investor.php @@ -8,8 +8,20 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type investor_alias = array{ + * address?: string|null, + * casID?: string|null, + * email?: string|null, + * mobile?: string|null, + * name?: string|null, + * pan?: string|null, + * pincode?: string|null, + * } + */ final class Investor implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/Meta.php b/src/CasParser/UnifiedResponse/Meta.php index 4fd4dcb..fc1cc9e 100644 --- a/src/CasParser/UnifiedResponse/Meta.php +++ b/src/CasParser/UnifiedResponse/Meta.php @@ -10,8 +10,16 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type meta_alias = array{ + * casType?: CasType::*|null, + * generatedAt?: \DateTimeInterface|null, + * statementPeriod?: StatementPeriod|null, + * } + */ final class Meta implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php b/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php index 4eedc9c..039a1ae 100644 --- a/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php +++ b/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php @@ -8,8 +8,14 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type statement_period = array{ + * from?: \DateTimeInterface|null, to?: \DateTimeInterface|null + * } + */ final class StatementPeriod implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/MutualFund.php b/src/CasParser/UnifiedResponse/MutualFund.php index 57b4a87..9fbfe52 100644 --- a/src/CasParser/UnifiedResponse/MutualFund.php +++ b/src/CasParser/UnifiedResponse/MutualFund.php @@ -10,8 +10,19 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type mutual_fund = array{ + * additionalInfo?: AdditionalInfo|null, + * amc?: string|null, + * folioNumber?: string|null, + * registrar?: string|null, + * schemes?: list|null, + * value?: float|null, + * } + */ final class MutualFund implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php b/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php index 901e899..ee35a57 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php @@ -10,9 +10,14 @@ /** * Additional folio information. + * + * @phpstan-type additional_info = array{ + * kyc?: string|null, pan?: string|null, pankyc?: string|null + * } */ final class AdditionalInfo implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php index 2b36434..ea61498 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php @@ -12,8 +12,24 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type scheme_alias = array{ + * additionalInfo?: AdditionalInfo|null, + * cost?: float|null, + * gain?: Gain|null, + * isin?: string|null, + * name?: string|null, + * nav?: float|null, + * nominees?: list|null, + * transactions?: list|null, + * type?: Type::*|null, + * units?: float|null, + * value?: float|null, + * } + */ final class Scheme implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php index 4326b2e..622d843 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php @@ -10,9 +10,18 @@ /** * Additional information specific to the scheme. + * + * @phpstan-type additional_info = array{ + * advisor?: string|null, + * amfi?: string|null, + * closeUnits?: float|null, + * openUnits?: float|null, + * rtaCode?: string|null, + * } */ final class AdditionalInfo implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php index a535725..17404b4 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php @@ -8,8 +8,12 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type gain_alias = array{absolute?: float|null, percentage?: float|null} + */ final class Gain implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php index a8ecf41..d0f88b7 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php @@ -8,8 +8,21 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type transaction_alias = array{ + * amount?: float|null, + * balance?: float|null, + * date?: \DateTimeInterface|null, + * description?: string|null, + * dividendRate?: float|null, + * nav?: float|null, + * type?: string|null, + * units?: float|null, + * } + */ final class Transaction implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/Summary.php b/src/CasParser/UnifiedResponse/Summary.php index 588f740..e807258 100644 --- a/src/CasParser/UnifiedResponse/Summary.php +++ b/src/CasParser/UnifiedResponse/Summary.php @@ -9,8 +9,14 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type summary_alias = array{ + * accounts?: Accounts|null, totalValue?: float|null + * } + */ final class Summary implements BaseModel { + /** @use SdkModel */ use SdkModel; #[Api(optional: true)] diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts.php b/src/CasParser/UnifiedResponse/Summary/Accounts.php index ccaf08c..3d1823f 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts.php @@ -11,8 +11,14 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type accounts_alias = array{ + * demat?: Demat|null, insurance?: Insurance|null, mutualFunds?: MutualFunds|null + * } + */ final class Accounts implements BaseModel { + /** @use SdkModel */ use SdkModel; #[Api(optional: true)] diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php b/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php index 0502ecb..9dcd487 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php @@ -8,8 +8,12 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type demat_alias = array{count?: int|null, totalValue?: float|null} + */ final class Demat implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php b/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php index fe063c9..6af8f26 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php @@ -8,8 +8,12 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type insurance_alias = array{count?: int|null, totalValue?: float|null} + */ final class Insurance implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php b/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php index 42a3af2..0ff7095 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php @@ -8,8 +8,12 @@ use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; +/** + * @phpstan-type mutual_funds = array{count?: int|null, totalValue?: float|null} + */ final class MutualFunds implements BaseModel { + /** @use SdkModel */ use SdkModel; /** diff --git a/src/Core/Concerns/SdkModel.php b/src/Core/Concerns/SdkModel.php index 138a009..86db005 100644 --- a/src/Core/Concerns/SdkModel.php +++ b/src/Core/Concerns/SdkModel.php @@ -14,6 +14,8 @@ /** * @internal + * + * @template-covariant Data of array */ trait SdkModel { @@ -65,13 +67,13 @@ public function __toString(): string } /** + * @internal + * * Magic get is intended to occur when we have manually unset * a native class property, indicating an omitted value, - * or a property overridden with an incongruent type. + * or a property overridden with an incongruent type * * @throws \Exception - * - * @internal */ public function __get(string $key): mixed { @@ -92,10 +94,12 @@ public function __get(string $key): mixed return null; } - /** @return array */ + /** + * @return Data + */ public function toArray(): array { - return $this->__serialize(); + return $this->__serialize(); // @phpstan-ignore-line } /** @@ -200,8 +204,10 @@ public function jsonSerialize(): array /** * @internal + * + * @param array $data */ - public static function fromArray(mixed $data): self + public static function fromArray(array $data): self { return self::converter()->from($data); // @phpstan-ignore-line } diff --git a/src/Core/Concerns/SdkPage.php b/src/Core/Concerns/SdkPage.php index 3233e06..e9edd14 100644 --- a/src/Core/Concerns/SdkPage.php +++ b/src/Core/Concerns/SdkPage.php @@ -50,7 +50,7 @@ public function hasNextPage(): bool * Before calling this method, you must check if there is a next page * using {@link hasNextPage()}. * - * @return static of AbstractPage + * @return static of static * * @throws APIStatusError */ diff --git a/src/Core/Contracts/BasePage.php b/src/Core/Contracts/BasePage.php index f0a26c0..b0719ee 100644 --- a/src/Core/Contracts/BasePage.php +++ b/src/Core/Contracts/BasePage.php @@ -33,9 +33,11 @@ public function __construct( ); /** + * @param array $data + * * @return static */ - public static function fromArray(mixed $data): self; + public static function fromArray(array $data): self; /** @return array */ public function toArray(): array; diff --git a/tests/Core/TestModel.php b/tests/Core/TestModel.php index dea7648..7c56501 100644 --- a/tests/Core/TestModel.php +++ b/tests/Core/TestModel.php @@ -11,6 +11,7 @@ class TestModel implements BaseModel { + /** @use SdkModel> */ use SdkModel; #[Api] From 92d9817bf28718bfe5ecc6e61f6ed42abe4b0d4b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 27 Aug 2025 05:23:12 +0000 Subject: [PATCH 19/29] feat!: rename errors to exceptions --- README.md | 32 +++++++++---------- src/Core/BaseClient.php | 4 +-- src/Core/Concerns/SdkPage.php | 4 +-- src/Core/Errors/AuthenticationError.php | 9 ------ src/Core/Errors/BadRequestError.php | 9 ------ src/Core/Errors/ConflictError.php | 9 ------ src/Core/Errors/InternalServerError.php | 9 ------ src/Core/Errors/NotFoundError.php | 9 ------ src/Core/Errors/PermissionDeniedError.php | 9 ------ src/Core/Errors/RateLimitError.php | 9 ------ src/Core/Errors/UnprocessableEntityError.php | 9 ------ .../APIConnectionException.php} | 4 +-- .../APIException.php} | 4 +-- .../APIStatusException.php} | 22 ++++++------- .../APITimeoutException.php} | 6 ++-- .../Exceptions/AuthenticationException.php | 9 ++++++ src/Core/Exceptions/BadRequestException.php | 9 ++++++ .../CasParserException.php} | 4 +-- src/Core/Exceptions/ConflictException.php | 9 ++++++ .../Exceptions/InternalServerException.php | 9 ++++++ src/Core/Exceptions/NotFoundException.php | 9 ++++++ .../Exceptions/PermissionDeniedException.php | 9 ++++++ src/Core/Exceptions/RateLimitException.php | 9 ++++++ .../UnprocessableEntityException.php | 9 ++++++ 24 files changed, 112 insertions(+), 112 deletions(-) delete mode 100644 src/Core/Errors/AuthenticationError.php delete mode 100644 src/Core/Errors/BadRequestError.php delete mode 100644 src/Core/Errors/ConflictError.php delete mode 100644 src/Core/Errors/InternalServerError.php delete mode 100644 src/Core/Errors/NotFoundError.php delete mode 100644 src/Core/Errors/PermissionDeniedError.php delete mode 100644 src/Core/Errors/RateLimitError.php delete mode 100644 src/Core/Errors/UnprocessableEntityError.php rename src/Core/{Errors/APIConnectionError.php => Exceptions/APIConnectionException.php} (52%) rename src/Core/{Errors/APIError.php => Exceptions/APIException.php} (83%) rename src/Core/{Errors/APIStatusError.php => Exceptions/APIStatusException.php} (64%) rename src/Core/{Errors/APITimeoutError.php => Exceptions/APITimeoutException.php} (68%) create mode 100644 src/Core/Exceptions/AuthenticationException.php create mode 100644 src/Core/Exceptions/BadRequestException.php rename src/Core/{Errors/CasParserError.php => Exceptions/CasParserException.php} (76%) create mode 100644 src/Core/Exceptions/ConflictException.php create mode 100644 src/Core/Exceptions/InternalServerException.php create mode 100644 src/Core/Exceptions/NotFoundException.php create mode 100644 src/Core/Exceptions/PermissionDeniedException.php create mode 100644 src/Core/Exceptions/RateLimitException.php create mode 100644 src/Core/Exceptions/UnprocessableEntityException.php diff --git a/README.md b/README.md index 85cec47..3da87eb 100644 --- a/README.md +++ b/README.md @@ -63,16 +63,16 @@ However, builders are also provided `(new Dog)->withName("Joey")`. ### Handling errors -When the library is unable to connect to the API, or if the API returns a non-success status code (i.e., 4xx or 5xx response), a subclass of `CasParser\Errors\APIError` will be thrown: +When the library is unable to connect to the API, or if the API returns a non-success status code (i.e., 4xx or 5xx response), a subclass of `CasParser\Core\Exceptions\APIException` will be thrown: ```php casParser->smartParse(); -} catch (APIConnectionError $e) { +} catch (APIConnectionException $e) { echo "The server could not be reached", PHP_EOL; var_dump($e->getPrevious()); } catch (RateLimitError $_) { @@ -85,19 +85,19 @@ try { Error codes are as follows: -| Cause | Error Type | -| ---------------- | -------------------------- | -| HTTP 400 | `BadRequestError` | -| HTTP 401 | `AuthenticationError` | -| HTTP 403 | `PermissionDeniedError` | -| HTTP 404 | `NotFoundError` | -| HTTP 409 | `ConflictError` | -| HTTP 422 | `UnprocessableEntityError` | -| HTTP 429 | `RateLimitError` | -| HTTP >= 500 | `InternalServerError` | -| Other HTTP error | `APIStatusError` | -| Timeout | `APITimeoutError` | -| Network error | `APIConnectionError` | +| Cause | Error Type | +| ---------------- | ------------------------------ | +| HTTP 400 | `BadRequestException` | +| HTTP 401 | `AuthenticationException` | +| HTTP 403 | `PermissionDeniedException` | +| HTTP 404 | `NotFoundException` | +| HTTP 409 | `ConflictException` | +| HTTP 422 | `UnprocessableEntityException` | +| HTTP 429 | `RateLimitException` | +| HTTP >= 500 | `InternalServerException` | +| Other HTTP error | `APIStatusException` | +| Timeout | `APITimeoutException` | +| Network error | `APIConnectionException` | ### Retries diff --git a/src/Core/BaseClient.php b/src/Core/BaseClient.php index 3676f37..d0be9fb 100644 --- a/src/Core/BaseClient.php +++ b/src/Core/BaseClient.php @@ -8,7 +8,7 @@ use CasParser\Core\Contracts\BaseStream; use CasParser\Core\Conversion\Contracts\Converter; use CasParser\Core\Conversion\Contracts\ConverterSource; -use CasParser\Core\Errors\APIStatusError; +use CasParser\Core\Exceptions\APIStatusException; use CasParser\RequestOptions; use Http\Discovery\Psr17FactoryDiscovery; use Http\Discovery\Psr18ClientDiscovery; @@ -203,7 +203,7 @@ protected function sendRequest( } if ($code >= 400 && $code < 500) { - throw APIStatusError::from(request: $req, response: $rsp); + throw APIStatusException::from(request: $req, response: $rsp); } if ($code >= 500 && $retryCount < $opts->maxRetries) { diff --git a/src/Core/Concerns/SdkPage.php b/src/Core/Concerns/SdkPage.php index e9edd14..6576465 100644 --- a/src/Core/Concerns/SdkPage.php +++ b/src/Core/Concerns/SdkPage.php @@ -7,7 +7,7 @@ use CasParser\Client; use CasParser\Core\Conversion\Contracts\Converter; use CasParser\Core\Conversion\Contracts\ConverterSource; -use CasParser\Core\Errors\APIStatusError; +use CasParser\Core\Exceptions\APIStatusException; use CasParser\RequestOptions; /** @@ -52,7 +52,7 @@ public function hasNextPage(): bool * * @return static of static * - * @throws APIStatusError + * @throws APIStatusException */ public function getNextPage(): static { diff --git a/src/Core/Errors/AuthenticationError.php b/src/Core/Errors/AuthenticationError.php deleted file mode 100644 index 63d2fd3..0000000 --- a/src/Core/Errors/AuthenticationError.php +++ /dev/null @@ -1,9 +0,0 @@ -getStatusCode(); $cls = match (true) { - 400 === $status => BadRequestError::class, - 401 === $status => AuthenticationError::class, - 403 === $status => PermissionDeniedError::class, - 404 === $status => NotFoundError::class, - 409 === $status => ConflictError::class, - 422 === $status => UnprocessableEntityError::class, - 429 === $status => RateLimitError::class, - $status >= 500 => InternalServerError::class, - default => APIStatusError::class + 400 === $status => BadRequestException::class, + 401 === $status => AuthenticationException::class, + 403 === $status => PermissionDeniedException::class, + 404 === $status => NotFoundException::class, + 409 === $status => ConflictException::class, + 422 === $status => UnprocessableEntityException::class, + 429 === $status => RateLimitException::class, + $status >= 500 => InternalServerException::class, + default => APIStatusException::class }; return new $cls(request: $request, response: $response, message: $message); diff --git a/src/Core/Errors/APITimeoutError.php b/src/Core/Exceptions/APITimeoutException.php similarity index 68% rename from src/Core/Errors/APITimeoutError.php rename to src/Core/Exceptions/APITimeoutException.php index e3427bd..5e9e4d3 100644 --- a/src/Core/Errors/APITimeoutError.php +++ b/src/Core/Exceptions/APITimeoutException.php @@ -1,13 +1,13 @@ Date: Wed, 27 Aug 2025 05:25:11 +0000 Subject: [PATCH 20/29] fix: add create release workflow --- .github/workflows/release-doctor.yml | 19 +++++++++++++++++++ bin/check-release-environment | 25 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 .github/workflows/release-doctor.yml create mode 100644 bin/check-release-environment diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml new file mode 100644 index 0000000..8dec9f0 --- /dev/null +++ b/.github/workflows/release-doctor.yml @@ -0,0 +1,19 @@ +name: Release Doctor +on: + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + release_doctor: + name: release doctor + runs-on: ubuntu-latest + if: github.repository == 'CASParser/cas-parser-php' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') + + steps: + - uses: actions/checkout@v4 + + - name: Check release environment + run: | + bash ./bin/check-release-environment diff --git a/bin/check-release-environment b/bin/check-release-environment new file mode 100644 index 0000000..cf571b6 --- /dev/null +++ b/bin/check-release-environment @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +errors=() + +if [ -z "${PACKAGIST_USERNAME}" ]; then + errors+=("The PACKAGIST_USERNAME secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +if [ -z "${PACKAGIST_SAFE_KEY}" ]; then + errors+=("The PACKAGIST_SAFE_KEY secret has not been set. Please set it in either this repository's secrets or your organization secrets") +fi + +lenErrors=${#errors[@]} + +if [[ lenErrors -gt 0 ]]; then + echo -e "Found the following errors in the release environment:\n" + + for error in "${errors[@]}"; do + echo -e "- $error\n" + done + + exit 1 +fi + +echo "The environment is ready to push releases!" From 607f49a7bfb66fc667c15ced3baaa556e1b689ab Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 03:22:50 +0000 Subject: [PATCH 21/29] chore: remove `php-http/multipart-stream-builder` as a required dependency --- composer.json | 8 ++------ src/Core/Concerns/SdkModel.php | 2 -- src/Core/Contracts/BaseStream.php | 2 ++ src/Core/Conversion/UnionOf.php | 3 +++ 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index d9e0bf6..36da290 100644 --- a/composer.json +++ b/composer.json @@ -1,10 +1,7 @@ { "$schema": "https://getcomposer.org/schema.json", "autoload": { - "files": [ - "src/Core.php", - "src/Client.php" - ], + "files": ["src/Core.php", "src/Client.php"], "psr-4": { "CasParser\\": "src/" } @@ -26,13 +23,12 @@ "preferred-install": "dist", "sort-packages": true }, - "license": "APACHE-2.0", "description": "Cas Parser PHP SDK", + "license": "APACHE-2.0", "name": "org-placeholder/cas-parser", "require": { "php": "^8.1", "php-http/discovery": "^1", - "php-http/multipart-stream-builder": "^1", "psr/http-client": "^1", "psr/http-client-implementation": "^1", "psr/http-factory-implementation": "^1", diff --git a/src/Core/Concerns/SdkModel.php b/src/Core/Concerns/SdkModel.php index 86db005..2cf2154 100644 --- a/src/Core/Concerns/SdkModel.php +++ b/src/Core/Concerns/SdkModel.php @@ -203,8 +203,6 @@ public function jsonSerialize(): array } /** - * @internal - * * @param array $data */ public static function fromArray(array $data): self diff --git a/src/Core/Contracts/BaseStream.php b/src/Core/Contracts/BaseStream.php index b37ce42..26a9dd7 100644 --- a/src/Core/Contracts/BaseStream.php +++ b/src/Core/Contracts/BaseStream.php @@ -10,6 +10,8 @@ use Psr\Http\Message\ResponseInterface; /** + * @internal + * * @template TInner * * @extends \IteratorAggregate diff --git a/src/Core/Conversion/UnionOf.php b/src/Core/Conversion/UnionOf.php index 7937f98..4a059ab 100644 --- a/src/Core/Conversion/UnionOf.php +++ b/src/Core/Conversion/UnionOf.php @@ -9,6 +9,9 @@ use CasParser\Core\Conversion\Contracts\Converter; use CasParser\Core\Conversion\Contracts\ConverterSource; +/** + * @internal + */ final class UnionOf implements Converter { /** From 86ba48b8fc66042665d86362545915504f04011e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 03:23:47 +0000 Subject: [PATCH 22/29] fix: remove inaccurate `license` field in composer.json --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 36da290..752e12f 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,6 @@ "sort-packages": true }, "description": "Cas Parser PHP SDK", - "license": "APACHE-2.0", "name": "org-placeholder/cas-parser", "require": { "php": "^8.1", From 95b27e82468e3026ab7c6adad0af449ef2dd9355 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 03:39:04 +0000 Subject: [PATCH 23/29] chore(refactor): simplify base page interface --- src/Core/Concerns/SdkModel.php | 43 +++++++++++++++++++++------------ src/Core/Concerns/SdkPage.php | 7 ++++++ src/Core/Contracts/BasePage.php | 13 +--------- src/Core/Conversion/ModelOf.php | 13 ++++------ 4 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/Core/Concerns/SdkModel.php b/src/Core/Concerns/SdkModel.php index 2cf2154..1f68dd0 100644 --- a/src/Core/Concerns/SdkModel.php +++ b/src/Core/Concerns/SdkModel.php @@ -5,7 +5,6 @@ namespace CasParser\Core\Concerns; use CasParser\Core\Contracts\BaseModel; -use CasParser\Core\Contracts\BasePage; use CasParser\Core\Conversion; use CasParser\Core\Conversion\CoerceState; use CasParser\Core\Conversion\Contracts\Converter; @@ -15,7 +14,7 @@ /** * @internal * - * @template-covariant Data of array + * @template-covariant Shape of array */ trait SdkModel { @@ -41,12 +40,12 @@ public function __serialize(): array /** * @internal * - * @param array $data + * @param array $data */ public function __unserialize(array $data): void { foreach ($data as $key => $value) { - $this->offsetSet($key, value: $value); + $this->offsetSet($key, value: $value); // @phpstan-ignore-line } } @@ -73,12 +72,14 @@ public function __toString(): string * a native class property, indicating an omitted value, * or a property overridden with an incongruent type * + * @return value-of + * * @throws \Exception */ public function __get(string $key): mixed { if (!array_key_exists($key, array: self::$converter->properties)) { - throw new \Exception("Property '{$key}' does not exist in {$this}::class"); + throw new \RuntimeException("Property '{$key}' does not exist in {$this}::class"); } // The unset property was overridden by a value with an incongruent type. @@ -91,11 +92,11 @@ public function __get(string $key): mixed // An optional property which was unset to be omitted from serialized is being accessed. // Return null to match user's expectations. - return null; + return null; // @phpstan-ignore-line } /** - * @return Data + * @return Shape */ public function toArray(): array { @@ -104,6 +105,8 @@ public function toArray(): array /** * @internal + * + * @param key-of $offset */ public function offsetExists(mixed $offset): bool { @@ -130,6 +133,10 @@ public function offsetExists(mixed $offset): bool /** * @internal + * + * @param key-of $offset + * + * @return value-of */ public function &offsetGet(mixed $offset): mixed { @@ -137,19 +144,21 @@ public function &offsetGet(mixed $offset): mixed throw new \InvalidArgumentException; } - if (!$this->offsetExists($offset)) { - return null; + if (!$this->offsetExists($offset)) { // @phpstan-ignore-line + return null; // @phpstan-ignore-line } if (array_key_exists($offset, array: $this->_data)) { - return $this->_data[$offset]; + return $this->_data[$offset]; // @phpstan-ignore-line } - return $this->{$offset}; + return $this->{$offset}; // @phpstan-ignore-line } /** * @internal + * + * @param key-of $offset */ public function offsetSet(mixed $offset, mixed $value): void { @@ -163,9 +172,9 @@ public function offsetSet(mixed $offset, mixed $value): void $coerced = Conversion::coerce($type, value: $value, state: new CoerceState(translateNames: false)); - if (property_exists($this, property: $offset)) { + if (property_exists($this, property: $offset)) { // @phpstan-ignore-line try { - $this->{$offset} = $coerced; + $this->{$offset} = $coerced; // @phpstan-ignore-line unset($this->_data[$offset]); return; @@ -179,6 +188,8 @@ public function offsetSet(mixed $offset, mixed $value): void /** * @internal + * + * @param key-of $offset */ public function offsetUnset(mixed $offset): void { @@ -186,7 +197,7 @@ public function offsetUnset(mixed $offset): void throw new \InvalidArgumentException; } - if (property_exists($this, property: $offset)) { + if (property_exists($this, property: $offset)) { // @phpstan-ignore-line unset($this->{$offset}); } @@ -205,7 +216,7 @@ public function jsonSerialize(): array /** * @param array $data */ - public static function fromArray(array $data): self + public static function fromArray(array $data): static { return self::converter()->from($data); // @phpstan-ignore-line } @@ -249,7 +260,7 @@ private function unsetOptionalProperties(): void */ private static function serialize(mixed $value): mixed { - if ($value instanceof BaseModel || $value instanceof BasePage) { + if ($value instanceof BaseModel) { return $value->toArray(); } diff --git a/src/Core/Concerns/SdkPage.php b/src/Core/Concerns/SdkPage.php index 6576465..ffbe780 100644 --- a/src/Core/Concerns/SdkPage.php +++ b/src/Core/Concerns/SdkPage.php @@ -100,6 +100,13 @@ public function pagingEachItem(): \Generator } } + /** + * @param array $data + * + * @return static + */ + abstract public static function fromArray(array $data): static; + /** * @return array{normalized_request, RequestOptions} */ diff --git a/src/Core/Contracts/BasePage.php b/src/Core/Contracts/BasePage.php index b0719ee..ed36ab8 100644 --- a/src/Core/Contracts/BasePage.php +++ b/src/Core/Contracts/BasePage.php @@ -14,10 +14,9 @@ * * @template Item * - * @extends \ArrayAccess * @extends \IteratorAggregate */ -interface BasePage extends \ArrayAccess, \JsonSerializable, \Stringable, \IteratorAggregate +interface BasePage extends \IteratorAggregate { /** * @internal @@ -32,16 +31,6 @@ public function __construct( mixed $data, ); - /** - * @param array $data - * - * @return static - */ - public static function fromArray(array $data): self; - - /** @return array */ - public function toArray(): array; - public function hasNextPage(): bool; /** diff --git a/src/Core/Conversion/ModelOf.php b/src/Core/Conversion/ModelOf.php index f0103ca..0dbf406 100644 --- a/src/Core/Conversion/ModelOf.php +++ b/src/Core/Conversion/ModelOf.php @@ -6,7 +6,6 @@ use CasParser\Core\Attributes\Api; use CasParser\Core\Contracts\BaseModel; -use CasParser\Core\Contracts\BasePage; use CasParser\Core\Conversion; use CasParser\Core\Conversion\Contracts\Converter; @@ -21,7 +20,7 @@ final class ModelOf implements Converter public readonly array $properties; /** - * @param \ReflectionClass> $class + * @param \ReflectionClass $class */ public function __construct(public readonly \ReflectionClass $class) { @@ -89,15 +88,13 @@ public function coerce(mixed $value, CoerceState $state): mixed $acc[$name] = $item; } - return $this->from($acc); + return $this->from($acc); // @phpstan-ignore-line } /** - * @param array $data - * - * @return BaseModel|BasePage + * @param array $data */ - public function from(array $data): BaseModel|BasePage + public function from(array $data): BaseModel { $instance = $this->class->newInstanceWithoutConstructor(); $instance->__unserialize($data); // @phpstan-ignore-line @@ -107,7 +104,7 @@ public function from(array $data): BaseModel|BasePage public function dump(mixed $value, DumpState $state): mixed { - if ($value instanceof BaseModel || $value instanceof BasePage) { + if ($value instanceof BaseModel) { $value = $value->toArray(); } From 44033196dc6699b911b64f473f95badc0f548c71 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 29 Aug 2025 03:27:41 +0000 Subject: [PATCH 24/29] chore: add additional php doc tags --- .gitignore | 8 ++++---- composer.json | 1 + src/CasGenerator/CasGeneratorGenerateCasParams.php | 2 ++ src/CasParser/CasParserCamsKfintechParams.php | 2 ++ src/CasParser/CasParserCdslParams.php | 2 ++ src/CasParser/CasParserNsdlParams.php | 2 ++ src/CasParser/CasParserSmartParseParams.php | 2 ++ src/Client.php | 6 ++++++ src/Core/ServiceContracts/CasGeneratorContract.php | 2 ++ src/Core/ServiceContracts/CasParserContract.php | 8 ++++++++ src/Core/Services/CasGeneratorService.php | 2 ++ src/Core/Services/CasParserService.php | 8 ++++++++ 12 files changed, 41 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 70d76f1..6739884 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,11 @@ -*.swo -*.swp +composer.lock +docs/ .idea/ .php-cs-fixer.cache .php-cs-fixer.php -.phpdoc/ .phpunit.cache -composer.lock phpunit.xml playground/ +*.swo +*.swp vendor/ diff --git a/composer.json b/composer.json index 752e12f..7163498 100644 --- a/composer.json +++ b/composer.json @@ -44,6 +44,7 @@ "symfony/http-client": "^7" }, "scripts": { + "build:docs": "curl --etag-save ./vendor/ag.etags --etag-compare ./vendor/ag.etags --create-dirs --remote-name --output-dir ./vendor/bin --no-progress-meter -- https://github.com/ApiGen/ApiGen/releases/latest/download/apigen.phar && php ./vendor/bin/apigen.phar --output docs -- src", "lint": "./scripts/lint", "test": "./scripts/test" } diff --git a/src/CasGenerator/CasGeneratorGenerateCasParams.php b/src/CasGenerator/CasGeneratorGenerateCasParams.php index 10d952f..cfa554e 100644 --- a/src/CasGenerator/CasGeneratorGenerateCasParams.php +++ b/src/CasGenerator/CasGeneratorGenerateCasParams.php @@ -14,6 +14,8 @@ * This endpoint generates CAS (Consolidated Account Statement) documents by submitting a mailback request to the specified CAS authority. * Currently only supports KFintech, with plans to support CAMS, CDSL, and NSDL in the future. * + * @see CasParser\CasGenerator->generateCas + * * @phpstan-type cas_generator_generate_cas_params = array{ * email: string, * fromDate: string, diff --git a/src/CasParser/CasParserCamsKfintechParams.php b/src/CasParser/CasParserCamsKfintechParams.php index b90c8b4..db0a934 100644 --- a/src/CasParser/CasParserCamsKfintechParams.php +++ b/src/CasParser/CasParserCamsKfintechParams.php @@ -13,6 +13,8 @@ * This endpoint specifically parses CAMS/KFintech CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from CAMS or KFintech. * + * @see CasParser\CasParser->camsKfintech + * * @phpstan-type cas_parser_cams_kfintech_params = array{ * password?: string, pdfFile?: string, pdfURL?: string * } diff --git a/src/CasParser/CasParserCdslParams.php b/src/CasParser/CasParserCdslParams.php index eb3d930..53cd224 100644 --- a/src/CasParser/CasParserCdslParams.php +++ b/src/CasParser/CasParserCdslParams.php @@ -13,6 +13,8 @@ * This endpoint specifically parses CDSL CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from CDSL. * + * @see CasParser\CasParser->cdsl + * * @phpstan-type cas_parser_cdsl_params = array{ * password?: string, pdfFile?: string, pdfURL?: string * } diff --git a/src/CasParser/CasParserNsdlParams.php b/src/CasParser/CasParserNsdlParams.php index 0aca4b1..603eacc 100644 --- a/src/CasParser/CasParserNsdlParams.php +++ b/src/CasParser/CasParserNsdlParams.php @@ -13,6 +13,8 @@ * This endpoint specifically parses NSDL CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from NSDL. * + * @see CasParser\CasParser->nsdl + * * @phpstan-type cas_parser_nsdl_params = array{ * password?: string, pdfFile?: string, pdfURL?: string * } diff --git a/src/CasParser/CasParserSmartParseParams.php b/src/CasParser/CasParserSmartParseParams.php index 1a0ba15..9491057 100644 --- a/src/CasParser/CasParserSmartParseParams.php +++ b/src/CasParser/CasParserSmartParseParams.php @@ -13,6 +13,8 @@ * This endpoint parses CAS (Consolidated Account Statement) PDF files from NSDL, CDSL, or CAMS/KFintech and returns data in a unified format. * It auto-detects the CAS type and transforms the data into a consistent structure regardless of the source. * + * @see CasParser\CasParser->smartParse + * * @phpstan-type cas_parser_smart_parse_params = array{ * password?: string, pdfFile?: string, pdfURL?: string * } diff --git a/src/Client.php b/src/Client.php index ae5e493..e757f4f 100644 --- a/src/Client.php +++ b/src/Client.php @@ -12,8 +12,14 @@ class Client extends BaseClient { public string $apiKey; + /** + * @api + */ public CasParserService $casParser; + /** + * @api + */ public CasGeneratorService $casGenerator; public function __construct(?string $apiKey = null, ?string $baseUrl = null) diff --git a/src/Core/ServiceContracts/CasGeneratorContract.php b/src/Core/ServiceContracts/CasGeneratorContract.php index 2ee3281..5d2cab4 100644 --- a/src/Core/ServiceContracts/CasGeneratorContract.php +++ b/src/Core/ServiceContracts/CasGeneratorContract.php @@ -13,6 +13,8 @@ interface CasGeneratorContract { /** + * @api + * * @param string $email Email address to receive the CAS document * @param string $fromDate Start date for the CAS period (format YYYY-MM-DD) * @param string $password Password to protect the generated CAS PDF diff --git a/src/Core/ServiceContracts/CasParserContract.php b/src/Core/ServiceContracts/CasParserContract.php index e2fb15d..ac15e0a 100644 --- a/src/Core/ServiceContracts/CasParserContract.php +++ b/src/Core/ServiceContracts/CasParserContract.php @@ -12,6 +12,8 @@ interface CasParserContract { /** + * @api + * * @param string $password Password for the PDF file (if required) * @param string $pdfFile Base64 encoded CAS PDF file * @param string $pdfURL URL to the CAS PDF file @@ -24,6 +26,8 @@ public function camsKfintech( ): UnifiedResponse; /** + * @api + * * @param string $password Password for the PDF file (if required) * @param string $pdfFile Base64 encoded CAS PDF file * @param string $pdfURL URL to the CAS PDF file @@ -36,6 +40,8 @@ public function cdsl( ): UnifiedResponse; /** + * @api + * * @param string $password Password for the PDF file (if required) * @param string $pdfFile Base64 encoded CAS PDF file * @param string $pdfURL URL to the CAS PDF file @@ -48,6 +54,8 @@ public function nsdl( ): UnifiedResponse; /** + * @api + * * @param string $password Password for the PDF file (if required) * @param string $pdfFile Base64 encoded CAS PDF file * @param string $pdfURL URL to the CAS PDF file diff --git a/src/Core/Services/CasGeneratorService.php b/src/Core/Services/CasGeneratorService.php index 0a017a9..57fc5b1 100644 --- a/src/Core/Services/CasGeneratorService.php +++ b/src/Core/Services/CasGeneratorService.php @@ -18,6 +18,8 @@ final class CasGeneratorService implements CasGeneratorContract public function __construct(private Client $client) {} /** + * @api + * * This endpoint generates CAS (Consolidated Account Statement) documents by submitting a mailback request to the specified CAS authority. * Currently only supports KFintech, with plans to support CAMS, CDSL, and NSDL in the future. * diff --git a/src/Core/Services/CasParserService.php b/src/Core/Services/CasParserService.php index ae635e1..7f3b4b6 100644 --- a/src/Core/Services/CasParserService.php +++ b/src/Core/Services/CasParserService.php @@ -20,6 +20,8 @@ final class CasParserService implements CasParserContract public function __construct(private Client $client) {} /** + * @api + * * This endpoint specifically parses CAMS/KFintech CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from CAMS or KFintech. * @@ -49,6 +51,8 @@ public function camsKfintech( } /** + * @api + * * This endpoint specifically parses CDSL CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from CDSL. * @@ -78,6 +82,8 @@ public function cdsl( } /** + * @api + * * This endpoint specifically parses NSDL CAS (Consolidated Account Statement) PDF files and returns data in a unified format. * Use this endpoint when you know the PDF is from NSDL. * @@ -107,6 +113,8 @@ public function nsdl( } /** + * @api + * * This endpoint parses CAS (Consolidated Account Statement) PDF files from NSDL, CDSL, or CAMS/KFintech and returns data in a unified format. * It auto-detects the CAS type and transforms the data into a consistent structure regardless of the source. * From f1b303add63f19005284bec65d3b5907d8f34372 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 30 Aug 2025 03:06:09 +0000 Subject: [PATCH 25/29] chore: refactor request options --- src/Client.php | 11 +- src/Core/BaseClient.php | 52 +++++----- src/Core/Contracts/BasePage.php | 4 +- src/RequestOptions.php | 172 +++++++++++++++++--------------- 4 files changed, 128 insertions(+), 111 deletions(-) diff --git a/src/Client.php b/src/Client.php index e757f4f..c0f04b8 100644 --- a/src/Client.php +++ b/src/Client.php @@ -7,6 +7,8 @@ use CasParser\Core\BaseClient; use CasParser\Core\Services\CasGeneratorService; use CasParser\Core\Services\CasParserService; +use Http\Discovery\Psr17FactoryDiscovery; +use Http\Discovery\Psr18ClientDiscovery; class Client extends BaseClient { @@ -30,12 +32,19 @@ public function __construct(?string $apiKey = null, ?string $baseUrl = null) 'CAS_PARSER_BASE_URL' ) ?: 'https://portfolio-parser.api.casparser.in'; + $options = new RequestOptions( + uriFactory: Psr17FactoryDiscovery::findUriFactory(), + streamFactory: Psr17FactoryDiscovery::findStreamFactory(), + requestFactory: Psr17FactoryDiscovery::findRequestFactory(), + transporter: Psr18ClientDiscovery::find(), + ); + parent::__construct( headers: [ 'Content-Type' => 'application/json', 'Accept' => 'application/json', ], baseUrl: $base, - options: new RequestOptions, + options: $options, ); $this->casParser = new CasParserService($this); diff --git a/src/Core/BaseClient.php b/src/Core/BaseClient.php index d0be9fb..1e28dad 100644 --- a/src/Core/BaseClient.php +++ b/src/Core/BaseClient.php @@ -10,8 +10,6 @@ use CasParser\Core\Conversion\Contracts\ConverterSource; use CasParser\Core\Exceptions\APIStatusException; use CasParser\RequestOptions; -use Http\Discovery\Psr17FactoryDiscovery; -use Http\Discovery\Psr18ClientDiscovery; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\RequestInterface; @@ -33,14 +31,6 @@ class BaseClient { protected UriInterface $baseUrl; - protected UriFactoryInterface $uriFactory; - - protected StreamFactoryInterface $streamFactory; - - protected RequestFactoryInterface $requestFactory; - - protected ClientInterface $transporter; - /** * @param array|null> $headers */ @@ -49,12 +39,8 @@ public function __construct( string $baseUrl, protected RequestOptions $options = new RequestOptions, ) { - $this->uriFactory = Psr17FactoryDiscovery::findUriFactory(); - $this->streamFactory = Psr17FactoryDiscovery::findStreamFactory(); - $this->requestFactory = Psr17FactoryDiscovery::findRequestFactory(); - - $this->baseUrl = $this->uriFactory->createUri($baseUrl); - $this->transporter = Psr18ClientDiscovery::find(); + assert(null !== $this->options->uriFactory); + $this->baseUrl = $this->options->uriFactory->createUri($baseUrl); } /** @@ -63,6 +49,7 @@ public function __construct( * @param array $headers * @param class-string> $page * @param class-string> $stream + * @param RequestOptions|array|null $options */ public function request( string $method, @@ -73,17 +60,18 @@ public function request( string|Converter|ConverterSource|null $convert = null, ?string $page = null, ?string $stream = null, - mixed $options = [], + RequestOptions|array|null $options = [], ): mixed { // @phpstan-ignore-next-line [$req, $opts] = $this->buildRequest(method: $method, path: $path, query: $query, headers: $headers, body: $body, opts: $options); ['method' => $method, 'path' => $uri, 'headers' => $headers] = $req; + assert(null !== $opts->requestFactory); - $request = $this->requestFactory->createRequest($method, uri: $uri); + $request = $opts->requestFactory->createRequest($method, uri: $uri); $request = Util::withSetHeaders($request, headers: $headers); // @phpstan-ignore-next-line - $rsp = $this->sendRequest($request, data: $body, opts: $opts, redirectCount: 0, retryCount: 0); + $rsp = $this->sendRequest($opts, req: $request, data: $body, redirectCount: 0, retryCount: 0); $decoded = Util::decodeContent($rsp); @@ -123,14 +111,18 @@ protected function authHeaders(): array * @param string|list $path * @param array $query * @param array|null> $headers - * @param RequestOptions|array{ + * @param array{ * timeout?: float|null, * maxRetries?: int|null, * initialRetryDelay?: float|null, * maxRetryDelay?: float|null, - * extraHeaders?: list|null, - * extraQueryParams?: list|null, - * extraBodyParams?: list|null, + * extraHeaders?: array|null>|null, + * extraQueryParams?: array|null, + * extraBodyParams?: mixed, + * transporter?: ClientInterface|null, + * uriFactory?: UriFactoryInterface|null, + * streamFactory?: StreamFactoryInterface|null, + * requestFactory?: RequestFactoryInterface|null, * }|null $opts * * @return array{normalized_request, RequestOptions} @@ -143,7 +135,7 @@ protected function buildRequest( mixed $body, RequestOptions|array|null $opts, ): array { - $opts = [...$this->options->__serialize(), ...RequestOptions::parse($opts)->__serialize()]; + $opts = array_merge($this->options->toArray(), RequestOptions::parse($opts)->toArray()); $options = new RequestOptions(...$opts); $parsedPath = Util::parsePath($path); @@ -182,14 +174,16 @@ protected function followRedirect( * mixed,>|null $data */ protected function sendRequest( + RequestOptions $opts, RequestInterface $req, mixed $data, - RequestOptions $opts, int $retryCount, int $redirectCount, ): ResponseInterface { - $req = Util::withSetBody($this->streamFactory, req: $req, body: $data); - $rsp = $this->transporter->sendRequest($req); + assert(null !== $opts->streamFactory && null !== $opts->transporter); + + $req = Util::withSetBody($opts->streamFactory, req: $req, body: $data); + $rsp = $opts->transporter->sendRequest($req); $code = $rsp->getStatusCode(); if ($code >= 300 && $code < 400) { @@ -199,7 +193,7 @@ protected function sendRequest( $req = $this->followRedirect($rsp, req: $req); - return $this->sendRequest($req, data: $data, opts: $opts, retryCount: $retryCount, redirectCount: ++$redirectCount); + return $this->sendRequest($opts, req: $req, data: $data, retryCount: $retryCount, redirectCount: ++$redirectCount); } if ($code >= 400 && $code < 500) { @@ -209,7 +203,7 @@ protected function sendRequest( if ($code >= 500 && $retryCount < $opts->maxRetries) { usleep((int) $opts->initialRetryDelay); - return $this->sendRequest($req, data: $data, opts: $opts, retryCount: ++$retryCount, redirectCount: $redirectCount); + return $this->sendRequest($opts, req: $req, data: $data, retryCount: ++$retryCount, redirectCount: $redirectCount); } return $rsp; diff --git a/src/Core/Contracts/BasePage.php b/src/Core/Contracts/BasePage.php index ed36ab8..40eecd6 100644 --- a/src/Core/Contracts/BasePage.php +++ b/src/Core/Contracts/BasePage.php @@ -12,6 +12,8 @@ /** * @internal * + * @phpstan-import-type normalized_request from \CasParser\Core\BaseClient + * * @template Item * * @extends \IteratorAggregate @@ -21,7 +23,7 @@ interface BasePage extends \IteratorAggregate /** * @internal * - * @param array $request + * @param normalized_request $request */ public function __construct( Converter|ConverterSource|string $convert, diff --git a/src/RequestOptions.php b/src/RequestOptions.php index 1c9b4ff..7512679 100644 --- a/src/RequestOptions.php +++ b/src/RequestOptions.php @@ -4,99 +4,111 @@ namespace CasParser; -class RequestOptions +use CasParser\Core\Attributes\Api as Property; +use CasParser\Core\Concerns\SdkModel; +use CasParser\Core\Contracts\BaseModel; +use CasParser\Core\Implementation\Omittable; +use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestFactoryInterface; +use Psr\Http\Message\StreamFactoryInterface; +use Psr\Http\Message\UriFactoryInterface; + +use const CasParser\Core\OMIT as omit; + +/** + * @phpstan-type request_options = array{ + * timeout?: float|null, + * maxRetries?: int|null, + * initialRetryDelay?: float|null, + * maxRetryDelay?: float|null, + * extraHeaders?: array>|null, + * extraQueryParams?: array|null, + * extraBodyParams?: mixed, + * transporter?: ClientInterface|null, + * uriFactory?: UriFactoryInterface|null, + * streamFactory?: StreamFactoryInterface|null, + * requestFactory?: RequestFactoryInterface|null, + * } + * @phpstan-type request_opts = null|RequestOptions|request_options + */ +final class RequestOptions implements BaseModel { - public const DEFAULT_TIMEOUT = 60; + /** @use SdkModel */ + use SdkModel; - public const DEFAULT_MAX_RETRIES = 2; + #[Property] + public float $timeout = 60; - public const DEFAULT_INITIAL_RETRYDELAY = 0.5; + #[Property] + public int $maxRetries = 2; - public const DEFAULT_MAX_RETRY_DELAY = 8.0; + #[Property] + public float $initialRetryDelay = 0.5; - /** - * @param list $extraHeaders - * @param list $extraQueryParams - * @param list $extraBodyParams - */ - public function __construct( - public float $timeout = self::DEFAULT_TIMEOUT, - public int $maxRetries = self::DEFAULT_MAX_RETRIES, - public float $initialRetryDelay = self::DEFAULT_INITIAL_RETRYDELAY, - public float $maxRetryDelay = self::DEFAULT_MAX_RETRY_DELAY, - public array $extraHeaders = [], - public array $extraQueryParams = [], - public array $extraBodyParams = [], - ) {} + #[Property] + public float $maxRetryDelay = 8.0; - /** - * @return array{ - * timeout: float, - * maxRetries: int, - * initialRetryDelay: float, - * maxRetryDelay: float, - * extraHeaders: list, - * extraQueryParams: list, - * extraBodyParams: list, - * } - */ - public function __serialize(): array - { - return [ - 'timeout' => $this->timeout, - 'maxRetries' => $this->maxRetries, - 'initialRetryDelay' => $this->initialRetryDelay, - 'maxRetryDelay' => $this->maxRetryDelay, - 'extraHeaders' => $this->extraHeaders, - 'extraQueryParams' => $this->extraQueryParams, - 'extraBodyParams' => $this->extraBodyParams, - ]; - } + /** @var array|null> $extraHeaders */ + #[Property] + public array $extraHeaders = []; + + /** @var array $extraQueryParams */ + #[Property] + public array $extraQueryParams = []; + + #[Property] + public mixed $extraBodyParams; + + #[Property(optional: true)] + public ?ClientInterface $transporter; + + #[Property(optional: true)] + public ?UriFactoryInterface $uriFactory; + + #[Property(optional: true)] + public ?StreamFactoryInterface $streamFactory; + + #[Property(optional: true)] + public ?RequestFactoryInterface $requestFactory; /** - * @param array{ - * timeout?: float|null, - * maxRetries?: int|null, - * initialRetryDelay?: float|null, - * maxRetryDelay?: float|null, - * extraHeaders?: list|null, - * extraQueryParams?: list|null, - * extraBodyParams?: list|null, - * } $data + * @param array|null>|null $extraHeaders + * @param array|null $extraQueryParams + * @param mixed|Omittable $extraBodyParams */ - public function __unserialize(array $data): void - { - $this->timeout = $data['timeout'] ?? self::DEFAULT_TIMEOUT; - $this - ->maxRetries = $data['maxRetries'] ?? self::DEFAULT_MAX_RETRIES - ; - $this - ->initialRetryDelay = $data[ - 'initialRetryDelay' - ] ?? self::DEFAULT_INITIAL_RETRYDELAY - ; - $this->maxRetryDelay = $data[ - 'maxRetryDelay' - ] ?? self::DEFAULT_MAX_RETRY_DELAY; - $this->extraHeaders = $data[ - 'extraHeaders' - ] ?? []; - $this->extraQueryParams = $data['extraQueryParams'] ?? []; - $this - ->extraBodyParams = $data['extraBodyParams'] ?? [] + public function __construct( + ?float $timeout = null, + ?int $maxRetries = null, + ?float $initialRetryDelay = null, + ?float $maxRetryDelay = null, + ?array $extraHeaders = null, + ?array $extraQueryParams = null, + mixed $extraBodyParams = omit, + ?ClientInterface $transporter = null, + ?UriFactoryInterface $uriFactory = null, + ?StreamFactoryInterface $streamFactory = null, + ?RequestFactoryInterface $requestFactory = null, + ) { + self::introspect(); + $this->unsetOptionalProperties(); + + null !== $timeout && $this->timeout = $timeout; + null !== $maxRetries && $this->maxRetries = $maxRetries; + null !== $initialRetryDelay && $this + ->initialRetryDelay = $initialRetryDelay ; + null !== $maxRetryDelay && $this->maxRetryDelay = $maxRetryDelay; + null !== $extraHeaders && $this->extraHeaders = $extraHeaders; + null !== $extraQueryParams && $this->extraQueryParams = $extraQueryParams; + omit !== $extraBodyParams && $this->extraBodyParams = $extraBodyParams; + null !== $transporter && $this->transporter = $transporter; + null !== $uriFactory && $this->uriFactory = $uriFactory; + null !== $streamFactory && $this->streamFactory = $streamFactory; + null !== $requestFactory && $this->requestFactory = $requestFactory; } /** - * @param RequestOptions|array{ - * timeout?: float|null, - * maxRetries?: int|null, - * initialRetryDelay?: float|null, - * maxRetryDelay?: float|null, - * extraHeaders?: list|null, - * extraQueryParams?: list|null, - * extraBodyParams?: list|null, - * }|null $options + * @param request_opts|null $options */ public static function parse(RequestOptions|array|null $options): self { From f38ec08395db6f5fcf1c8a8e2ccae49cd0e4537a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 30 Aug 2025 03:08:57 +0000 Subject: [PATCH 26/29] chore: simplify model initialization --- src/CasGenerator/CasGeneratorGenerateCasParams.php | 3 +-- .../CasGeneratorGenerateCasResponse.php | 3 +-- src/CasParser/CasParserCamsKfintechParams.php | 3 +-- src/CasParser/CasParserCdslParams.php | 3 +-- src/CasParser/CasParserNsdlParams.php | 3 +-- src/CasParser/CasParserSmartParseParams.php | 3 +-- src/CasParser/UnifiedResponse.php | 3 +-- src/CasParser/UnifiedResponse/DematAccount.php | 3 +-- .../DematAccount/AdditionalInfo.php | 3 +-- .../UnifiedResponse/DematAccount/Holdings.php | 3 +-- .../UnifiedResponse/DematAccount/Holdings/Aif.php | 3 +-- .../DematAccount/Holdings/CorporateBond.php | 3 +-- .../DematAccount/Holdings/DematMutualFund.php | 3 +-- .../DematAccount/Holdings/Equity.php | 3 +-- .../DematAccount/Holdings/GovernmentSecurity.php | 3 +-- src/CasParser/UnifiedResponse/Insurance.php | 3 +-- .../Insurance/LifeInsurancePolicy.php | 3 +-- src/CasParser/UnifiedResponse/Investor.php | 3 +-- src/CasParser/UnifiedResponse/Meta.php | 3 +-- .../UnifiedResponse/Meta/StatementPeriod.php | 3 +-- src/CasParser/UnifiedResponse/MutualFund.php | 3 +-- .../UnifiedResponse/MutualFund/AdditionalInfo.php | 3 +-- .../UnifiedResponse/MutualFund/Scheme.php | 3 +-- .../MutualFund/Scheme/AdditionalInfo.php | 3 +-- .../UnifiedResponse/MutualFund/Scheme/Gain.php | 3 +-- .../MutualFund/Scheme/Transaction.php | 3 +-- src/CasParser/UnifiedResponse/Summary.php | 3 +-- src/CasParser/UnifiedResponse/Summary/Accounts.php | 3 +-- .../UnifiedResponse/Summary/Accounts/Demat.php | 3 +-- .../UnifiedResponse/Summary/Accounts/Insurance.php | 3 +-- .../Summary/Accounts/MutualFunds.php | 3 +-- src/Core/Concerns/SdkModel.php | 14 +++++--------- src/Core/Concerns/SdkPage.php | 8 ++++++-- src/Core/Services/CasGeneratorService.php | 3 +++ src/Core/Services/CasParserService.php | 3 +++ src/RequestOptions.php | 3 +-- tests/Core/TestModel.php | 5 ++--- 37 files changed, 51 insertions(+), 78 deletions(-) diff --git a/src/CasGenerator/CasGeneratorGenerateCasParams.php b/src/CasGenerator/CasGeneratorGenerateCasParams.php index cfa554e..f8f5c20 100644 --- a/src/CasGenerator/CasGeneratorGenerateCasParams.php +++ b/src/CasGenerator/CasGeneratorGenerateCasParams.php @@ -91,8 +91,7 @@ final class CasGeneratorGenerateCasParams implements BaseModel */ public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasGenerator/CasGeneratorGenerateCasResponse.php b/src/CasGenerator/CasGeneratorGenerateCasResponse.php index 1d211cf..97e841e 100644 --- a/src/CasGenerator/CasGeneratorGenerateCasResponse.php +++ b/src/CasGenerator/CasGeneratorGenerateCasResponse.php @@ -26,8 +26,7 @@ final class CasGeneratorGenerateCasResponse implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/CasParserCamsKfintechParams.php b/src/CasParser/CasParserCamsKfintechParams.php index db0a934..0dda13d 100644 --- a/src/CasParser/CasParserCamsKfintechParams.php +++ b/src/CasParser/CasParserCamsKfintechParams.php @@ -45,8 +45,7 @@ final class CasParserCamsKfintechParams implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/CasParserCdslParams.php b/src/CasParser/CasParserCdslParams.php index 53cd224..c8401d7 100644 --- a/src/CasParser/CasParserCdslParams.php +++ b/src/CasParser/CasParserCdslParams.php @@ -45,8 +45,7 @@ final class CasParserCdslParams implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/CasParserNsdlParams.php b/src/CasParser/CasParserNsdlParams.php index 603eacc..df6c11a 100644 --- a/src/CasParser/CasParserNsdlParams.php +++ b/src/CasParser/CasParserNsdlParams.php @@ -45,8 +45,7 @@ final class CasParserNsdlParams implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/CasParserSmartParseParams.php b/src/CasParser/CasParserSmartParseParams.php index 9491057..5596cf1 100644 --- a/src/CasParser/CasParserSmartParseParams.php +++ b/src/CasParser/CasParserSmartParseParams.php @@ -45,8 +45,7 @@ final class CasParserSmartParseParams implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse.php b/src/CasParser/UnifiedResponse.php index 87eba69..2e0c131 100644 --- a/src/CasParser/UnifiedResponse.php +++ b/src/CasParser/UnifiedResponse.php @@ -51,8 +51,7 @@ final class UnifiedResponse implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/DematAccount.php b/src/CasParser/UnifiedResponse/DematAccount.php index e63c58e..1833505 100644 --- a/src/CasParser/UnifiedResponse/DematAccount.php +++ b/src/CasParser/UnifiedResponse/DematAccount.php @@ -77,8 +77,7 @@ final class DematAccount implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php index abfc204..90b1ca7 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/DematAccount/AdditionalInfo.php @@ -79,8 +79,7 @@ final class AdditionalInfo implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php index 6742ece..f942fef 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings.php @@ -53,8 +53,7 @@ final class Holdings implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php index 6055554..d893d44 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Aif.php @@ -54,8 +54,7 @@ final class Aif implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php index c7d479e..2f86609 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/CorporateBond.php @@ -54,8 +54,7 @@ final class CorporateBond implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php index b49eda3..3d14ad3 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/DematMutualFund.php @@ -54,8 +54,7 @@ final class DematMutualFund implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php index 4220716..771f7ec 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/Equity.php @@ -54,8 +54,7 @@ final class Equity implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php b/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php index 731f7d6..0755acd 100644 --- a/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php +++ b/src/CasParser/UnifiedResponse/DematAccount/Holdings/GovernmentSecurity.php @@ -54,8 +54,7 @@ final class GovernmentSecurity implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/Insurance.php b/src/CasParser/UnifiedResponse/Insurance.php index 63d5579..49d0ef6 100644 --- a/src/CasParser/UnifiedResponse/Insurance.php +++ b/src/CasParser/UnifiedResponse/Insurance.php @@ -29,8 +29,7 @@ final class Insurance implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php b/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php index 710655c..58eaa3d 100644 --- a/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php +++ b/src/CasParser/UnifiedResponse/Insurance/LifeInsurancePolicy.php @@ -82,8 +82,7 @@ final class LifeInsurancePolicy implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/Investor.php b/src/CasParser/UnifiedResponse/Investor.php index 178e313..e2befea 100644 --- a/src/CasParser/UnifiedResponse/Investor.php +++ b/src/CasParser/UnifiedResponse/Investor.php @@ -68,8 +68,7 @@ final class Investor implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/Meta.php b/src/CasParser/UnifiedResponse/Meta.php index fc1cc9e..1187ef3 100644 --- a/src/CasParser/UnifiedResponse/Meta.php +++ b/src/CasParser/UnifiedResponse/Meta.php @@ -41,8 +41,7 @@ final class Meta implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php b/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php index 039a1ae..eb49ce1 100644 --- a/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php +++ b/src/CasParser/UnifiedResponse/Meta/StatementPeriod.php @@ -32,8 +32,7 @@ final class StatementPeriod implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/MutualFund.php b/src/CasParser/UnifiedResponse/MutualFund.php index 9fbfe52..f95a9f8 100644 --- a/src/CasParser/UnifiedResponse/MutualFund.php +++ b/src/CasParser/UnifiedResponse/MutualFund.php @@ -61,8 +61,7 @@ final class MutualFund implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php b/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php index ee35a57..bf28a8b 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/MutualFund/AdditionalInfo.php @@ -40,8 +40,7 @@ final class AdditionalInfo implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php index ea61498..4923d8c 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme.php @@ -99,8 +99,7 @@ final class Scheme implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php index 622d843..d12d703 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/AdditionalInfo.php @@ -56,8 +56,7 @@ final class AdditionalInfo implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php index 17404b4..f951aba 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Gain.php @@ -30,8 +30,7 @@ final class Gain implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php index d0f88b7..90f6ad6 100644 --- a/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php +++ b/src/CasParser/UnifiedResponse/MutualFund/Scheme/Transaction.php @@ -75,8 +75,7 @@ final class Transaction implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/Summary.php b/src/CasParser/UnifiedResponse/Summary.php index e807258..0b9d4fb 100644 --- a/src/CasParser/UnifiedResponse/Summary.php +++ b/src/CasParser/UnifiedResponse/Summary.php @@ -30,8 +30,7 @@ final class Summary implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts.php b/src/CasParser/UnifiedResponse/Summary/Accounts.php index 3d1823f..d80af8b 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts.php @@ -32,8 +32,7 @@ final class Accounts implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php b/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php index 9dcd487..8eb9bf5 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/Demat.php @@ -30,8 +30,7 @@ final class Demat implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php b/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php index 6af8f26..fce66f1 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/Insurance.php @@ -30,8 +30,7 @@ final class Insurance implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php b/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php index 0ff7095..9ce16cb 100644 --- a/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php +++ b/src/CasParser/UnifiedResponse/Summary/Accounts/MutualFunds.php @@ -30,8 +30,7 @@ final class MutualFunds implements BaseModel public function __construct() { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); } /** diff --git a/src/Core/Concerns/SdkModel.php b/src/Core/Concerns/SdkModel.php index 1f68dd0..0e94205 100644 --- a/src/Core/Concerns/SdkModel.php +++ b/src/Core/Concerns/SdkModel.php @@ -50,6 +50,8 @@ public function __unserialize(array $data): void } /** + * @internal + * * @return array */ public function __debugInfo(): array @@ -135,8 +137,6 @@ public function offsetExists(mixed $offset): bool * @internal * * @param key-of $offset - * - * @return value-of */ public function &offsetGet(mixed $offset): mixed { @@ -205,6 +205,8 @@ public function offsetUnset(mixed $offset): void } /** + * @internal + * * @return array */ public function jsonSerialize(): array @@ -238,16 +240,10 @@ public static function converter(): Converter /** * @internal */ - public static function introspect(): void + private function initialize(): void { static::converter(); - } - /** - * @internal - */ - private function unsetOptionalProperties(): void - { foreach (self::$converter->properties as $name => $info) { if ($info->optional) { unset($this->{$name}); diff --git a/src/Core/Concerns/SdkPage.php b/src/Core/Concerns/SdkPage.php index ffbe780..20e1c71 100644 --- a/src/Core/Concerns/SdkPage.php +++ b/src/Core/Concerns/SdkPage.php @@ -70,7 +70,7 @@ public function getNextPage(): static } /** - * Generator yielding each page (instance of static). + * Iterator yielding each page (instance of static). * * @return \Generator */ @@ -87,7 +87,7 @@ public function getIterator(): \Generator } /** - * Generator yielding each item across all pages. + * Iterator yielding each item across all pages. * * @return \Generator */ @@ -101,6 +101,8 @@ public function pagingEachItem(): \Generator } /** + * @internal + * * @param array $data * * @return static @@ -108,6 +110,8 @@ public function pagingEachItem(): \Generator abstract public static function fromArray(array $data): static; /** + * @internal + * * @return array{normalized_request, RequestOptions} */ abstract protected function nextRequest(): ?array; diff --git a/src/Core/Services/CasGeneratorService.php b/src/Core/Services/CasGeneratorService.php index 57fc5b1..1fb4a94 100644 --- a/src/Core/Services/CasGeneratorService.php +++ b/src/Core/Services/CasGeneratorService.php @@ -15,6 +15,9 @@ final class CasGeneratorService implements CasGeneratorContract { + /** + * @internal + */ public function __construct(private Client $client) {} /** diff --git a/src/Core/Services/CasParserService.php b/src/Core/Services/CasParserService.php index 7f3b4b6..27fbe5a 100644 --- a/src/Core/Services/CasParserService.php +++ b/src/Core/Services/CasParserService.php @@ -17,6 +17,9 @@ final class CasParserService implements CasParserContract { + /** + * @internal + */ public function __construct(private Client $client) {} /** diff --git a/src/RequestOptions.php b/src/RequestOptions.php index 7512679..66efefc 100644 --- a/src/RequestOptions.php +++ b/src/RequestOptions.php @@ -89,8 +89,7 @@ public function __construct( ?StreamFactoryInterface $streamFactory = null, ?RequestFactoryInterface $requestFactory = null, ) { - self::introspect(); - $this->unsetOptionalProperties(); + $this->initialize(); null !== $timeout && $this->timeout = $timeout; null !== $maxRetries && $this->maxRetries = $maxRetries; diff --git a/tests/Core/TestModel.php b/tests/Core/TestModel.php index 7c56501..4d24c06 100644 --- a/tests/Core/TestModel.php +++ b/tests/Core/TestModel.php @@ -36,13 +36,12 @@ public function __construct( ?string $owner, ?array $friends = null, ) { + $this->initialize(); + $this->name = $name; $this->ageYears = $ageYears; $this->owner = $owner; - self::introspect(); - $this->unsetOptionalProperties(); - null != $friends && $this->friends = $friends; } } From 7f3730ee1d2081c219209b4cf06a9568af6b2559 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 30 Aug 2025 03:10:24 +0000 Subject: [PATCH 27/29] feat!: use builders for RequestOptions --- README.md | 10 +- src/Client.php | 2 +- src/Core.php | 4 +- src/Core/BaseClient.php | 19 +- src/Core/Concerns/SdkParams.php | 23 +-- .../{Omittable.php => Omit.php} | 4 +- src/RequestOptions.php | 164 ++++++++++++++---- 7 files changed, 156 insertions(+), 70 deletions(-) rename src/Core/Implementation/{Omittable.php => Omit.php} (76%) diff --git a/README.md b/README.md index 3da87eb..75f8882 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ Certain errors will be automatically retried 2 times by default, with a short ex Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict, 429 Rate Limit, >=500 Internal errors, and timeouts will all be retried by default. -You can use the `max_retries` option to configure or disable this: +You can use the `maxRetries` option to configure or disable this: ```php casParser->smartParse(new RequestOptions(maxRetries: 5)); +$result = $client->casParser->smartParse( + requestOptions: RequestOptions::with(maxRetries: 5) +); ``` ## Advanced concepts @@ -129,7 +131,7 @@ $result = $client->casParser->smartParse(new RequestOptions(maxRetries: 5)); You can send undocumented parameters to any endpoint, and read undocumented response properties, like so: -Note: the `extra_` parameters of the same name overrides the documented parameters. +Note: the `extra*` parameters of the same name overrides the documented parameters. ```php casParser->smartParse( - new RequestOptions( + requestOptions: RequestOptions::with( extraQueryParams: ["my_query_parameter" => "value"], extraBodyParams: ["my_body_parameter" => "value"], extraHeaders: ["my-header" => "value"], diff --git a/src/Client.php b/src/Client.php index c0f04b8..2f39108 100644 --- a/src/Client.php +++ b/src/Client.php @@ -32,7 +32,7 @@ public function __construct(?string $apiKey = null, ?string $baseUrl = null) 'CAS_PARSER_BASE_URL' ) ?: 'https://portfolio-parser.api.casparser.in'; - $options = new RequestOptions( + $options = RequestOptions::with( uriFactory: Psr17FactoryDiscovery::findUriFactory(), streamFactory: Psr17FactoryDiscovery::findStreamFactory(), requestFactory: Psr17FactoryDiscovery::findRequestFactory(), diff --git a/src/Core.php b/src/Core.php index 22b6470..45dd12c 100644 --- a/src/Core.php +++ b/src/Core.php @@ -4,6 +4,6 @@ namespace CasParser\Core; -use CasParser\Core\Implementation\Omittable; +use CasParser\Core\Implementation\Omit; -const OMIT = Omittable::OMIT; +const OMIT = Omit::omit; diff --git a/src/Core/BaseClient.php b/src/Core/BaseClient.php index 1e28dad..6eb8b01 100644 --- a/src/Core/BaseClient.php +++ b/src/Core/BaseClient.php @@ -32,6 +32,8 @@ class BaseClient protected UriInterface $baseUrl; /** + * @internal + * * @param array|null> $headers */ public function __construct( @@ -108,6 +110,8 @@ protected function authHeaders(): array } /** + * @internal + * * @param string|list $path * @param array $query * @param array|null> $headers @@ -135,26 +139,31 @@ protected function buildRequest( mixed $body, RequestOptions|array|null $opts, ): array { - $opts = array_merge($this->options->toArray(), RequestOptions::parse($opts)->toArray()); - $options = new RequestOptions(...$opts); + $options = RequestOptions::parse($this->options, $opts); $parsedPath = Util::parsePath($path); /** @var array $mergedQuery */ - $mergedQuery = array_merge_recursive($query, $options->extraQueryParams); + $mergedQuery = array_merge_recursive( + $query, + $options->extraQueryParams ?? [], + ); $uri = Util::joinUri($this->baseUrl, path: $parsedPath, query: $mergedQuery)->__toString(); /** @var array|null> $mergedHeaders */ $mergedHeaders = [...$this->headers, ...$this->authHeaders(), ...$headers, - ...$options->extraHeaders, ]; + ...($options->extraHeaders ?? []), ]; $req = ['method' => strtoupper($method), 'path' => $uri, 'query' => $mergedQuery, 'headers' => $mergedHeaders, 'body' => $body]; return [$req, $options]; } + /** + * @internal + */ protected function followRedirect( ResponseInterface $rsp, RequestInterface $req @@ -170,6 +179,8 @@ protected function followRedirect( } /** + * @internal + * * @param bool|int|float|string|resource|\Traversable|array|null $data */ diff --git a/src/Core/Concerns/SdkParams.php b/src/Core/Concerns/SdkParams.php index 71d2e80..1010389 100644 --- a/src/Core/Concerns/SdkParams.php +++ b/src/Core/Concerns/SdkParams.php @@ -18,15 +18,7 @@ trait SdkParams * @param array|self|null $params * @param array|RequestOptions|null $options * - * @return array{array, array{ - * timeout: float, - * maxRetries: int, - * initialRetryDelay: float, - * maxRetryDelay: float, - * extraHeaders: list, - * extraQueryParams: list, - * extraBodyParams: list, - * }} + * @return array{array, RequestOptions} */ public static function parseRequest(array|self|null $params, array|RequestOptions|null $options): array { @@ -40,17 +32,6 @@ public static function parseRequest(array|self|null $params, array|RequestOption $opts->maxRetries = 0; } - $opt = $opts->__serialize(); - if (empty($opt['extraHeaders'])) { - unset($opt['extraHeaders']); - } - if (empty($opt['extraQueryParams'])) { - unset($opt['extraQueryParams']); - } - if (empty($opt['extraBodyParams'])) { - unset($opt['extraBodyParams']); - } - - return [$dumped, $opt]; // @phpstan-ignore-line + return [$dumped, $opts]; // @phpstan-ignore-line } } diff --git a/src/Core/Implementation/Omittable.php b/src/Core/Implementation/Omit.php similarity index 76% rename from src/Core/Implementation/Omittable.php rename to src/Core/Implementation/Omit.php index 3dac9c1..d79cd57 100644 --- a/src/Core/Implementation/Omittable.php +++ b/src/Core/Implementation/Omit.php @@ -7,7 +7,7 @@ /** * @internal */ -enum Omittable +enum Omit { - case OMIT; + case omit; } diff --git a/src/RequestOptions.php b/src/RequestOptions.php index 66efefc..03ece1a 100644 --- a/src/RequestOptions.php +++ b/src/RequestOptions.php @@ -7,7 +7,7 @@ use CasParser\Core\Attributes\Api as Property; use CasParser\Core\Concerns\SdkModel; use CasParser\Core\Contracts\BaseModel; -use CasParser\Core\Implementation\Omittable; +use CasParser\Core\Implementation\Omit; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; @@ -48,15 +48,15 @@ final class RequestOptions implements BaseModel #[Property] public float $maxRetryDelay = 8.0; - /** @var array|null> $extraHeaders */ - #[Property] - public array $extraHeaders = []; + /** @var array|null>|null $extraHeaders */ + #[Property(optional: true)] + public ?array $extraHeaders; - /** @var array $extraQueryParams */ - #[Property] - public array $extraQueryParams = []; + /** @var array|null $extraQueryParams */ + #[Property(optional: true)] + public ?array $extraQueryParams; - #[Property] + #[Property(optional: true)] public mixed $extraBodyParams; #[Property(optional: true)] @@ -71,12 +71,27 @@ final class RequestOptions implements BaseModel #[Property(optional: true)] public ?RequestFactoryInterface $requestFactory; + public function __construct() + { + $this->initialize(); + } + + /** + * @param request_opts|null $options + */ + public static function parse(RequestOptions|array|null ...$options): self + { + $parsed = array_map(static fn ($o) => $o instanceof self ? $o->toArray() : $o ?? [], array: $options); + + return self::with(...array_merge(...$parsed)); // @phpstan-ignore-line + } + /** * @param array|null>|null $extraHeaders * @param array|null $extraQueryParams - * @param mixed|Omittable $extraBodyParams + * @param mixed|Omit $extraBodyParams */ - public function __construct( + public static function with( ?float $timeout = null, ?int $maxRetries = null, ?float $initialRetryDelay = null, @@ -88,40 +103,117 @@ public function __construct( ?UriFactoryInterface $uriFactory = null, ?StreamFactoryInterface $streamFactory = null, ?RequestFactoryInterface $requestFactory = null, - ) { - $this->initialize(); + ): self { + $obj = new self; + + null !== $timeout && $obj->timeout = $timeout; + null !== $maxRetries && $obj->maxRetries = $maxRetries; + null !== $initialRetryDelay && $obj->initialRetryDelay = $initialRetryDelay; + null !== $maxRetryDelay && $obj->maxRetryDelay = $maxRetryDelay; + null !== $extraHeaders && $obj->extraHeaders = $extraHeaders; + null !== $extraQueryParams && $obj->extraQueryParams = $extraQueryParams; + omit !== $extraBodyParams && $obj->extraBodyParams = $extraBodyParams; + null !== $transporter && $obj->transporter = $transporter; + null !== $uriFactory && $obj->uriFactory = $uriFactory; + null !== $streamFactory && $obj->streamFactory = $streamFactory; + null !== $requestFactory && $obj->requestFactory = $requestFactory; + + return $obj; + } + + public function withTimeout(float $timeout): self + { + $obj = clone $this; + $obj->timeout = $timeout; + + return $obj; + } + + public function withMaxRetries(int $maxRetries): self + { + $obj = clone $this; + $obj->maxRetries = $maxRetries; + + return $obj; + } + + public function withInitialRetryDelay(float $initialRetryDelay): self + { + $obj = clone $this; + $obj->initialRetryDelay = $initialRetryDelay; - null !== $timeout && $this->timeout = $timeout; - null !== $maxRetries && $this->maxRetries = $maxRetries; - null !== $initialRetryDelay && $this - ->initialRetryDelay = $initialRetryDelay - ; - null !== $maxRetryDelay && $this->maxRetryDelay = $maxRetryDelay; - null !== $extraHeaders && $this->extraHeaders = $extraHeaders; - null !== $extraQueryParams && $this->extraQueryParams = $extraQueryParams; - omit !== $extraBodyParams && $this->extraBodyParams = $extraBodyParams; - null !== $transporter && $this->transporter = $transporter; - null !== $uriFactory && $this->uriFactory = $uriFactory; - null !== $streamFactory && $this->streamFactory = $streamFactory; - null !== $requestFactory && $this->requestFactory = $requestFactory; + return $obj; + } + + public function withMaxRetryDelay(float $maxRetryDelay): self + { + $obj = clone $this; + $obj->maxRetryDelay = $maxRetryDelay; + + return $obj; } /** - * @param request_opts|null $options + * @param array|null> $extraHeaders + */ + public function withExtraHeaders(array $extraHeaders): self + { + $obj = clone $this; + $obj->extraHeaders = $extraHeaders; + + return $obj; + } + + /** + * @param array $extraQueryParams */ - public static function parse(RequestOptions|array|null $options): self + public function withExtraQueryParams(array $extraQueryParams): self + { + $obj = clone $this; + $obj->extraQueryParams = $extraQueryParams; + + return $obj; + } + + public function withExtraBodyParams(mixed $extraBodyParams): self + { + $obj = clone $this; + $obj->extraBodyParams = $extraBodyParams; + + return $obj; + } + + public function withTransporter(ClientInterface $transporter): self + { + $obj = clone $this; + $obj->transporter = $transporter; + + return $obj; + } + + public function withUriFactory(UriFactoryInterface $uriFactory): self { - if (is_null($options)) { - return new self; - } + $obj = clone $this; + $obj->uriFactory = $uriFactory; - if ($options instanceof self) { - return $options; - } + return $obj; + } + + public function withStreamFactory( + StreamFactoryInterface $streamFactory + ): self { + $obj = clone $this; + $obj->streamFactory = $streamFactory; + + return $obj; + } - $opts = new self; - $opts->__unserialize($options); + public function withRequestFactory( + RequestFactoryInterface $requestFactory + ): self { + $obj = clone $this; + $obj->requestFactory = $requestFactory; - return $opts; + return $obj; } } From 36dc0b68f8706fe3bd5fcbb7f57c5487cbea2496 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 02:49:58 +0000 Subject: [PATCH 28/29] chore(internal): refactor base client internals --- src/Core/BaseClient.php | 88 +++++++++++++++++++++++++++++----- src/Core/Concerns/SdkModel.php | 2 +- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/src/Core/BaseClient.php b/src/Core/BaseClient.php index 6eb8b01..cae68a4 100644 --- a/src/Core/BaseClient.php +++ b/src/Core/BaseClient.php @@ -8,8 +8,10 @@ use CasParser\Core\Contracts\BaseStream; use CasParser\Core\Conversion\Contracts\Converter; use CasParser\Core\Conversion\Contracts\ConverterSource; +use CasParser\Core\Exceptions\APIConnectionException; use CasParser\Core\Exceptions\APIStatusException; use CasParser\RequestOptions; +use Psr\Http\Client\ClientExceptionInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestFactoryInterface; use Psr\Http\Message\RequestInterface; @@ -41,7 +43,7 @@ public function __construct( string $baseUrl, protected RequestOptions $options = new RequestOptions, ) { - assert(null !== $this->options->uriFactory); + assert(!is_null($this->options->uriFactory)); $this->baseUrl = $this->options->uriFactory->createUri($baseUrl); } @@ -67,7 +69,7 @@ public function request( // @phpstan-ignore-next-line [$req, $opts] = $this->buildRequest(method: $method, path: $path, query: $query, headers: $headers, body: $body, opts: $options); ['method' => $method, 'path' => $uri, 'headers' => $headers] = $req; - assert(null !== $opts->requestFactory); + assert(!is_null($opts->requestFactory)); $request = $opts->requestFactory->createRequest($method, uri: $uri); $request = Util::withSetHeaders($request, headers: $headers); @@ -170,7 +172,7 @@ protected function followRedirect( ): RequestInterface { $location = $rsp->getHeaderLine('Location'); if (!$location) { - throw new \RuntimeException('Redirection without Location header'); + throw new APIConnectionException($req, message: 'Redirection without Location header'); } $uri = Util::joinUri($req->getUri(), path: $location); @@ -178,6 +180,55 @@ protected function followRedirect( return $req->withUri($uri); } + /** + * @internal + */ + protected function shouldRetry( + RequestOptions $opts, + int $retryCount, + ?ResponseInterface $rsp + ): bool { + if ($retryCount >= $opts->maxRetries) { + return false; + } + + $code = $rsp?->getStatusCode(); + if (408 == $code || 409 == $code || 429 == $code || $code >= 500) { + return true; + } + + return false; + } + + /** + * @internal + */ + protected function retryDelay( + RequestOptions $opts, + int $retryCount, + ?ResponseInterface $rsp + ): float { + if (!empty($header = $rsp?->getHeaderLine('retry-after'))) { + if (is_numeric($header)) { + return floatval($header); + } + + try { + $date = new \DateTimeImmutable($header); + $span = time() - $date->getTimestamp(); + + return max(0.0, $span); + } catch (\DateMalformedStringException) { + } + } + + $scale = $retryCount ** 2; + $jitter = 1 - (0.25 * mt_rand() / mt_getrandmax()); + $naive = $opts->initialRetryDelay * $scale * $jitter; + + return max(0.0, min($naive, $opts->maxRetryDelay)); + } + /** * @internal * @@ -194,12 +245,23 @@ protected function sendRequest( assert(null !== $opts->streamFactory && null !== $opts->transporter); $req = Util::withSetBody($opts->streamFactory, req: $req, body: $data); - $rsp = $opts->transporter->sendRequest($req); - $code = $rsp->getStatusCode(); + + $rsp = null; + $err = null; + + try { + $rsp = $opts->transporter->sendRequest($req); + } catch (ClientExceptionInterface $e) { + $err = $e; + } + + $code = $rsp?->getStatusCode(); if ($code >= 300 && $code < 400) { + assert(!is_null($rsp)); + if ($redirectCount >= 20) { - throw new \RuntimeException('Maximum redirects exceeded'); + throw new APIConnectionException($req, message: 'Maximum redirects exceeded'); } $req = $this->followRedirect($rsp, req: $req); @@ -207,12 +269,16 @@ protected function sendRequest( return $this->sendRequest($opts, req: $req, data: $data, retryCount: $retryCount, redirectCount: ++$redirectCount); } - if ($code >= 400 && $code < 500) { - throw APIStatusException::from(request: $req, response: $rsp); - } + if ($code >= 400 || is_null($rsp)) { + if ($this->shouldRetry($opts, retryCount: $retryCount, rsp: $rsp)) { + $exn = is_null($rsp) ? new APIConnectionException($req, previous: $err) : APIStatusException::from(request: $req, response: $rsp); + + throw $exn; + } - if ($code >= 500 && $retryCount < $opts->maxRetries) { - usleep((int) $opts->initialRetryDelay); + $seconds = $this->retryDelay($opts, retryCount: $redirectCount, rsp: $rsp); + $floor = floor($seconds); + time_nanosleep((int) $floor, nanoseconds: (int) ($seconds - $floor) * 10 ** 9); return $this->sendRequest($opts, req: $req, data: $data, retryCount: ++$retryCount, redirectCount: $redirectCount); } diff --git a/src/Core/Concerns/SdkModel.php b/src/Core/Concerns/SdkModel.php index 0e94205..27f095b 100644 --- a/src/Core/Concerns/SdkModel.php +++ b/src/Core/Concerns/SdkModel.php @@ -125,7 +125,7 @@ public function offsetExists(mixed $offset): bool return true; } - $property = self::$converter->properties[$offset]->property ?? new \ReflectionProperty($this, property: $offset); + $property = self::$converter->properties[$offset]->property; return $property->isInitialized($this); } From dd7855a47b1e8591ae9417d25804781e2f243c7f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 02:50:18 +0000 Subject: [PATCH 29/29] release: 0.1.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 51 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c7159c1..3d2ac0b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.0.2" + ".": "0.1.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 334efb5..3a8e525 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,56 @@ # Changelog +## 0.1.0 (2025-09-03) + +Full Changelog: [v0.0.2...v0.1.0](https://github.com/CASParser/cas-parser-php/compare/v0.0.2...v0.1.0) + +### ⚠ BREAKING CHANGES + +* use builders for RequestOptions +* rename errors to exceptions +* pagination field rename, and basic streaming docs +* **refactor:** namespacing cleanup +* **refactor:** clean up pagination, errors, as well as request methods + +### Features + +* **client:** add streaming ([8a3649a](https://github.com/CASParser/cas-parser-php/commit/8a3649ac38d283238cb78f183d88388d2220350f)) +* **client:** improve error handling ([b8969bb](https://github.com/CASParser/cas-parser-php/commit/b8969bb06c1b5c4575ad2f0a25af16f2bb0f5c5f)) +* **client:** use named parameters in methods ([f977b9a](https://github.com/CASParser/cas-parser-php/commit/f977b9a00b4f72c4d0add8e637baf699339b3707)) +* ensure `->toArray()` benefits from structural typing ([16e7c95](https://github.com/CASParser/cas-parser-php/commit/16e7c9593a3216a1af73bd62b8c3d9561d2a05ad)) +* pagination field rename, and basic streaming docs ([961e540](https://github.com/CASParser/cas-parser-php/commit/961e54000c4f7e50b2c2bdbed82ac08b4862450f)) +* **php:** differentiate null and omit ([d5736cf](https://github.com/CASParser/cas-parser-php/commit/d5736cf656dd266165e262c6fe85a6bc0a12e5d9)) +* **php:** rename internal types ([d8b47a2](https://github.com/CASParser/cas-parser-php/commit/d8b47a27256c7429058623b48624698804778320)) +* **refactor:** clean up pagination, errors, as well as request methods ([26437b5](https://github.com/CASParser/cas-parser-php/commit/26437b521827b8cf5393feee1e8af173e18d7a22)) +* **refactor:** namespacing cleanup ([666f374](https://github.com/CASParser/cas-parser-php/commit/666f37473888855fcc884cb9d851d43ce62aa75f)) +* rename errors to exceptions ([92d9817](https://github.com/CASParser/cas-parser-php/commit/92d9817bf28718bfe5ecc6e61f6ed42abe4b0d4b)) +* use builders for RequestOptions ([7f3730e](https://github.com/CASParser/cas-parser-php/commit/7f3730ee1d2081c219209b4cf06a9568af6b2559)) + + +### Bug Fixes + +* add create release workflow ([11358ea](https://github.com/CASParser/cas-parser-php/commit/11358ea756b190e7c2f459311f409f7ec6ddd255)) +* basic pagination should work ([041e76c](https://github.com/CASParser/cas-parser-php/commit/041e76c9f5db339bd9834e609058b3b4edc865d3)) +* **client:** elide null named parameters ([47e2824](https://github.com/CASParser/cas-parser-php/commit/47e28244783dd47d03f095cf0015aa947c0db5b8)) +* minor bugs ([2e13643](https://github.com/CASParser/cas-parser-php/commit/2e13643e201a80bfb8d1ffbeea643133cb79701e)) +* remove inaccurate `license` field in composer.json ([86ba48b](https://github.com/CASParser/cas-parser-php/commit/86ba48b8fc66042665d86362545915504f04011e)) +* streaming internals ([39bcd29](https://github.com/CASParser/cas-parser-php/commit/39bcd29aa689ba5505428c732b63d07fc049c010)) + + +### Chores + +* add additional php doc tags ([4403319](https://github.com/CASParser/cas-parser-php/commit/44033196dc6699b911b64f473f95badc0f548c71)) +* improve model annotations ([b45934c](https://github.com/CASParser/cas-parser-php/commit/b45934c91c079bc8eff6a3cfb53e8adab8927034)) +* **internal:** refactor base client internals ([36dc0b6](https://github.com/CASParser/cas-parser-php/commit/36dc0b68f8706fe3bd5fcbb7f57c5487cbea2496)) +* **internal:** refactored internal codepaths ([b306fa4](https://github.com/CASParser/cas-parser-php/commit/b306fa456c045ddd061225257fd4490e04099f12)) +* intuitively order union types ([150660a](https://github.com/CASParser/cas-parser-php/commit/150660ae58f257d5faaa6f8cf7c4f9093e0bae2c)) +* readme improvements ([ac66540](https://github.com/CASParser/cas-parser-php/commit/ac665402fa3edc84c13de25248d0b901e54aaab5)) +* refactor request options ([f1b303a](https://github.com/CASParser/cas-parser-php/commit/f1b303add63f19005284bec65d3b5907d8f34372)) +* **refactor:** simplify base page interface ([95b27e8](https://github.com/CASParser/cas-parser-php/commit/95b27e82468e3026ab7c6adad0af449ef2dd9355)) +* remove `php-http/multipart-stream-builder` as a required dependency ([607f49a](https://github.com/CASParser/cas-parser-php/commit/607f49a7bfb66fc667c15ced3baaa556e1b689ab)) +* remove type aliases ([ff49892](https://github.com/CASParser/cas-parser-php/commit/ff4989246f4c361a99aa1240db0b7c956fa5161d)) +* simplify model initialization ([f38ec08](https://github.com/CASParser/cas-parser-php/commit/f38ec08395db6f5fcf1c8a8e2ccae49cd0e4537a)) + ## 0.0.2 (2025-08-18) Full Changelog: [v0.0.1...v0.0.2](https://github.com/CASParser/cas-parser-php/compare/v0.0.1...v0.0.2)