Skip to content

Instantly share code, notes, and snippets.

@crazywhalecc
Created August 27, 2023 06:05
Show Gist options
  • Save crazywhalecc/1f91dfa9bb1137cc78a9b2344b243f2c to your computer and use it in GitHub Desktop.
Save crazywhalecc/1f91dfa9bb1137cc78a9b2344b243f2c to your computer and use it in GitHub Desktop.
GoCommand.php
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\doctor\CheckListHandler;
use SPC\exception\DownloaderException;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\Downloader;
use Symfony\Component\Console\Attribute\AsCommand;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\multiselect;
use function Laravel\Prompts\select;
#[AsCommand('go', 'Interactive build')]
class GoCommand extends BaseCommand
{
/** @var array[] The main steps */
private const MAIN_STEPS = [
'doctor' => [
'display_name' => 'Doctor',
'help' => 'Check your system environment',
'func' => 'doctor',
],
'download' => [
'display_name' => 'Download',
'help' => 'Download required packages to compile',
'func' => 'download',
],
'build:libs' => [
'display_name' => 'Build Libraries',
'help' => 'Build your selected libraries only',
'func' => 'buildLibs',
],
'quit' => [
'display_name' => 'Quit',
'help' => 'Quit',
'func' => 'bye',
],
];
/**
* Go command entry
*/
public function handle(): int
{
// Calculate longest display name
$maxlen = max(...array_map(fn ($x) => strlen($x['display_name']), array_values(self::MAIN_STEPS)));
$select = array_map(function ($x) use ($maxlen) {
$x['display_name'] = str_pad($x['display_name'], $maxlen);
return $x['display_name'] . ' - ' . $x['help'];
}, self::MAIN_STEPS);
try {
$way = null;
while ($way !== 'quit') {
// Ask ways to do
$way = select('Please choose your compile steps', $select);
if (method_exists($this, self::MAIN_STEPS[$way]['func'] ?? '')) {
$this->{self::MAIN_STEPS[$way]['func']}();
}
}
} catch (\Throwable $e) {
$this->output->writeln('<error>' . $e->getMessage() . '</error>');
pcntl_signal(SIGINT, SIG_IGN);
return static::FAILURE;
}
return static::SUCCESS;
}
/**
* [STEP] doctor
*
* @throws \ReflectionException
* @throws RuntimeException
* @throws FileSystemException
*/
protected function doctor(): void
{
$checker = new CheckListHandler($this->input, $this->output);
$checker->runCheck(FIX_POLICY_PROMPT);
$this->output->writeln('<info>Doctor check complete !</info>');
}
/**
* [STEP] download
*
* @throws FileSystemException
* @throws RuntimeException
* @throws WrongUsageException
* @throws DownloaderException
*/
protected function download(): void
{
// Ask which PHP to download
$php_ver = select('Which version of php you want to download?', [
'8.2',
'8.1',
'8.0',
'7.4',
]);
// Choose which to download
$download_sources = select('Do you want to download all dependencies?', [
'all' => 'Yes, All (About 50+ sources)',
'by-source' => 'No, I want to download sources by source name',
'by-ext' => 'No, I want to download sources by required extensions',
]);
switch ($download_sources) {
case 'all':
default:
$sources = [];
break;
case 'by-source':
// Ask sources to download
$sources = multiselect('Which resources are you going to download?', array_keys(Config::getSources()), default: [
'php-src',
'pkg-config',
'micro',
], scroll: 10);
break;
case 'by-ext':
$exts = multiselect('Which extensions are you going to download?', array_keys(Config::getExts()), scroll: 10, validate: fn ($exts) => $this->validateExt($exts));
$confirmed = confirm('Do you want to include suggested libraries and extensions ?');
$sources = ['php-src', 'pkg-config', 'micro'];
foreach ($exts as $ext) {
$sources = array_merge($sources, $this->getExtSources($ext, suggest: $confirmed));
}
break;
}
$this->output->writeln('<info>You will download: ' . implode(', ', $sources) . '</info>');
$cnt = count($sources);
$ni = 0;
foreach ($sources as $source) {
++$ni;
logger()->info("Fetching source {$source} [{$ni}/{$cnt}]");
Downloader::downloadSource($source, Config::getSource($source), options: ['php-ver' => $php_ver]);
}
$this->output->writeln('<info>Download complete !</info>');
}
/**
* [STEP] quit
*/
private function bye(): void
{
$this->output->writeln('<comment>Bye</comment>');
}
/**
* [VALIDATE] Validate extensions list
*
* @param array $exts Extensions
*/
private function validateExt(array $exts): null|string
{
if (in_array('swow', $exts) && in_array('swoole', $exts)) {
return 'Cannot compile swow and swoole at the same time!';
}
if (empty($exts)) {
return 'Please select at least one extension!';
}
return null;
}
/**
* @throws WrongUsageException
* @throws FileSystemException
*/
private function getExtSources(string $ext, int $depth = 0, bool $suggest = true): array
{
if ($depth > 30) {
throw new \RuntimeException('Too many dependencies or circular dependencies!');
}
$sources = [];
if (Config::getExt($ext, 'type') === 'external') {
$sources[] = Config::getExt($ext, 'source');
}
foreach (Config::getExt($ext, 'ext-depends', []) as $ext_depend) {
$sources = array_merge($sources, $this->getExtSources($ext_depend, $depth + 1, $suggest));
}
if ($suggest) {
foreach (Config::getExt($ext, 'ext-suggests', []) as $ext_suggest) {
$sources = array_merge($sources, $this->getExtSources($ext_suggest, $depth + 1, $suggest));
}
}
foreach (Config::getExt($ext, 'lib-depends', []) as $lib_depend) {
$sources = array_merge($sources, $this->getLibSources($lib_depend, $depth + 1, $suggest));
}
if ($suggest) {
foreach (Config::getExt($ext, 'lib-suggests', []) as $lib_depend) {
$sources = array_merge($sources, $this->getLibSources($lib_depend, $depth + 1, $suggest));
}
}
return array_unique($sources);
}
/**
* @throws FileSystemException
* @throws WrongUsageException
*/
private function getLibSources(string $lib, int $depth = 0, bool $suggest = true): array
{
if ($depth > 30) {
throw new \RuntimeException('Too many dependencies or circular dependencies!');
}
$sources = [Config::getLib($lib, 'source')];
foreach (Config::getLib($lib, 'lib-depends', []) as $lib_depend) {
$sources = array_merge($sources, $this->getLibSources($lib_depend, $depth + 1, $suggest));
}
if ($suggest) {
foreach (Config::getLib($lib, 'lib-suggests', []) as $lib_depend) {
$sources = array_merge($sources, $this->getLibSources($lib_depend, $depth + 1, $suggest));
}
}
return array_unique($sources);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment