Skip to content

Commit f7aa895

Browse files
committed
Merge branch 'curler-handle-fix'
2 parents 560e084 + a2e3750 commit f7aa895

File tree

3 files changed

+85
-45
lines changed

3 files changed

+85
-45
lines changed

src/Curler/Curler.php

Lines changed: 79 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -384,12 +384,12 @@ final class Curler implements IReadable, IWritable, ProvidesBuilder
384384
/**
385385
* @var \CurlHandle|resource|null
386386
*/
387-
private static $Handle;
387+
private $Handle;
388388

389389
/**
390390
* @var bool|null
391391
*/
392-
private static $HandleIsReset;
392+
private $HandleIsReset;
393393

394394
/**
395395
* @var int
@@ -406,6 +406,21 @@ final class Curler implements IReadable, IWritable, ProvidesBuilder
406406
*/
407407
private static $DefaultUserAgent;
408408

409+
/**
410+
* @var \CurlHandle|resource|null
411+
*/
412+
private static $SharedHandle;
413+
414+
/**
415+
* @var bool|null
416+
*/
417+
private static $SharedHandleIsReset;
418+
419+
/**
420+
* @var int
421+
*/
422+
private static $HandleCount = 0;
423+
409424
/**
410425
* @param (callable(Curler): string[])|null $responseCacheKeyCallback
411426
* @param (callable(Curler): Curler)|null $responseCallback
@@ -588,29 +603,29 @@ public function responseContentTypeIs(string $mimeType): bool
588603

589604
protected function getEffectiveUrl(): ?string
590605
{
591-
return self::$Handle
592-
? curl_getinfo(self::$Handle, CURLINFO_EFFECTIVE_URL)
606+
return $this->Handle
607+
? curl_getinfo($this->Handle, CURLINFO_EFFECTIVE_URL)
593608
: null;
594609
}
595610

596611
protected function close(): void
597612
{
598-
if (!self::$Handle) {
613+
if (!$this->Handle) {
599614
return;
600615
}
601616

602617
if ($this->CookieKey && $this->ExecuteCount) {
603-
Cache::set($this->CookieKey, curl_getinfo(self::$Handle, CURLINFO_COOKIELIST));
618+
Cache::set($this->CookieKey, curl_getinfo($this->Handle, CURLINFO_COOKIELIST));
604619
}
605620

606-
curl_reset(self::$Handle);
607-
self::$HandleIsReset = true;
621+
curl_reset($this->Handle);
622+
$this->HandleIsReset = true;
608623
}
609624

610625
/**
611626
* @param mixed[]|null $query
612627
*/
613-
private function initialise(string $method, ?array $query, ?ICurlerPager $pager = null): void
628+
private function initialise(string $method, ?array $query, ?ICurlerPager $pager = null, bool $async = false): void
614629
{
615630
$this->ExecuteCount = 0;
616631

@@ -619,69 +634,83 @@ private function initialise(string $method, ?array $query, ?ICurlerPager $pager
619634
}
620635
$this->QueryString = $this->getQueryString($query);
621636

622-
if (self::$Handle) {
623-
if (!self::$HandleIsReset) {
624-
curl_reset(self::$Handle);
637+
if ($async) {
638+
if ($this->Handle && $this->Handle === self::$SharedHandle) {
639+
$handle = null;
640+
$handleIsReset = null;
641+
$this->Handle = &$handle;
642+
$this->HandleIsReset = &$handleIsReset;
625643
}
626-
self::$HandleIsReset = false;
627-
curl_setopt(self::$Handle, CURLOPT_URL, $this->BaseUrl . $this->QueryString);
628644
} else {
629-
self::$Handle = curl_init($this->BaseUrl . $this->QueryString);
645+
$this->maybeDiscardHandle();
646+
$this->Handle = &self::$SharedHandle;
647+
$this->HandleIsReset = &self::$SharedHandleIsReset;
648+
}
649+
650+
if ($this->Handle) {
651+
if (!$this->HandleIsReset) {
652+
curl_reset($this->Handle);
653+
}
654+
$this->HandleIsReset = false;
655+
curl_setopt($this->Handle, CURLOPT_URL, $this->BaseUrl . $this->QueryString);
656+
} else {
657+
$this->Handle = curl_init($this->BaseUrl . $this->QueryString);
658+
self::$HandleCount++;
630659
}
631660

632661
// Return the transfer as a string
633-
curl_setopt(self::$Handle, CURLOPT_RETURNTRANSFER, true);
662+
curl_setopt($this->Handle, CURLOPT_RETURNTRANSFER, true);
634663

635664
// Enable all supported encoding types (e.g. gzip, deflate) and set
636665
// Accept-Encoding header accordingly
637-
curl_setopt(self::$Handle, CURLOPT_ENCODING, '');
666+
curl_setopt($this->Handle, CURLOPT_ENCODING, '');
638667

639668
// Collect response headers
640669
curl_setopt(
641-
self::$Handle,
670+
$this->Handle,
642671
CURLOPT_HEADERFUNCTION,
643672
fn($curl, $header) => strlen($this->processHeader($header))
644673
);
645674

646675
if ($this->ConnectTimeout !== null) {
647-
curl_setopt(self::$Handle, CURLOPT_CONNECTTIMEOUT, $this->ConnectTimeout);
676+
curl_setopt($this->Handle, CURLOPT_CONNECTTIMEOUT, $this->ConnectTimeout);
648677
}
649678

650679
if ($this->Timeout !== null) {
651-
curl_setopt(self::$Handle, CURLOPT_TIMEOUT, $this->Timeout);
680+
curl_setopt($this->Handle, CURLOPT_TIMEOUT, $this->Timeout);
652681
}
653682

654683
if ($this->FollowRedirects) {
655-
curl_setopt(self::$Handle, CURLOPT_FOLLOWLOCATION, true);
684+
curl_setopt($this->Handle, CURLOPT_FOLLOWLOCATION, true);
656685
if ($this->MaxRedirects !== null) {
657-
curl_setopt(self::$Handle, CURLOPT_MAXREDIRS, $this->MaxRedirects);
686+
curl_setopt($this->Handle, CURLOPT_MAXREDIRS, $this->MaxRedirects);
658687
}
659688
}
660689

661690
if ($this->CookieKey = $this->getCookieKey()) {
662691
// Enable cookies without loading them from a file
663-
curl_setopt(self::$Handle, CURLOPT_COOKIEFILE, '');
692+
curl_setopt($this->Handle, CURLOPT_COOKIEFILE, '');
664693

665694
foreach (Cache::get($this->CookieKey) ?: [] as $cookie) {
666-
curl_setopt(self::$Handle, CURLOPT_COOKIELIST, $cookie);
695+
curl_setopt($this->Handle, CURLOPT_COOKIELIST, $cookie);
667696
}
668697
}
669698

670699
// In debug mode, collect request headers
671700
if (Env::debug()) {
672-
curl_setopt(self::$Handle, CURLINFO_HEADER_OUT, true);
701+
curl_setopt($this->Handle, CURLINFO_HEADER_OUT, true);
673702
}
674703

675704
switch ($method) {
676705
case HttpRequestMethod::GET:
677706
break;
678707

679708
case HttpRequestMethod::HEAD:
680-
curl_setopt(self::$Handle, CURLOPT_NOBODY, true);
709+
curl_setopt($this->Handle, CURLOPT_NOBODY, true);
681710
break;
682711

683712
case HttpRequestMethod::POST:
684-
curl_setopt(self::$Handle, CURLOPT_POST, true);
713+
curl_setopt($this->Handle, CURLOPT_POST, true);
685714
break;
686715

687716
case HttpRequestMethod::PUT:
@@ -690,7 +719,7 @@ private function initialise(string $method, ?array $query, ?ICurlerPager $pager
690719
case HttpRequestMethod::CONNECT:
691720
case HttpRequestMethod::OPTIONS:
692721
case HttpRequestMethod::TRACE:
693-
curl_setopt(self::$Handle, CURLOPT_CUSTOMREQUEST, $method);
722+
curl_setopt($this->Handle, CURLOPT_CUSTOMREQUEST, $method);
694723
break;
695724

696725
default:
@@ -782,7 +811,7 @@ private function clearResponse(): void
782811
private function applyData($data): void
783812
{
784813
curl_setopt(
785-
self::$Handle,
814+
$this->Handle,
786815
CURLOPT_POSTFIELDS,
787816
($this->Body = $this->prepareData($data))
788817
);
@@ -912,7 +941,7 @@ protected function execute(bool $close = true, int $depth = 0): string
912941

913942
$this->ExecuteCount++;
914943

915-
curl_setopt(self::$Handle, CURLOPT_HTTPHEADER, $this->Headers->getHeaders());
944+
curl_setopt($this->Handle, CURLOPT_HTTPHEADER, $this->Headers->getHeaders());
916945

917946
$attempt = 0;
918947
while ($attempt++ < 2) {
@@ -929,11 +958,11 @@ protected function execute(bool $close = true, int $depth = 0): string
929958
}
930959

931960
// Execute the request
932-
$result = curl_exec(self::$Handle);
933-
$this->CurlInfo = curl_getinfo(self::$Handle);
961+
$result = curl_exec($this->Handle);
962+
$this->CurlInfo = curl_getinfo($this->Handle);
934963

935964
if ($result === false) {
936-
$error = curl_errno(self::$Handle);
965+
$error = curl_errno($this->Handle);
937966
throw new CurlerCurlErrorException($error, $this);
938967
}
939968

@@ -1183,7 +1212,7 @@ private function process(string $method, ?array $query, $data = null, ?string $m
11831212
if (is_array($data) || is_object($data)) {
11841213
$this->applyData($data);
11851214
} elseif (is_string($data) && $mimeType) {
1186-
curl_setopt(self::$Handle, CURLOPT_POSTFIELDS, $data);
1215+
curl_setopt($this->Handle, CURLOPT_POSTFIELDS, $data);
11871216
$this->setContentType($mimeType);
11881217
}
11891218

@@ -1224,7 +1253,7 @@ private function paginate(string $method, ?array $query, $data = null): Generato
12241253
$data = $this->Pager->prepareData($data);
12251254
}
12261255

1227-
$this->initialise($method, $query, $this->Pager);
1256+
$this->initialise($method, $query, $this->Pager, true);
12281257

12291258
do {
12301259
if ($data !== null) {
@@ -1251,7 +1280,7 @@ private function paginate(string $method, ?array $query, $data = null): Generato
12511280
}
12521281
[$url, $data, $headers] =
12531282
[$page->nextUrl(), $page->nextData(), $page->nextHeaders()];
1254-
curl_setopt(self::$Handle, CURLOPT_URL, $url);
1283+
curl_setopt($this->Handle, CURLOPT_URL, $url);
12551284
if ($headers !== null) {
12561285
$this->Headers = $headers;
12571286
}
@@ -1361,7 +1390,7 @@ public function getAllLinked(?array $query = null): array
13611390

13621391
do {
13631392
if ($nextUrl) {
1364-
curl_setopt(self::$Handle, CURLOPT_URL, $nextUrl);
1393+
curl_setopt($this->Handle, CURLOPT_URL, $nextUrl);
13651394
$this->clearResponse();
13661395
$nextUrl = null;
13671396
}
@@ -1395,7 +1424,7 @@ public function getAllLinkedByEntity($entityName, ?array $query = null): array
13951424

13961425
do {
13971426
if ($nextUrl) {
1398-
curl_setopt(self::$Handle, CURLOPT_URL, $nextUrl);
1427+
curl_setopt($this->Handle, CURLOPT_URL, $nextUrl);
13991428
$this->clearResponse();
14001429
}
14011430

@@ -1545,4 +1574,16 @@ public static function getBuilder(): string
15451574
{
15461575
return CurlerBuilder::class;
15471576
}
1577+
1578+
public function __destruct()
1579+
{
1580+
$this->maybeDiscardHandle();
1581+
}
1582+
1583+
private function maybeDiscardHandle(): void
1584+
{
1585+
if ($this->Handle && $this->Handle !== self::$SharedHandle) {
1586+
self::$HandleCount--;
1587+
}
1588+
}
15481589
}

stubs/CurlHandle.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
if (PHP_VERSION_ID < 80000 && extension_loaded('curl')) {
4+
final class CurlHandle {}
5+
}

tests/phpstan-conditional.php

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,7 @@
99
'tests/fixtures/Utility/Reflect/MyClassWithUnionsAndIntersections.php',
1010
],
1111
],
12-
'ignoreErrors' => [
13-
[
14-
'message' => '#^Property Lkrms\\\\Curler\\\\Curler\:\:\$Handle has unknown class CurlHandle as its type\.$#',
15-
'count' => 1,
16-
'path' => '../src/Curler/Curler.php',
17-
],
18-
],
12+
'ignoreErrors' => [],
1913
]
2014
];
2115
}

0 commit comments

Comments
 (0)