Skip to content

Commit

Permalink
Add a command to generate listing
Browse files Browse the repository at this point in the history
Add support to do DI in commands

Fix autoloading in tests
  • Loading branch information
shivammathur committed Feb 4, 2025
1 parent 50ff897 commit 03ad5dc
Show file tree
Hide file tree
Showing 12 changed files with 705 additions and 184 deletions.
2 changes: 1 addition & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
bootstrap="autoloader.php"
cacheDirectory=".phpunit.cache"
executionOrder="depends,defects"
shortenArraysForExportThreshold="10"
Expand Down
25 changes: 24 additions & 1 deletion runner.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,37 @@ function discoverCommands(string $directory, $argc, $argv): array
if ($file->isFile() && $file->getExtension() === 'php') {
$className = getClassName($directory, $file);
if (is_subclass_of($className, Command::class)) {
$instance = new $className($argc, $argv);
$instance = resolve($className);
$instance->setCliArguments($argc, $argv);
$commands[$instance->getSignature()] = $instance;
}
}
}
return $commands;
}

function resolve(string $className) {
$reflection = new ReflectionClass($className);
$constructor = $reflection->getConstructor();
if (!$constructor) {
return new $className;
}
$parameters = $constructor->getParameters();
$dependencies = [];
foreach ($parameters as $parameter) {
$type = $parameter->getType();
if ($type && !$type->isBuiltin()) {
$dependencyClass = $type->getName();
$dependencies[] = resolve($dependencyClass);
} elseif ($parameter->isDefaultValueAvailable()) {
$dependencies[] = $parameter->getDefaultValue();
} else {
throw new Exception("Cannot resolve dependency: " . $parameter->getName());
}
}
return $reflection->newInstanceArgs($dependencies);
}

function listCommands(array $commands): void
{
echo "Available commands:\n";
Expand Down
134 changes: 134 additions & 0 deletions src/Actions/GetListing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?php

namespace App\Actions;

class GetListing
{
public function handle(string $directory): array
{
$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' => basename($file_ori),
'size' => $this->bytes2string(filesize($file_ori)),
'sha256' => $sha256sums[strtolower(basename($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)) {
if(in_array($type, ['test_pack', 'source'])) {
$releases[$version_short][$type] = [
'path' => $fileName,
'size' => $this->bytes2string(filesize($filePath)),
'sha256' => $sha256sums[strtolower(basename($file_ori))]
];
} else {
$releases[$version_short][$key][$type] = [
'path' => $fileName,
'size' => $this->bytes2string(filesize($filePath)),
'sha256' => $sha256sums[strtolower(basename($file_ori))]
];
}
}
}
}
return $releases;
}

public function getSha256Sums($directory): array
{
$result = [];
if(!file_exists("$directory/sha256sum.txt")) {
file_put_contents("$directory/sha256sum.txt", '');
}
$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;
}

public function bytes2string(int $size): string
{
$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];
}

public 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
];
}
}
36 changes: 36 additions & 0 deletions src/Actions/UpdateReleasesJson.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace App\Actions;

use DateTimeImmutable;
use Exception;

class UpdateReleasesJson
{
/**
* @throws Exception
*/
public function handle(array $releases, string $directory): void
{
try {
foreach ($releases as &$release) {
foreach ($release as &$build_type) {
if (!is_array($build_type) || !isset($build_type['mtime'])) {
continue;
}

$date = new DateTimeImmutable($build_type['mtime']);
$build_type['mtime'] = $date->format('c');
}
unset($build_type);
}
unset($release);
file_put_contents(
$directory . '/releases.json',
json_encode($releases, JSON_PRETTY_PRINT)
);
} catch (Exception $exception) {
throw new Exception('Failed to generate releases.json: ' . $exception->getMessage());
}
}
}
20 changes: 9 additions & 11 deletions src/Console/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,19 @@ abstract class Command
public const INVALID = 2;

protected string $signature = '';

protected string $description = '';
protected array $arguments = [];
protected array $options = [];

public function __construct(
protected ?int $argc = null,
protected ?array $argv = null,
protected array $arguments = [],
protected array $options = [],
) {
if ($argc !== null && $argv !== null) {
$this->parse($argc, $argv);
}
public function __construct() {
//
}

public function setCliArguments(int $argc, array $argv): void {
$this->parse($argc, $argv);
}
abstract public function handle();

abstract public function handle(): int;

private function parse($argc, $argv): void {
$pattern = '/\{(\w+)}|\{--(\w+)}/';
Expand Down
38 changes: 38 additions & 0 deletions src/Console/Command/GenerateListingCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace App\Console\Command;

use App\Actions\GetListing;
use App\Actions\UpdateReleasesJson;
use App\Console\Command;
use Exception;

class GenerateListingCommand extends Command
{
protected string $signature = 'php:generate-listing --directory=';
protected string $description = 'Generate Listing for PHP builds in a directory';

public function __construct(
protected GetListing $generateListing,
protected UpdateReleasesJson $updateReleasesJson,
) {
parent::__construct();
}

public function handle(): int
{
try {
$directory = $this->getOption('directory');
if (!$directory) {
throw new Exception('Directory is required');
}

$releases = $this->generateListing->handle($directory);
$this->updateReleasesJson->handle($releases, $directory);
return Command::SUCCESS;
} catch (Exception $e) {
echo $e->getMessage();
return Command::FAILURE;
}
}
}
Loading

0 comments on commit 03ad5dc

Please sign in to comment.