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/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/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 @@ +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/V1/Http/BaseEndpoint.php b/src/V1/Http/BaseEndpoint.php index da2b61d6..570ff2d0 100644 --- a/src/V1/Http/BaseEndpoint.php +++ b/src/V1/Http/BaseEndpoint.php @@ -5,6 +5,7 @@ namespace Mindee\V1\Http; use CurlHandle; +use Mindee\Http\CurlSslConfig; /** * Abstract class for endpoints. @@ -38,7 +39,7 @@ protected function initCurlSessionGet(string $queueId): array curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->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/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++; } 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;