11
11
12
12
use RuntimeException ;
13
13
use Toolkit \Cli \Cli ;
14
+ use function array_merge ;
14
15
use function basename ;
15
16
use function error_get_last ;
16
17
use function fclose ;
17
18
use function file_put_contents ;
18
19
use function fopen ;
19
20
use function getcwd ;
21
+ use function getenv ;
20
22
use function is_resource ;
23
+ use function preg_replace ;
21
24
use function stream_context_create ;
22
25
use function stream_context_set_params ;
26
+ use function strpos ;
23
27
use function trim ;
24
28
use const STREAM_NOTIFY_AUTH_REQUIRED ;
25
29
use const STREAM_NOTIFY_AUTH_RESULT ;
@@ -41,7 +45,7 @@ final class Download
41
45
{
42
46
public const PROGRESS_TEXT = 'text ' ;
43
47
44
- public const PROGRESS_BAR = 'bar ' ;
48
+ public const PROGRESS_BAR = 'bar ' ;
45
49
46
50
/** @var string */
47
51
private $ url ;
@@ -55,6 +59,19 @@ final class Download
55
59
/** @var string */
56
60
private $ showType ;
57
61
62
+ /**
63
+ * @var bool
64
+ */
65
+ private $ debug = false ;
66
+
67
+ /**
68
+ * http context options
69
+ *
70
+ * @var array
71
+ * @link https://www.php.net/manual/en/context.http.php
72
+ */
73
+ private $ httpCtxOptions = [];
74
+
58
75
/**
59
76
* @param string $url
60
77
* @param string $saveAs
@@ -118,14 +135,15 @@ public function start(): self
118
135
$ this ->saveAs = $ save ;
119
136
}
120
137
121
- $ ctx = stream_context_create ();
138
+ $ ctx = $ this -> createStreamContext ();
122
139
123
140
// register stream notification callback
141
+ // https://www.php.net/manual/en/function.stream-notification-callback.php
124
142
stream_context_set_params ($ ctx , [
125
143
'notification ' => [$ this , 'progressShow ' ]
126
144
]);
127
145
128
- Cli::write ("Download: {$ this ->url }\n Save As: {$ save }\n" );
146
+ Cli::write ("Download: {$ this ->url }\n Save As: {$ save }\n" );
129
147
130
148
$ fp = fopen ($ this ->url , 'rb ' , false , $ ctx );
131
149
@@ -145,16 +163,71 @@ public function start(): self
145
163
return $ this ;
146
164
}
147
165
166
+ protected function createStreamContext ()
167
+ {
168
+ // https://www.php.net/manual/en/context.http.php
169
+ $ httpOpts = [
170
+ 'max_redirects ' => '15 ' ,
171
+ 'protocol_version ' => '1.1 ' ,
172
+ 'header ' => [
173
+ 'Connection: close ' , // on 'protocol_version' => '1.1'
174
+ ],
175
+ // 'follow_location' => '1',
176
+ // 'timeout' => 0,
177
+ // 'proxy' => 'tcp://my-proxy.localhost:3128',
178
+ ];
179
+
180
+ if ($ this ->httpCtxOptions ) {
181
+ $ httpOpts = array_merge ($ httpOpts , $ this ->httpCtxOptions );
182
+ }
183
+
184
+ $ isHttps = strpos ($ this ->url , 'https ' ) === 0 ;
185
+
186
+ if (!isset ($ httpOpts ['proxy ' ])) {
187
+ if ($ isHttps ) {
188
+ $ proxyUrl = (string )getenv ('https_proxy ' );
189
+ } else {
190
+ $ proxyUrl = (string )getenv ('http_proxy ' );
191
+ }
192
+
193
+ if ($ proxyUrl ) {
194
+ $ this ->debugf ('Uses proxy ENV variable: http%s_proxy=%s ' , $ isHttps ? 's ' : '' , $ proxyUrl );
195
+
196
+ // convert 'http://127.0.0.1:10801' to 'tcp://127.0.0.1:10801'
197
+ // see https://github.com/guzzle/guzzle/issues/1555#issuecomment-239450114
198
+ if (strpos ($ proxyUrl , 'http ' ) === 0 ) {
199
+ $ proxyUrl = preg_replace ('/^http[s]?/ ' , 'tcp ' , $ proxyUrl );
200
+ }
201
+
202
+ $ httpOpts ['proxy ' ] = $ proxyUrl ;
203
+ // see https://www.php.net/manual/en/context.http.php#110449
204
+ $ httpOpts ['request_fulluri ' ] = true ;
205
+ }
206
+ }
207
+
208
+ return stream_context_create ([
209
+ 'http ' => $ httpOpts ,
210
+ ]);
211
+ }
212
+
148
213
/**
149
- * @param int $notifyCode stream notify code
150
- * @param int $severity severity code
151
- * @param string $message Message text
152
- * @param int $messageCode Message code
153
- * @param int $transferredBytes Have been transferred bytes
154
- * @param int $maxBytes Target max length bytes
214
+ * @link https://www.php.net/manual/en/function.stream-notification-callback.php
215
+ *
216
+ * @param int $notifyCode stream notify code
217
+ * @param int $severity severity code
218
+ * @param string|null $message Message text
219
+ * @param int $messageCode Message code
220
+ * @param int $transferredBytes Have been transferred bytes
221
+ * @param int $maxBytes Target max length bytes
155
222
*/
156
- public function progressShow (int $ notifyCode , $ severity , string $ message , $ messageCode , int $ transferredBytes , int $ maxBytes ): void
157
- {
223
+ public function progressShow (
224
+ int $ notifyCode ,
225
+ int $ severity ,
226
+ ?string $ message ,
227
+ $ messageCode ,
228
+ int $ transferredBytes ,
229
+ int $ maxBytes
230
+ ): void {
158
231
$ msg = '' ;
159
232
160
233
switch ($ notifyCode ) {
@@ -172,7 +245,7 @@ public function progressShow(int $notifyCode, $severity, string $message, $messa
172
245
break ;
173
246
174
247
case STREAM_NOTIFY_CONNECT :
175
- $ msg = 'Connected ... ' ;
248
+ $ msg = '> Connected ... ' ;
176
249
break ;
177
250
178
251
case STREAM_NOTIFY_FILE_SIZE_IS :
@@ -226,6 +299,17 @@ public function showProgressByType($transferredBytes): string
226
299
return '' ;
227
300
}
228
301
302
+ /**
303
+ * @param string $format
304
+ * @param mixed ...$args
305
+ */
306
+ public function debugf (string $ format , ...$ args ): void
307
+ {
308
+ if ($ this ->debug ) {
309
+ Cli::printf ("[DEBUG] $ format \n" , ...$ args );
310
+ }
311
+ }
312
+
229
313
/**
230
314
* @return string
231
315
*/
@@ -273,4 +357,20 @@ public function setSaveAs(string $saveAs): void
273
357
{
274
358
$ this ->saveAs = trim ($ saveAs );
275
359
}
360
+
361
+ /**
362
+ * @param bool $debug
363
+ */
364
+ public function setDebug (bool $ debug ): void
365
+ {
366
+ $ this ->debug = $ debug ;
367
+ }
368
+
369
+ /**
370
+ * @param array $httpCtxOptions
371
+ */
372
+ public function setHttpCtxOptions (array $ httpCtxOptions ): void
373
+ {
374
+ $ this ->httpCtxOptions = $ httpCtxOptions ;
375
+ }
276
376
}
0 commit comments