From b86d81d10d7b7ff565b446071d54e37ddde5818f Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 16 Jul 2026 11:42:50 +0200 Subject: [PATCH 1/2] :sparkles: add support for cancellation token in polling --- src/CustomSleepMixin.php | 29 +++++++++++++++++-------- src/Http/CancellationToken.php | 39 ++++++++++++++++++++++++++++++++++ src/V1/Client.php | 7 ++++-- src/V2/Client.php | 17 +++++++++------ 4 files changed, 74 insertions(+), 18 deletions(-) create mode 100644 src/Http/CancellationToken.php diff --git a/src/CustomSleepMixin.php b/src/CustomSleepMixin.php index bb4203c8..8ef468a2 100644 --- a/src/CustomSleepMixin.php +++ b/src/CustomSleepMixin.php @@ -4,26 +4,37 @@ namespace Mindee; +use Mindee\Error\MindeeException; +use Mindee\Http\CancellationToken; + trait CustomSleepMixin { /** * Waits for a custom amount of time from either a float or an integer. - * Purposefully waits for one more millisecond on windows due to flakiness in delays between OS. + * Purposefully waits for one more millisecond on Windows due to flakiness in delays between OS. * @param float|integer $delay Delay in seconds. + * @param CancellationToken|null $cancellationToken CancellationToken to check for cancellation. */ - protected static function customSleep(float|int $delay): void + protected static function customSleep(float|int $delay, ?CancellationToken $cancellationToken = null): void { if ($delay <= 0) { return; } - - $seconds = (int) $delay; - $nanoseconds = abs($seconds - (float) $delay); - if ( - strtoupper(substr(PHP_OS_FAMILY, 0, 7)) === 'WINDOWS' - ) { + $endTime = microtime(true) + $delay; + $pollIntervalMicroseconds = 100_000; + while (microtime(true) < $endTime) { + if ($cancellationToken && $cancellationToken->isCancelled()) { + throw new MindeeException("Polling operation was cancelled."); + } + $remainingSeconds = $endTime - microtime(true); + if ($remainingSeconds <= 0) { + break; + } + $sleepMicroseconds = (int) min($pollIntervalMicroseconds, $remainingSeconds * 1_000_000); + usleep($sleepMicroseconds); + } + if (PHP_OS_FAMILY === 'Windows') { usleep(1000); } - time_nanosleep($seconds, (int) ($nanoseconds * 1_000_000_000)); } } diff --git a/src/Http/CancellationToken.php b/src/Http/CancellationToken.php new file mode 100644 index 00000000..cb41ff3c --- /dev/null +++ b/src/Http/CancellationToken.php @@ -0,0 +1,39 @@ +isCanceled = true; + } + + /** + * Checks whether the token is canceled. + * @return boolean whether the token is canceled. + */ + public function isCanceled(): bool + { + return $this->isCanceled; + } + + /** + * Checks whether the token is canceled, but in British. + * @return boolean whether the token is canceled. + */ + public function isCancelled(): bool + { + return $this->isCanceled; + } +} diff --git a/src/V1/Client.php b/src/V1/Client.php index a5fdc433..b2e81df2 100644 --- a/src/V1/Client.php +++ b/src/V1/Client.php @@ -16,6 +16,7 @@ use Mindee\Error\ErrorCode; use Mindee\Error\MindeeApiException; use Mindee\Error\MindeeException; +use Mindee\Http\CancellationToken; use Mindee\Input\InputSource; use Mindee\Input\LocalInputSource; use Mindee\Input\LocalResponse; @@ -349,6 +350,7 @@ public function parse( * @param PredictMethodOptions|null $options Prediction Options. * @param PollingOptions|null $asyncOptions Async Options. Manages timers. * @param PageOptions|null $pageOptions Options to apply to the PDF file. + * @param CancellationToken|null $cancellationToken CancellationToken to check for cancellation. * @throws MindeeApiException Throws if the document couldn't be retrieved in time. */ public function enqueueAndParse( @@ -356,7 +358,8 @@ public function enqueueAndParse( InputSource $inputDoc, ?PredictMethodOptions $options = null, ?PollingOptions $asyncOptions = null, - ?PageOptions $pageOptions = null + ?PageOptions $pageOptions = null, + ?CancellationToken $cancellationToken = null, ): AsyncPredictResponse { if (null === $options) { $options = new PredictMethodOptions(); @@ -377,7 +380,7 @@ public function enqueueAndParse( ); error_log("Successfully enqueued document with job id: " . $enqueueResponse->job->id); - $this->customSleep($asyncOptions->initialDelaySec); + $this->customSleep($asyncOptions->initialDelaySec, $cancellationToken); $retryCounter = 1; $pollResults = $this->parseQueued($predictionType, $enqueueResponse->job->id, $options->endpoint); diff --git a/src/V2/Client.php b/src/V2/Client.php index 25de2e47..9997a827 100644 --- a/src/V2/Client.php +++ b/src/V2/Client.php @@ -7,6 +7,7 @@ use Mindee\ClientOptions\PollingOptions; use Mindee\CustomSleepMixin; use Mindee\Error\MindeeException; +use Mindee\Http\CancellationToken; use Mindee\Input\InputSource; use Mindee\V2\ClientOptions\BaseParameters; use Mindee\V2\Http\MindeeApiV2; @@ -45,7 +46,7 @@ public function __construct(?string $apiKey = null) * @category Asynchronous */ public function enqueue( - InputSource $inputSource, + InputSource $inputSource, BaseParameters $params ): JobResponse { return $this->mindeeApi->reqPostEnqueue($inputSource, $params); @@ -103,14 +104,16 @@ public function getJob(string $jobId): JobResponse * @param InputSource $inputDoc Input document to parse. * @param BaseParameters $params Parameters relating to prediction options. * @param PollingOptions|null $pollingOptions Options to apply to the polling. + * @param CancellationToken|null $cancellationToken CancellationToken to check for cancellation. * @return BaseResponse A response containing parsing results. * @throws MindeeException Throws if enqueueing fails, job fails, or times out. */ public function enqueueAndGetResult( - string $responseClass, - InputSource $inputDoc, - BaseParameters $params, - ?PollingOptions $pollingOptions = null + string $responseClass, + InputSource $inputDoc, + BaseParameters $params, + ?PollingOptions $pollingOptions = null, + ?CancellationToken $cancellationToken = null ): BaseResponse { if (!$pollingOptions) { $pollingOptions = new PollingOptions(); @@ -126,7 +129,7 @@ public function enqueueAndGetResult( $jobId = $enqueueResponse->job->id; error_log("Successfully enqueued document with job ID: " . $jobId); - $this->customSleep($pollingOptions->initialDelaySec); + $this->customSleep($pollingOptions->initialDelaySec, $cancellationToken); $retryCounter = 1; $pollResults = $this->getJob($jobId); @@ -144,7 +147,7 @@ public function enqueueAndGetResult( . ". Job status: " . $pollResults->job->status ); - $this->customSleep($pollingOptions->delaySec); + $this->customSleep($pollingOptions->delaySec, $cancellationToken); $pollResults = $this->getJob($jobId); $retryCounter++; } From edeca7f316cdb3df892b65d60d9ffaa49b263f28 Mon Sep 17 00:00:00 2001 From: sebastianMindee <130448732+sebastianMindee@users.noreply.github.com> Date: Thu, 16 Jul 2026 11:52:10 +0200 Subject: [PATCH 2/2] :sparkles: add support for more secure HTTP calls through CA bundle --- composer.json | 3 ++- src/Http/CurlSslConfig.php | 36 ++++++++++++++++++++++++++++++++++++ src/V1/Http/BaseEndpoint.php | 5 +++-- src/V2/Http/MindeeApiV2.php | 3 ++- 4 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 src/Http/CurlSslConfig.php diff --git a/composer.json b/composer.json index 2c38a14b..615b69a5 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,8 @@ "symfony/console": ">=6.0", "setasign/fpdf": "^1.8", "setasign/fpdi": "^2.6.4", - "smalot/pdfparser": "^2.12" + "smalot/pdfparser": "^2.12", + "composer/ca-bundle": "^1.5" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.38", diff --git a/src/Http/CurlSslConfig.php b/src/Http/CurlSslConfig.php new file mode 100644 index 00000000..64d17f36 --- /dev/null +++ b/src/Http/CurlSslConfig.php @@ -0,0 +1,36 @@ +settings->requestTimeout); curl_setopt($ch, CURLOPT_HTTPGET, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + CurlSslConfig::apply($ch); curl_setopt($ch, CURLOPT_USERAGENT, getUserAgent()); $resp = [ @@ -72,7 +73,7 @@ public function setFinalCurlOpts( if ($postFields !== null) { curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); } - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + CurlSslConfig::apply($ch); curl_setopt($ch, CURLOPT_USERAGENT, getUserAgent()); $resp = [ 'data' => curl_exec($ch), diff --git a/src/V2/Http/MindeeApiV2.php b/src/V2/Http/MindeeApiV2.php index b1efefc4..6835c1b9 100644 --- a/src/V2/Http/MindeeApiV2.php +++ b/src/V2/Http/MindeeApiV2.php @@ -13,6 +13,7 @@ use Mindee\Error\ErrorCode; use Mindee\Error\MindeeApiException; use Mindee\Error\MindeeException; +use Mindee\Http\CurlSslConfig; use Mindee\Input\InputSource; use Mindee\Input\LocalInputSource; use Mindee\Input\UrlInputSource; @@ -306,7 +307,7 @@ private function initChannel(): bool|CurlHandle curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->requestTimeout); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + CurlSslConfig::apply($ch); curl_setopt($ch, CURLOPT_USERAGENT, $this->getUserAgent()); return $ch;