11<?php
22
33namespace PHPWatch \PHPCommitBuilder ;
4+
45class DownloadLinkFetcher {
6+
7+ private const string RELEASES_JSON = 'https://downloads.php.net/~windows/releases/releases.json ' ;
8+ private const string RELEASES_QA_JSON = 'https://downloads.php.net/~windows/qa/releases.json ' ;
9+
10+ private ?array $ hashes = null ;
11+
512 public function getLinksForTag (string $ tag ): array {
613 $ return = [];
714
@@ -11,9 +18,11 @@ public function getLinksForTag(string $tag): array {
1118 foreach ($ urls as $ type => $ links ) {
1219 foreach ($ links as $ link ) {
1320 if (isset ($ urlStatus [$ link ])) {
21+ $ file = basename ($ link );
1422 $ return [$ type ] = [
1523 'url ' => $ link ,
1624 'size ' => $ urlStatus [$ link ],
25+ 'sha256 ' => isset ($ this ->hashes [$ file ]) ? $ this ->hashes [$ file ]['sha256 ' ] : null ,
1726 ];
1827 }
1928 }
@@ -22,49 +31,96 @@ public function getLinksForTag(string $tag): array {
2231 return $ return ;
2332 }
2433
25- private static function determineVCVersion (string $ tag ): string {
26- if (preg_match ('/^php-7\.2\./ ' , $ tag )) {
27- return 'VC15 ' ;
28- }
29- if (preg_match ('/^php-7\.3\./ ' , $ tag )) {
30- return 'VC15 ' ;
34+ private static function inspectTag (string $ tag ): array {
35+ $ info = [
36+ 'QA ' => false ,
37+ 'VS ' => 'vs17 ' ,
38+ 'lookupHash ' => false ,
39+ ];
40+
41+ if (preg_match ('/^php-[\d.]+0(alpha|beta|rc|RC)\d$/ ' , $ tag )) {
42+ $ info ['QA ' ] = true ;
43+ }
44+
45+ if (preg_match ('/^php-7\.2\./ ' , $ tag ) || preg_match ('/^php-7\.3\./ ' , $ tag )) {
46+ $ info ['VS ' ] = 'VC15 ' ;
47+ return $ info ;
3148 }
49+
3250 if (preg_match ('/^php-7\.4\./ ' , $ tag )) {
33- return 'vc15 ' ;
51+ $ info ['VS ' ] = 'vc15 ' ;
52+ return $ info ;
3453 }
54+
3555 if (preg_match ('/^php-8\.[0123]\./ ' , $ tag )) {
36- return 'vs16 ' ;
56+ $ info ['VS ' ] = 'vs16 ' ;
57+ $ info ['lookupHash ' ] = true ;
58+ return $ info ;
3759 }
38- return 'vs17 ' ;
60+
61+ return $ info ;
3962 }
4063
64+ private function ensureReleasesJson (): void {
65+ if ($ this ->hashes !== null ) {
66+ return ;
67+ }
68+
69+ $ hashes = [];
70+
71+ $ this ->ingestHashList (self ::RELEASES_JSON , $ hashes );
72+ $ this ->ingestHashList (self ::RELEASES_QA_JSON , $ hashes );
73+
74+ $ this ->hashes = $ hashes ;
75+ }
76+
77+ private function ingestHashList (string $ url , &$ hashes ): void {
78+ $ releasesJson = file_get_contents (self ::RELEASES_JSON );
79+ $ releases = json_decode ($ releasesJson , associative: true , depth: 10 , flags: JSON_THROW_ON_ERROR );
80+
81+ foreach ($ releases as $ version_ => $ files ) {
82+ foreach ($ files as $ file ) {
83+ if (!isset ($ file ['zip ' ])) {
84+ continue ;
85+ }
86+
87+ if (isset ($ file ['zip ' ]['sha256 ' ])) {
88+ $ hashes [$ file ['zip ' ]['path ' ]]['sha256 ' ] = $ file ['zip ' ]['sha256 ' ];
89+ }
90+ }
91+ }
92+ }
93+
4194 private function getWindowsLinks (string $ tag ): array {
4295 $ folder = 'releases/archives ' ;
4396 $ folder_alt = 'releases ' ;
4497
45- if (preg_match ('/^php-[\d.]+0(alpha|beta|rc|RC)\d$/ ' , $ tag )) {
46- $ folder = 'qa/archives ' ;
47- $ folder_alt = 'qa ' ;
48- }
98+ $ info = self ::inspectTag ($ tag );
99+ $ vsVersion = $ info ['VS ' ];
49100
50- $ vsVersion = self ::determineVCVersion ($ tag );
101+ if ($ info ['QA ' ]) {
102+ $ folder = 'qa/archives ' ;
103+ $ folder_alt = 'qa ' ;
104+ }
105+
106+ $ this ->ensureReleasesJson ();
51107
52108 return [
53109 'x64NTS ' => [
54- 'https://windows .php.net/downloads / ' . $ folder . '/ ' . $ tag . '-nts-Win32- ' . $ vsVersion .'-x64.zip ' ,
55- 'https://windows .php.net/downloads / ' . $ folder_alt . '/ ' . $ tag . '-nts-Win32- ' . $ vsVersion .'-x64.zip ' ,
110+ 'https://downloads .php.net/~windows / ' . $ folder . '/ ' . $ tag . '-nts-Win32- ' . $ vsVersion .'-x64.zip ' ,
111+ 'https://downloads .php.net/~windows / ' . $ folder_alt . '/ ' . $ tag . '-nts-Win32- ' . $ vsVersion .'-x64.zip ' ,
56112 ],
57113 'x64TS ' => [
58- 'https://windows .php.net/downloads / ' . $ folder . '/ ' . $ tag . '-Win32- ' . $ vsVersion . '-x64.zip ' ,
59- 'https://windows .php.net/downloads / ' . $ folder_alt . '/ ' . $ tag . '-Win32- ' . $ vsVersion . '-x64.zip ' ,
114+ 'https://downloads .php.net/~windows / ' . $ folder . '/ ' . $ tag . '-Win32- ' . $ vsVersion . '-x64.zip ' ,
115+ 'https://downloads .php.net/~windows / ' . $ folder_alt . '/ ' . $ tag . '-Win32- ' . $ vsVersion . '-x64.zip ' ,
60116 ],
61117 'x86NTS ' => [
62- 'https://windows .php.net/downloads / ' . $ folder . '/ ' . $ tag . '-nts-Win32- ' . $ vsVersion . '-x86.zip ' ,
63- 'https://windows .php.net/downloads / ' . $ folder_alt . '/ ' . $ tag . '-nts-Win32- ' . $ vsVersion . '-x86.zip ' ,
118+ 'https://downloads .php.net/~windows / ' . $ folder . '/ ' . $ tag . '-nts-Win32- ' . $ vsVersion . '-x86.zip ' ,
119+ 'https://downloads .php.net/~windows / ' . $ folder_alt . '/ ' . $ tag . '-nts-Win32- ' . $ vsVersion . '-x86.zip ' ,
64120 ],
65121 'x86TS ' => [
66- 'https://windows .php.net/downloads / ' . $ folder . '/ ' . $ tag . '-Win32- ' . $ vsVersion . '-x86.zip ' ,
67- 'https://windows .php.net/downloads / ' . $ folder_alt . '/ ' . $ tag . '-Win32- ' . $ vsVersion . '-x86.zip ' ,
122+ 'https://downloads .php.net/~windows / ' . $ folder . '/ ' . $ tag . '-Win32- ' . $ vsVersion . '-x86.zip ' ,
123+ 'https://downloads .php.net/~windows / ' . $ folder_alt . '/ ' . $ tag . '-Win32- ' . $ vsVersion . '-x86.zip ' ,
68124 ],
69125 ];
70126 }
@@ -96,6 +152,10 @@ private function getMultiUrlStatus(array $urlsets): array {
96152
97153 CURLOPT_NOBODY => true ,
98154 CURLOPT_HEADER => true ,
155+
156+ CURLOPT_HTTPHEADER => [
157+ 'Range: bytes=-1 ' ,
158+ ],
99159 ]);
100160
101161 curl_multi_add_handle ($ cm , $ ch );
@@ -111,7 +171,7 @@ private function getMultiUrlStatus(array $urlsets): array {
111171
112172 foreach ($ handlers as $ urls ) {
113173 foreach ($ urls as $ url ) {
114- if (curl_getinfo ($ url , CURLINFO_HTTP_CODE ) === 200 ) {
174+ if (curl_getinfo ($ url , CURLINFO_HTTP_CODE ) === 206 ) {
115175 $ completedUrls [curl_getinfo ($ url , CURLINFO_EFFECTIVE_URL )] = curl_multi_getcontent ($ url );
116176 }
117177 curl_multi_remove_handle ($ cm , $ url );
@@ -121,12 +181,12 @@ private function getMultiUrlStatus(array $urlsets): array {
121181 curl_multi_close ($ cm );
122182
123183 foreach ($ completedUrls as &$ headers ) {
124- preg_match ('/content-length: (?<size>\d+)\D /i ' , $ headers , $ matches );
184+ preg_match ('/content-range: bytes \d+-\d+\/ (?<size>\d+)\b /i ' , $ headers , $ matches );
125185 if (!empty ($ matches ['size ' ])) {
126186 $ headers = $ matches ['size ' ];
127187 }
128188 else {
129- throw new \LogicException ('Content-length not matched ' );
189+ throw new \LogicException ('Content-range not matched ' );
130190 }
131191 }
132192
0 commit comments