mirror of
https://github.com/crazywhalecc/static-php-cli.git
synced 2026-03-18 04:44:53 +08:00
Rework doctor command
This commit is contained in:
parent
01c4538ce0
commit
52430cbdde
@ -5,7 +5,11 @@ declare(strict_types=1);
|
|||||||
namespace SPC\command;
|
namespace SPC\command;
|
||||||
|
|
||||||
use SPC\doctor\CheckListHandler;
|
use SPC\doctor\CheckListHandler;
|
||||||
|
use SPC\doctor\CheckResult;
|
||||||
|
use SPC\exception\RuntimeException;
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
||||||
|
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||||
|
|
||||||
#[AsCommand('doctor', 'Diagnose whether the current environment can compile normally')]
|
#[AsCommand('doctor', 'Diagnose whether the current environment can compile normally')]
|
||||||
class DoctorCommand extends BaseCommand
|
class DoctorCommand extends BaseCommand
|
||||||
@ -18,14 +22,65 @@ class DoctorCommand extends BaseCommand
|
|||||||
public function handle(): int
|
public function handle(): int
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$checker = new CheckListHandler($this->input, $this->output);
|
$checker = new CheckListHandler();
|
||||||
$checker->runCheck($this->input->getOption('auto-fix') ? FIX_POLICY_AUTOFIX : FIX_POLICY_PROMPT);
|
|
||||||
|
$fix_policy = $this->input->getOption('auto-fix') ? FIX_POLICY_AUTOFIX : FIX_POLICY_PROMPT;
|
||||||
|
foreach ($checker->runChecks() as $check) {
|
||||||
|
if ($check->limit_os !== null && $check->limit_os !== PHP_OS_FAMILY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->output->write('Checking <comment>' . $check->item_name . '</comment> ... ');
|
||||||
|
|
||||||
|
$result = call_user_func($check->callback);
|
||||||
|
if ($result === null) {
|
||||||
|
$this->output->writeln('skipped');
|
||||||
|
} elseif ($result instanceof CheckResult) {
|
||||||
|
if ($result->isOK()) {
|
||||||
|
$this->output->writeln($result->getMessage() ?? 'ok');
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed
|
||||||
|
$this->output->writeln('<error>' . $result->getMessage() . '</error>');
|
||||||
|
switch ($fix_policy) {
|
||||||
|
case FIX_POLICY_DIE:
|
||||||
|
throw new RuntimeException('Some check items can not be fixed !');
|
||||||
|
case FIX_POLICY_PROMPT:
|
||||||
|
if ($result->getFixItem() !== '') {
|
||||||
|
$helper = new QuestionHelper();
|
||||||
|
$question = new ConfirmationQuestion('Do you want to fix it? [Y/n] ', true);
|
||||||
|
if ($helper->ask($this->input, $this->output, $question)) {
|
||||||
|
$checker->emitFix($this->output, $result);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException('You cancelled fix');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException('Some check items can not be fixed !');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FIX_POLICY_AUTOFIX:
|
||||||
|
if ($result->getFixItem() !== '') {
|
||||||
|
$this->output->writeln('Automatically fixing ' . $result->getFixItem() . ' ...');
|
||||||
|
$checker->emitFix($this->output, $result);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException('Some check items can not be fixed !');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->output->writeln('<info>Doctor check complete !</info>');
|
$this->output->writeln('<info>Doctor check complete !</info>');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$this->output->writeln('<error>' . $e->getMessage() . '</error>');
|
$this->output->writeln('<error>' . $e->getMessage() . '</error>');
|
||||||
|
|
||||||
pcntl_signal(SIGINT, SIG_IGN);
|
pcntl_signal(SIGINT, SIG_IGN);
|
||||||
|
|
||||||
return static::FAILURE;
|
return static::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return static::SUCCESS;
|
return static::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,105 +7,62 @@ namespace SPC\doctor;
|
|||||||
use SPC\exception\FileSystemException;
|
use SPC\exception\FileSystemException;
|
||||||
use SPC\exception\RuntimeException;
|
use SPC\exception\RuntimeException;
|
||||||
use SPC\store\FileSystem;
|
use SPC\store\FileSystem;
|
||||||
use Symfony\Component\Console\Helper\QuestionHelper;
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
|
||||||
|
|
||||||
class CheckListHandler
|
final class CheckListHandler
|
||||||
{
|
{
|
||||||
/** @var AsCheckItem[] */
|
/** @var AsCheckItem[] */
|
||||||
private array $check_list = [];
|
private array $check_list = [];
|
||||||
|
|
||||||
private array $fix_map = [];
|
private array $fix_map = [];
|
||||||
|
|
||||||
|
public function __construct() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @return array<AsCheckItem>
|
||||||
* @throws \ReflectionException
|
* @throws \ReflectionException
|
||||||
|
* @throws RuntimeException
|
||||||
* @throws FileSystemException
|
* @throws FileSystemException
|
||||||
* @throws RuntimeException
|
|
||||||
*/
|
*/
|
||||||
public function __construct(private readonly InputInterface $input, private readonly OutputInterface $output, bool $include_manual = false)
|
public function runChecks(bool $include_manual = false): array
|
||||||
{
|
{
|
||||||
$this->loadCheckList($include_manual);
|
return $this->loadCheckList($include_manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
*/
|
*/
|
||||||
public function runSingleCheck(string $item_name, int $fix_policy = FIX_POLICY_DIE): void
|
public function emitFix(OutputInterface $output, CheckResult $result): void
|
||||||
{
|
{
|
||||||
foreach ($this->check_list as $item) {
|
pcntl_signal(SIGINT, function () use ($output) {
|
||||||
if ($item->item_name === $item_name) {
|
$output->writeln('<error>You cancelled fix</error>');
|
||||||
$this->check_list = [$item];
|
});
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->runCheck($fix_policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
$fix_result = call_user_func($this->fix_map[$result->getFixItem()], ...$result->getFixParams());
|
||||||
* @throws RuntimeException
|
pcntl_signal(SIGINT, SIG_IGN);
|
||||||
*/
|
|
||||||
public function runCheck(int $fix_policy = FIX_POLICY_DIE): void
|
if ($fix_result) {
|
||||||
{
|
$output->writeln('<info>Fix done</info>');
|
||||||
foreach ($this->check_list as $item) {
|
} else {
|
||||||
if ($item->limit_os !== null && $item->limit_os !== PHP_OS_FAMILY) {
|
$output->writeln('<error>Fix failed</error>');
|
||||||
continue;
|
throw new RuntimeException('Some check item are not fixed');
|
||||||
}
|
|
||||||
$this->output->write('Checking <comment>' . $item->item_name . '</comment> ... ');
|
|
||||||
$result = call_user_func($item->callback);
|
|
||||||
if ($result === null) {
|
|
||||||
$this->output->writeln('skipped');
|
|
||||||
} elseif ($result instanceof CheckResult) {
|
|
||||||
if ($result->isOK()) {
|
|
||||||
$this->output->writeln($result->getMessage() ?? 'ok');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Failed
|
|
||||||
$this->output->writeln('<error>' . $result->getMessage() . '</error>');
|
|
||||||
switch ($fix_policy) {
|
|
||||||
case FIX_POLICY_DIE:
|
|
||||||
throw new RuntimeException('Some check items can not be fixed !');
|
|
||||||
case FIX_POLICY_PROMPT:
|
|
||||||
if ($result->getFixItem() !== '') {
|
|
||||||
$helper = new QuestionHelper();
|
|
||||||
$question = new ConfirmationQuestion('Do you want to fix it? [Y/n] ', true);
|
|
||||||
if ($helper->ask($this->input, $this->output, $question)) {
|
|
||||||
$this->emitFix($result);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException('You cancelled fix');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException('Some check items can not be fixed !');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FIX_POLICY_AUTOFIX:
|
|
||||||
if ($result->getFixItem() !== '') {
|
|
||||||
$this->output->writeln('Automatically fixing ' . $result->getFixItem() . ' ...');
|
|
||||||
$this->emitFix($result);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException('Some check items can not be fixed !');
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load Doctor check item list
|
* Load Doctor check item list
|
||||||
*
|
*
|
||||||
|
* @return array<AsCheckItem>
|
||||||
* @throws \ReflectionException
|
* @throws \ReflectionException
|
||||||
* @throws RuntimeException
|
* @throws RuntimeException
|
||||||
* @throws FileSystemException
|
* @throws FileSystemException
|
||||||
*/
|
*/
|
||||||
private function loadCheckList(bool $include_manual = false): void
|
private function loadCheckList(bool $include_manual = false): array
|
||||||
{
|
{
|
||||||
foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\\doctor\\item') as $class) {
|
foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\\doctor\\item') as $class) {
|
||||||
$ref = new \ReflectionClass($class);
|
$ref = new \ReflectionClass($class);
|
||||||
foreach ($ref->getMethods() as $method) {
|
foreach ($ref->getMethods() as $method) {
|
||||||
$attr = $method->getAttributes();
|
foreach ($method->getAttributes() as $a) {
|
||||||
foreach ($attr as $a) {
|
|
||||||
if (is_a($a->getName(), AsCheckItem::class, true)) {
|
if (is_a($a->getName(), AsCheckItem::class, true)) {
|
||||||
/** @var AsCheckItem $instance */
|
/** @var AsCheckItem $instance */
|
||||||
$instance = $a->newInstance();
|
$instance = $a->newInstance();
|
||||||
@ -126,26 +83,10 @@ class CheckListHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// sort check list by level
|
|
||||||
usort($this->check_list, fn ($a, $b) => $a->level > $b->level ? -1 : ($a->level == $b->level ? 0 : 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// sort check list by level
|
||||||
* @throws RuntimeException
|
usort($this->check_list, fn (AsCheckItem $a, AsCheckItem $b) => $a->level > $b->level ? -1 : ($a->level == $b->level ? 0 : 1));
|
||||||
*/
|
|
||||||
private function emitFix(CheckResult $result): void
|
return $this->check_list;
|
||||||
{
|
|
||||||
pcntl_signal(SIGINT, function () {
|
|
||||||
$this->output->writeln('<error>You cancelled fix</error>');
|
|
||||||
});
|
|
||||||
$fix = $this->fix_map[$result->getFixItem()];
|
|
||||||
$fix_result = call_user_func($fix, ...$result->getFixParams());
|
|
||||||
pcntl_signal(SIGINT, SIG_IGN);
|
|
||||||
if ($fix_result) {
|
|
||||||
$this->output->writeln('<info>Fix done</info>');
|
|
||||||
} else {
|
|
||||||
$this->output->writeln('<error>Fix failed</error>');
|
|
||||||
throw new RuntimeException('Some check item are not fixed');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
tests/SPC/doctor/CheckListHandlerTest.php
Normal file
21
tests/SPC/doctor/CheckListHandlerTest.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace SPC\Tests\doctor;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use SPC\doctor\CheckListHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class CheckListHandlerTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testRunChecksReturnsListOfCheck(): void
|
||||||
|
{
|
||||||
|
$list = new CheckListHandler();
|
||||||
|
|
||||||
|
$this->assertCount(6, $list->runChecks());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user