Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
29 changes: 20 additions & 9 deletions src/CustomSleepMixin.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
39 changes: 39 additions & 0 deletions src/Http/CancellationToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace Mindee\Http;

/**
* Custom Mindee HTTP cancellation token for polling.
*/
class CancellationToken
{
private bool $isCanceled = false;

/**
* Flags the token as canceled.
*/
public function cancel(): void
{
$this->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;
}
}
36 changes: 36 additions & 0 deletions src/Http/CurlSslConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace Mindee\Http;

use Composer\CaBundle\CaBundle;
use CurlHandle;

/**
* Configures TLS certificate verification for cURL handles.
*/
class CurlSslConfig
{
/**
* Enables peer verification and points cURL at a valid CA bundle.
*
* Uses the CA bundle detected by composer/ca-bundle (system store or the
* bundled Mozilla CA set as a fallback), so verification works reliably
* across OSes and PHP builds that ship without a configured CA file.
*
* @param CurlHandle $ch cURL handle to configure.
*/
public static function apply(CurlHandle $ch): void
{
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);

$caPathOrFile = CaBundle::getSystemCaRootBundlePath();
if (is_dir($caPathOrFile) || (is_link($caPathOrFile) && is_dir((string) readlink($caPathOrFile)))) {
curl_setopt($ch, CURLOPT_CAPATH, $caPathOrFile);
} else {
curl_setopt($ch, CURLOPT_CAINFO, $caPathOrFile);
}
}
}
7 changes: 5 additions & 2 deletions src/V1/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -349,14 +350,16 @@ 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(
string $predictionType,
InputSource $inputDoc,
?PredictMethodOptions $options = null,
?PollingOptions $asyncOptions = null,
?PageOptions $pageOptions = null
?PageOptions $pageOptions = null,
?CancellationToken $cancellationToken = null,
): AsyncPredictResponse {
if (null === $options) {
$options = new PredictMethodOptions();
Expand All @@ -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);

Expand Down
5 changes: 3 additions & 2 deletions src/V1/Http/BaseEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Mindee\V1\Http;

use CurlHandle;
use Mindee\Http\CurlSslConfig;

/**
* Abstract class for endpoints.
Expand Down Expand Up @@ -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 = [
Expand Down Expand Up @@ -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),
Expand Down
17 changes: 10 additions & 7 deletions src/V2/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand All @@ -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);

Expand All @@ -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++;
}
Expand Down
3 changes: 2 additions & 1 deletion src/V2/Http/MindeeApiV2.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Loading