@@ -37,45 +37,15 @@ final class CurlClientState extends ClientState
3737
3838 public static array $ curlVersion ;
3939
40- public function __construct (int $ maxHostConnections , int $ maxPendingPushes )
41- {
40+ public function __construct (
41+ private int $ maxHostConnections ,
42+ private int $ maxPendingPushes ,
43+ ) {
4244 self ::$ curlVersion ??= curl_version ();
43-
44- $ this ->handle = curl_multi_init ();
4545 $ this ->dnsCache = new DnsCache ();
46- $ this ->reset ();
4746
48- // Don't enable HTTP/1.1 pipelining: it forces responses to be sent in order
49- if (\defined ('CURLPIPE_MULTIPLEX ' )) {
50- curl_multi_setopt ($ this ->handle , \CURLMOPT_PIPELINING , \CURLPIPE_MULTIPLEX );
51- }
52- if (\defined ('CURLMOPT_MAX_HOST_CONNECTIONS ' ) && 0 < $ maxHostConnections ) {
53- $ maxHostConnections = curl_multi_setopt ($ this ->handle , \CURLMOPT_MAX_HOST_CONNECTIONS , $ maxHostConnections ) ? min (50 * $ maxHostConnections , 4294967295 ) : $ maxHostConnections ;
54- }
55- if (\defined ('CURLMOPT_MAXCONNECTS ' ) && 0 < $ maxHostConnections ) {
56- curl_multi_setopt ($ this ->handle , \CURLMOPT_MAXCONNECTS , $ maxHostConnections );
57- }
58-
59- // Skip configuring HTTP/2 push when it's unsupported or buggy, see https://bugs.php.net/77535
60- if (0 >= $ maxPendingPushes ) {
61- return ;
62- }
63-
64- // HTTP/2 push crashes before curl 7.61
65- if (!\defined ('CURLMOPT_PUSHFUNCTION ' ) || 0x073D00 > self ::$ curlVersion ['version_number ' ] || !(\CURL_VERSION_HTTP2 & self ::$ curlVersion ['features ' ])) {
66- return ;
67- }
68-
69- // Clone to prevent a circular reference
70- $ multi = clone $ this ;
71- $ multi ->handle = null ;
72- $ multi ->share = null ;
73- $ multi ->pushedResponses = &$ this ->pushedResponses ;
74- $ multi ->logger = &$ this ->logger ;
75- $ multi ->handlesActivity = &$ this ->handlesActivity ;
76- $ multi ->openHandles = &$ this ->openHandles ;
77-
78- curl_multi_setopt ($ this ->handle , \CURLMOPT_PUSHFUNCTION , static fn ($ parent , $ pushed , array $ requestHeaders ) => $ multi ->handlePush ($ parent , $ pushed , $ requestHeaders , $ maxPendingPushes ));
47+ // handle and share are initialized lazily in __get()
48+ unset($ this ->handle , $ this ->share );
7949 }
8050
8151 public function reset (): void
@@ -89,17 +59,59 @@ public function reset(): void
8959 $ this ->dnsCache ->evictions = $ this ->dnsCache ->evictions ?: $ this ->dnsCache ->removals ;
9060 $ this ->dnsCache ->removals = $ this ->dnsCache ->hostnames = [];
9161
92- $ this ->share = curl_share_init ();
62+ unset($ this ->share );
63+ }
64+
65+ public function __get (string $ name ): mixed
66+ {
67+ if ('share ' === $ name ) {
68+ $ this ->share = curl_share_init ();
69+
70+ curl_share_setopt ($ this ->share , \CURLSHOPT_SHARE , \CURL_LOCK_DATA_DNS );
71+ curl_share_setopt ($ this ->share , \CURLSHOPT_SHARE , \CURL_LOCK_DATA_SSL_SESSION );
9372
94- curl_share_setopt ($ this ->share , \CURLSHOPT_SHARE , \CURL_LOCK_DATA_DNS );
95- curl_share_setopt ($ this ->share , \CURLSHOPT_SHARE , \CURL_LOCK_DATA_SSL_SESSION );
73+ if (\defined ('CURL_LOCK_DATA_CONNECT ' )) {
74+ curl_share_setopt ($ this ->share , \CURLSHOPT_SHARE , \CURL_LOCK_DATA_CONNECT );
75+ }
9676
97- if (\defined ('CURL_LOCK_DATA_CONNECT ' )) {
98- curl_share_setopt ($ this ->share , \CURLSHOPT_SHARE , \CURL_LOCK_DATA_CONNECT );
77+ return $ this ->share ;
9978 }
79+
80+ if ('handle ' === $ name ) {
81+ $ this ->handle = curl_multi_init ();
82+
83+ // Don't enable HTTP/1.1 pipelining: it forces responses to be sent in order
84+ if (\defined ('CURLPIPE_MULTIPLEX ' )) {
85+ curl_multi_setopt ($ this ->handle , \CURLMOPT_PIPELINING , \CURLPIPE_MULTIPLEX );
86+ }
87+ if (\defined ('CURLMOPT_MAX_HOST_CONNECTIONS ' ) && 0 < $ this ->maxHostConnections ) {
88+ $ this ->maxHostConnections = curl_multi_setopt ($ this ->handle , \CURLMOPT_MAX_HOST_CONNECTIONS , $ this ->maxHostConnections ) ? min (50 * $ this ->maxHostConnections , 4294967295 ) : $ this ->maxHostConnections ;
89+ }
90+ if (\defined ('CURLMOPT_MAXCONNECTS ' ) && 0 < $ this ->maxHostConnections ) {
91+ curl_multi_setopt ($ this ->handle , \CURLMOPT_MAXCONNECTS , $ this ->maxHostConnections );
92+ }
93+
94+ // Skip configuring HTTP/2 push when it's unsupported or buggy, see https://bugs.php.net/77535
95+ if (0 < $ this ->maxPendingPushes && (\defined ('CURLMOPT_PUSHFUNCTION ' ) && 0x073D00 <= self ::$ curlVersion ['version_number ' ] && (\CURL_VERSION_HTTP2 & self ::$ curlVersion ['features ' ]))) {
96+ // Clone to prevent a circular reference
97+ $ multi = clone $ this ;
98+ $ multi ->handle = null ;
99+ $ multi ->share = null ;
100+ $ multi ->pushedResponses = &$ this ->pushedResponses ;
101+ $ multi ->logger = &$ this ->logger ;
102+ $ multi ->handlesActivity = &$ this ->handlesActivity ;
103+ $ multi ->openHandles = &$ this ->openHandles ;
104+
105+ curl_multi_setopt ($ this ->handle , \CURLMOPT_PUSHFUNCTION , $ multi ->handlePush (...));
106+ }
107+
108+ return $ this ->handle ;
109+ }
110+
111+ throw new \LogicException (\sprintf ('Unknown property "%s" on "%s". ' , $ name , self ::class));
100112 }
101113
102- private function handlePush ($ parent , $ pushed , array $ requestHeaders, int $ maxPendingPushes ): int
114+ private function handlePush ($ parent , $ pushed , array $ requestHeaders ): int
103115 {
104116 $ headers = [];
105117 $ origin = curl_getinfo ($ parent , \CURLINFO_EFFECTIVE_URL );
@@ -127,7 +139,7 @@ private function handlePush($parent, $pushed, array $requestHeaders, int $maxPen
127139 return \CURL_PUSH_DENY ;
128140 }
129141
130- if ($ maxPendingPushes <= \count ($ this ->pushedResponses )) {
142+ if ($ this -> maxPendingPushes <= \count ($ this ->pushedResponses )) {
131143 $ fifoUrl = key ($ this ->pushedResponses );
132144 unset($ this ->pushedResponses [$ fifoUrl ]);
133145 $ this ->logger ?->debug(\sprintf ('Evicting oldest pushed response: "%s" ' , $ fifoUrl ));
0 commit comments