diff --git a/src/SPC/command/DoctorCommand.php b/src/SPC/command/DoctorCommand.php
index 74fc48b8..b184fa21 100644
--- a/src/SPC/command/DoctorCommand.php
+++ b/src/SPC/command/DoctorCommand.php
@@ -5,7 +5,11 @@ declare(strict_types=1);
namespace SPC\command;
use SPC\doctor\CheckListHandler;
+use SPC\doctor\CheckResult;
+use SPC\exception\RuntimeException;
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')]
class DoctorCommand extends BaseCommand
@@ -18,14 +22,65 @@ class DoctorCommand extends BaseCommand
public function handle(): int
{
try {
- $checker = new CheckListHandler($this->input, $this->output);
- $checker->runCheck($this->input->getOption('auto-fix') ? FIX_POLICY_AUTOFIX : FIX_POLICY_PROMPT);
+ $checker = new CheckListHandler();
+
+ $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 ' . $check->item_name . ' ... ');
+
+ $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('' . $result->getMessage() . '');
+ 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('Doctor check complete !');
} catch (\Throwable $e) {
$this->output->writeln('' . $e->getMessage() . '');
+
pcntl_signal(SIGINT, SIG_IGN);
+
return static::FAILURE;
}
+
return static::SUCCESS;
}
}
diff --git a/src/SPC/doctor/CheckListHandler.php b/src/SPC/doctor/CheckListHandler.php
index 414c89f9..bfeb39c4 100644
--- a/src/SPC/doctor/CheckListHandler.php
+++ b/src/SPC/doctor/CheckListHandler.php
@@ -7,105 +7,62 @@ namespace SPC\doctor;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
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\Question\ConfirmationQuestion;
-class CheckListHandler
+final class CheckListHandler
{
/** @var AsCheckItem[] */
private array $check_list = [];
private array $fix_map = [];
+ public function __construct() {}
+
/**
+ * @return array
* @throws \ReflectionException
+ * @throws RuntimeException
* @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
*/
- 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) {
- if ($item->item_name === $item_name) {
- $this->check_list = [$item];
- break;
- }
- }
- $this->runCheck($fix_policy);
- }
+ pcntl_signal(SIGINT, function () use ($output) {
+ $output->writeln('You cancelled fix');
+ });
- /**
- * @throws RuntimeException
- */
- public function runCheck(int $fix_policy = FIX_POLICY_DIE): void
- {
- foreach ($this->check_list as $item) {
- if ($item->limit_os !== null && $item->limit_os !== PHP_OS_FAMILY) {
- continue;
- }
- $this->output->write('Checking ' . $item->item_name . ' ... ');
- $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('' . $result->getMessage() . '');
- 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;
- }
- }
+ $fix_result = call_user_func($this->fix_map[$result->getFixItem()], ...$result->getFixParams());
+ pcntl_signal(SIGINT, SIG_IGN);
+
+ if ($fix_result) {
+ $output->writeln('Fix done');
+ } else {
+ $output->writeln('Fix failed');
+ throw new RuntimeException('Some check item are not fixed');
}
}
/**
* Load Doctor check item list
*
+ * @return array
* @throws \ReflectionException
* @throws RuntimeException
* @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) {
$ref = new \ReflectionClass($class);
foreach ($ref->getMethods() as $method) {
- $attr = $method->getAttributes();
- foreach ($attr as $a) {
+ foreach ($method->getAttributes() as $a) {
if (is_a($a->getName(), AsCheckItem::class, true)) {
/** @var AsCheckItem $instance */
$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));
- }
- /**
- * @throws RuntimeException
- */
- private function emitFix(CheckResult $result): void
- {
- pcntl_signal(SIGINT, function () {
- $this->output->writeln('You cancelled fix');
- });
- $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('Fix done');
- } else {
- $this->output->writeln('Fix failed');
- throw new RuntimeException('Some check item are not fixed');
- }
+ // sort check list by level
+ usort($this->check_list, fn (AsCheckItem $a, AsCheckItem $b) => $a->level > $b->level ? -1 : ($a->level == $b->level ? 0 : 1));
+
+ return $this->check_list;
}
}
diff --git a/tests/SPC/doctor/CheckListHandlerTest.php b/tests/SPC/doctor/CheckListHandlerTest.php
new file mode 100644
index 00000000..28b37cf5
--- /dev/null
+++ b/tests/SPC/doctor/CheckListHandlerTest.php
@@ -0,0 +1,21 @@
+assertCount(6, $list->runChecks());
+ }
+}