Author: Shivam Mathur (shivammathur)
Date: 2024-08-12T17:04:41+05:30
Commit:
https://github.com/php/web-downloads/commit/1e5da29076b4a62c4e087cb53265526ed9c1129b
Raw diff:
https://github.com/php/web-downloads/commit/1e5da29076b4a62c4e087cb53265526ed9c1129b.diff
Add support for PHP builds
Changed paths:
M src/PeclHandler.php
M src/PhpHandler.php
M src/WinlibsHandler.php
Diff:
diff --git a/src/PeclHandler.php b/src/PeclHandler.php
index 2cfea4a..03bb7eb 100644
--- a/src/PeclHandler.php
+++ b/src/PeclHandler.php
@@ -43,11 +43,11 @@ protected function execute(array $data): void
*/
private function fetchExtension(string $extension, string $ref, string
$url, string $token): void
{
- $filepath = "/tmp/$extension-$ref-" . strtotime('now') . ".zip";
+ $filepath = "/tmp/$extension-$ref-" . hash('sha256', $url) .
strtotime('now') . ".zip";
FetchArtifact::handle($url, $filepath, $token);
- if(!file_exists($filepath) || !mime_content_type($filepath) ===
'application/zip') {
+ if(!file_exists($filepath) || mime_content_type($filepath) !==
'application/zip') {
throw new Exception('Failed to fetch the extension');
}
diff --git a/src/PhpHandler.php b/src/PhpHandler.php
index 617c915..1c02964 100644
--- a/src/PhpHandler.php
+++ b/src/PhpHandler.php
@@ -2,20 +2,290 @@
namespace App;
+use DateTimeImmutable;
+use Exception;
+use ZipArchive;
+
class PhpHandler extends BaseHandler
{
+ protected function validate(array $data): bool
+ {
+ $validator = new Validator([
+ 'url' => 'required|url',
+ 'token' => 'required|string',
+ ]);
+
+ $validator->validate($data);
+
+ $valid = $validator->isValid();
+
+ if(!$valid) {
+ http_response_code(400);
+ echo 'Invalid request: ' . $validator;
+ }
+
+ return $valid;
+ }
- public function handle(): void
+ protected function execute(array $data): void
{
+ try {
+ extract($data);
+ $this->fetchPhpBuild($url, $token);
+ } catch (Exception $exception) {
+ http_response_code(500);
+ echo 'Error: ' . $exception->getMessage();
+ }
}
- protected function validate(array $data): bool
+ /**
+ * @throws Exception
+ */
+ private function fetchPhpBuild(string $url, string $token): void
+ {
+ $hash = hash('sha256', $url) . strtotime('now');
+
+ $filepath = "/tmp/php-" . $hash . ".tar.gz";
+
+ FetchArtifact::handle($url, $filepath, $token);
+
+ if(!file_exists($filepath) || mime_content_type($filepath) !==
'application/zip') {
+ throw new Exception('Failed to fetch the PHP build');
+ }
+
+ $tempDirectory = "/tmp/php-" . $hash;
+
+ if(is_dir($tempDirectory)) {
+ rmdir($tempDirectory);
+ }
+ mkdir($tempDirectory, 0755, true);
+
+ $zip = new ZipArchive();
+
+ if ($zip->open($filepath) === TRUE) {
+ if($zip->extractTo($tempDirectory) === FALSE) {
+ throw new Exception('Failed to extract the extension build');
+ }
+ $zip->close();
+ } else {
+ throw new Exception('Failed to extract the extension');
+ }
+
+ unlink($filepath);
+
+ $destinationDirectory = $this->getDestinationDirectory($tempDirectory);
+
+ $this->moveBuild($tempDirectory, $destinationDirectory);
+
+ $this->generateListing($destinationDirectory);
+ }
+
+ private function getDestinationDirectory(string $tempDirectory): string
{
- return true;
+ $testPackFile = basename(glob($tempDirectory .
'/php-test-pack-*.zip')[0]);
+ $testPackFileName = str_replace('.zip', '', $testPackFile);
+ $version = explode('-', $testPackFileName)[3];
+ return $_ENV['BUILDS_DIRECTORY'] . (preg_match('/^\d+\.\d+\.\d+$/',
$version) ? '/releases' : '/qa');
}
- protected function execute(array $data): void
+ /**
+ * @throws Exception
+ */
+ private function moveBuild(string $tempDirectory, string
$destinationDirectory): void
{
- //
+ $files = glob($tempDirectory . '/*');
+ if($files) {
+ $version = $this->getFileVersion($files[0]);
+ foreach ($files as $file) {
+ $fileName = basename($file);
+ $destination = $destinationDirectory . '/' . $fileName;
+ rename($file, $destination);
+ }
+ rmdir($tempDirectory);
+ $this->copyBuildsToArchive($destinationDirectory, $version);
+ } else {
+ throw new Exception('No builds found in the artifact');
+ }
+ }
+
+ private function copyBuildsToArchive(string $directory, string $version):
void
+ {
+ $version_short = substr($version, 0, 3);
+ $files = glob($directory . '/php*-' . $version_short . '-*.zip');
+ foreach ($files as $file) {
+ $fileVersion = $this->getFileVersion($file);
+ if($fileVersion) {
+ copy($directory . '/' . basename($file), $directory .
'/archive/' . $file);
+ if(version_compare($fileVersion, $version) < 0) {
+ unlink($file);
+ }
+ }
+ }
+ }
+
+ private function getFileVersion(string $file): string
+ {
+ $file = preg_replace('/^php-((debug|devel|test)-pack)?/', '', $file);
+ return explode('-', $file)[0];
+ }
+
+ /**
+ * @throws Exception
+ */
+ private function generateListing(string $directory): void
+ {
+ $builds = glob($directory . '/php-[678].*[0-9]-latest.zip');
+ if (empty($builds)) {
+ $builds = glob($directory . '/php-[678].*[0-9].zip');
+ }
+
+ $releases = [];
+ $sha256sums = $this->getSha256Sums($directory);
+ foreach ($builds as $file) {
+ $file_ori = $file;
+ $mtime = date('Y-M-d H:i:s', filemtime($file));
+
+ $parts = $this->parseFileName(basename($file));
+ $key = ($parts['nts'] ? 'nts-' : 'ts-') . $parts['vc'] . '-' .
$parts['arch'];
+ $version_short = $parts['version_short'];
+ if (!isset($releases['version'])) {
+ $releases[$version_short]['version'] = $parts['version'];
+ }
+ $releases[$version_short][$key]['mtime'] = $mtime;
+ $releases[$version_short][$key]['zip'] = [
+ 'path' => $file_ori,
+ 'size' => $this->bytes2string(filesize($file_ori)),
+ 'sha256' => $sha256sums[strtolower($file_ori)]
+ ];
+ $namingPattern = $parts['version'] . ($parts['nts'] ? '-' .
$parts['nts'] : '') . '-Win32-' . $parts['vc'] . '-' . $parts['arch'] .
($parts['ts'] ? '-' . $parts['ts'] : '');
+ $build_types = [
+ 'source' => 'php-' . $parts['version'] . '-src.zip',
+ 'debug_pack' => 'php-debug-pack-' . $namingPattern . '.zip',
+ 'devel_pack' => 'php-devel-pack-' . $namingPattern . '.zip',
+ 'installer' => 'php-' . $namingPattern . '.msi',
+ 'test_pack' => 'php-test-pack-' . $parts['version'] . '.zip',
+ ];
+ foreach($build_types as $type => $fileName) {
+ $filePath = $directory . '/' . $fileName;
+ if (file_exists($filePath)) {
+ $releases[$version_short][$type] = [
+ 'path' => $fileName,
+ 'size' => $this->bytes2string(filesize($filePath))
+ ];
+ }
+ }
+ }
+
+ $this->updateReleasesJson($releases, $directory);
+ if($directory === $_ENV['BUILDS_DIRECTORY'] . '/releases') {
+ $this->updateLatestBuilds($releases, $directory);
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ private function updateReleasesJson(array $releases, string $directory):
void
+ {
+ foreach ($releases as &$release) {
+ foreach ($release as &$build_type) {
+ if (! is_array($build_type) || ! isset($build_type['mtime'])) {
+ continue;
+ }
+
+ try {
+ $date = new DateTimeImmutable($build_type['mtime']);
+ $build_type['mtime'] = $date->format('c');
+ } catch (Exception $exception) {
+ throw new Exception('Failed to generate releases.json: ' .
$exception->getMessage());
+ }
+ }
+ unset($build_type);
+ }
+ unset($release);
+
+ file_put_contents(
+ $directory . '/releases.json',
+ json_encode($releases, JSON_PRETTY_PRINT)
+ );
+ }
+
+ private function updateLatestBuilds($releases, $directory): void
+ {
+ foreach ($releases as $versionShort => $release) {
+ $latestFileName = str_replace($release['version'], $versionShort,
$release['path']);
+ $latestFileName = str_replace('.zip', '-latest.zip',
$latestFileName);
+ copy($directory . '/' . $release['path'], $directory . '/latest/'
. $latestFileName);
+ }
+ }
+
+ private function getSha256Sums($directory): array
+ {
+ $result = [];
+ $sha_file = fopen("$directory/sha256sum.txt", 'w');
+ foreach (scandir($directory) as $filename) {
+ if (pathinfo($filename, PATHINFO_EXTENSION) !== 'zip') {
+ continue;
+ }
+ $sha256 = hash_file('sha256', "$directory/$filename");
+ fwrite($sha_file, "$sha256 *$filename\n");
+ $result[strtolower(basename($filename))] = $sha256;
+ }
+ fclose($sha_file);
+ return $result;
+ }
+
+ private function bytes2string(int $size): float
+ {
+ $sizes = ['YB', 'ZB', 'EB', 'PB', 'TB', 'GB', 'MB', 'kB', 'B'];
+
+ $total = count($sizes);
+
+ while($total-- && $size > 1024) $size /= 1024;
+
+ return round($size, 2) . $sizes[$total];
+ }
+
+ private function parseFileName($fileName): array
+ {
+ $fileName = str_replace(['-Win32', '.zip'], ['', ''], $fileName);
+
+ $parts = explode('-', $fileName);
+ if (is_numeric($parts[2]) || $parts[2] == 'dev') {
+ $version = $parts[1] . '-' . $parts[2];
+ $nts = $parts[3] == 'nts' ? 'nts' : false;
+ if ($nts) {
+ $vc = $parts[4];
+ $arch = $parts[5];
+ } else {
+ $vc = $parts[3];
+ $arch = $parts[4];
+ }
+ } elseif ($parts[2] == 'nts') {
+ $nts = 'nts';
+ $version = $parts[1];
+ $vc = $parts[3];
+ $arch = $parts[4];
+ } else {
+ $nts = false;
+ $version = $parts[1];
+ $vc = $parts[2];
+ $arch = $parts[3];
+ }
+ if (is_numeric($vc)) {
+ $vc = 'VC6';
+ $arch = 'x86';
+ }
+ $t = count($parts) - 1;
+ $ts = is_numeric($parts[$t]) ? $parts[$t] : false;
+
+ return [
+ 'version' => $version,
+ 'version_short' => substr($version, 0, 3),
+ 'nts' => $nts,
+ 'vc' => $vc,
+ 'arch' => $arch,
+ 'ts' => $ts
+ ];
}
}
\ No newline at end of file
diff --git a/src/WinlibsHandler.php b/src/WinlibsHandler.php
index c541f82..63684b9 100644
--- a/src/WinlibsHandler.php
+++ b/src/WinlibsHandler.php
@@ -4,11 +4,6 @@
class WinlibsHandler extends BaseHandler
{
-
- public function handle(): void
- {
- }
-
protected function validate(array $data): bool
{
return true;