3434use Symfony \Contracts \HttpClient \Exception \RedirectionExceptionInterface ;
3535use Symfony \Contracts \HttpClient \Exception \ServerExceptionInterface ;
3636use Symfony \Contracts \HttpClient \Exception \TransportExceptionInterface ;
37+ use Symfony \Contracts \HttpClient \HttpClientInterface ;
3738use ZipArchive ;
3839
3940class Upgrade extends AbstractSetup
@@ -48,14 +49,19 @@ class Upgrade extends AbstractSetup
4849
4950 private bool $ isNightly ;
5051
52+ private HttpClientInterface $ httpClient ;
53+
5154 public function __construct (
5255 protected System $ system ,
5356 private readonly Configuration $ configuration ,
57+ ?HttpClientInterface $ httpClient = null ,
5458 ) {
5559 parent ::__construct ($ this ->system );
5660
5761 $ this ->isNightly =
5862 $ this ->configuration ->get (item: 'upgrade.releaseEnvironment ' ) === ReleaseType::NIGHTLY ->value ;
63+
64+ $ this ->httpClient = $ httpClient ?? HttpClient::create (['timeout ' => 60 ]);
5965 }
6066
6167 /**
@@ -125,25 +131,40 @@ public function downloadPackage(string $version): string
125131 {
126132 $ url = $ this ->getDownloadHost () . $ this ->getPath () . $ this ->getFilename ($ version );
127133
128- $ httpClient = HttpClient::create (['timeout ' => 60 ]);
134+ $ attempts = 3 ;
135+ $ lastExceptionMessage = null ;
129136
130- try {
131- $ response = $ httpClient ->request ('GET ' , $ url );
137+ for ($ i = 0 ; $ i < $ attempts ; $ i ++) {
138+ try {
139+ $ response = $ this ->httpClient ->request ('GET ' , $ url );
132140
133- if ($ response ->getStatusCode () !== 200 ) {
134- throw new Exception ('Cannot download package (HTTP Status: ' . $ response ->getStatusCode () . '). ' );
135- }
141+ if ($ response ->getStatusCode () !== 200 ) {
142+ throw new Exception ('Cannot download package (HTTP Status: ' . $ response ->getStatusCode () . '). ' );
143+ }
136144
137- $ package = $ response ->getContent ();
145+ $ package = $ response ->getContent ();
138146
139- file_put_contents ($ this ->upgradeDirectory . DIRECTORY_SEPARATOR . $ this ->getFilename ($ version ), $ package );
147+ $ targetPath = $ this ->upgradeDirectory . DIRECTORY_SEPARATOR . $ this ->getFilename ($ version );
148+ file_put_contents ($ targetPath , $ package );
140149
141- return $ this ->upgradeDirectory . DIRECTORY_SEPARATOR . $ this ->getFilename ($ version );
142- } catch (
143- TransportExceptionInterface |ClientExceptionInterface |RedirectionExceptionInterface |ServerExceptionInterface $ e
144- ) {
145- throw new Exception ($ e ->getMessage ());
150+ return $ targetPath ;
151+ } catch (
152+ TransportExceptionInterface |ClientExceptionInterface |RedirectionExceptionInterface |ServerExceptionInterface $ exception
153+ ) {
154+ $ lastExceptionMessage = $ exception ->getMessage ();
155+
156+ // After the last attempt, throw the exception outward
157+ if ($ i === ($ attempts - 1 )) {
158+ throw new Exception ('Download failed after ' . $ attempts . ' attempts: ' . $ lastExceptionMessage );
159+ }
160+
161+ // Short sleep to mitigate transient network issues
162+ usleep (250000 ); // 250ms
163+ }
146164 }
165+
166+ // Should not be reached, but for safety
167+ throw new Exception ('Download failed: ' . ($ lastExceptionMessage ?? 'unknown error ' ));
147168 }
148169
149170 /**
@@ -155,8 +176,7 @@ public function downloadPackage(string $version): string
155176 */
156177 public function verifyPackage (string $ path , string $ version ): bool
157178 {
158- $ httpClient = HttpClient::create (['timeout ' => 30 ]);
159- $ response = $ httpClient ->request ('GET ' , DownloadHostType::PHPMYFAQ ->value . 'info/ ' . $ version );
179+ $ response = $ this ->httpClient ->request ('GET ' , DownloadHostType::PHPMYFAQ ->value . 'info/ ' . $ version );
160180
161181 try {
162182 $ responseContent = json_decode ($ response ->getContent (), true , 512 , JSON_THROW_ON_ERROR );
@@ -267,8 +287,9 @@ public function installPackage(callable $progressCallback): bool
267287 $ currentFile = 0 ;
268288
269289 foreach ($ sourceDirIterator as $ item ) {
270- $ source = $ item ->getPathName ();
271- $ destination = $ destinationDir . DIRECTORY_SEPARATOR . $ sourceDirIterator ->getSubPathName ();
290+ $ source = $ item ->getRealPath ();
291+ $ relativePath = str_replace ($ sourceDir , '' , $ source );
292+ $ destination = $ destinationDir . DIRECTORY_SEPARATOR . $ relativePath ;
272293
273294 if ($ item ->isDir ()) {
274295 if (!is_dir ($ destination )) {
0 commit comments