2023-04-22 21:23:12 +08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace SPC\doctor;
|
|
|
|
|
|
|
|
|
|
use SPC\exception\FileSystemException;
|
|
|
|
|
use SPC\exception\RuntimeException;
|
|
|
|
|
use SPC\store\FileSystem;
|
|
|
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
|
|
|
|
2023-09-09 10:04:20 +02:00
|
|
|
final class CheckListHandler
|
2023-04-22 21:23:12 +08:00
|
|
|
{
|
|
|
|
|
/** @var AsCheckItem[] */
|
|
|
|
|
private array $check_list = [];
|
|
|
|
|
|
|
|
|
|
private array $fix_map = [];
|
|
|
|
|
|
2023-09-09 10:04:20 +02:00
|
|
|
public function __construct() {}
|
|
|
|
|
|
2023-04-22 21:23:12 +08:00
|
|
|
/**
|
2023-09-09 10:04:20 +02:00
|
|
|
* @return array<AsCheckItem>
|
2023-04-22 21:23:12 +08:00
|
|
|
* @throws \ReflectionException
|
|
|
|
|
* @throws RuntimeException
|
2023-09-09 10:04:20 +02:00
|
|
|
* @throws FileSystemException
|
2023-04-22 21:23:12 +08:00
|
|
|
*/
|
2023-09-09 10:04:20 +02:00
|
|
|
public function runChecks(bool $include_manual = false): array
|
2023-04-22 21:23:12 +08:00
|
|
|
{
|
2023-09-09 10:04:20 +02:00
|
|
|
return $this->loadCheckList($include_manual);
|
2023-06-28 18:38:14 +08:00
|
|
|
}
|
|
|
|
|
|
2023-08-20 19:51:45 +08:00
|
|
|
/**
|
|
|
|
|
* @throws RuntimeException
|
|
|
|
|
*/
|
2023-09-09 10:04:20 +02:00
|
|
|
public function emitFix(OutputInterface $output, CheckResult $result): void
|
2023-06-28 18:38:14 +08:00
|
|
|
{
|
2023-12-24 20:17:06 +08:00
|
|
|
if (PHP_OS_FAMILY === 'Windows') {
|
|
|
|
|
sapi_windows_set_ctrl_handler(function () use ($output) {
|
|
|
|
|
$output->writeln('<error>You cancelled fix</error>');
|
|
|
|
|
});
|
|
|
|
|
} elseif (extension_loaded('pcntl')) {
|
|
|
|
|
pcntl_signal(SIGINT, function () use ($output) {
|
|
|
|
|
$output->writeln('<error>You cancelled fix</error>');
|
|
|
|
|
});
|
|
|
|
|
}
|
2023-04-22 21:23:12 +08:00
|
|
|
|
2023-09-09 10:04:20 +02:00
|
|
|
$fix_result = call_user_func($this->fix_map[$result->getFixItem()], ...$result->getFixParams());
|
2023-12-24 20:17:06 +08:00
|
|
|
|
|
|
|
|
if (PHP_OS_FAMILY === 'Windows') {
|
|
|
|
|
sapi_windows_set_ctrl_handler(null);
|
|
|
|
|
} elseif (extension_loaded('pcntl')) {
|
|
|
|
|
pcntl_signal(SIGINT, SIG_IGN);
|
|
|
|
|
}
|
2023-09-09 10:04:20 +02:00
|
|
|
|
|
|
|
|
if ($fix_result) {
|
|
|
|
|
$output->writeln('<info>Fix done</info>');
|
|
|
|
|
} else {
|
|
|
|
|
$output->writeln('<error>Fix failed</error>');
|
|
|
|
|
throw new RuntimeException('Some check item are not fixed');
|
2023-04-22 21:23:12 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Load Doctor check item list
|
|
|
|
|
*
|
2023-09-09 10:04:20 +02:00
|
|
|
* @return array<AsCheckItem>
|
2023-04-22 21:23:12 +08:00
|
|
|
* @throws \ReflectionException
|
|
|
|
|
* @throws RuntimeException
|
|
|
|
|
* @throws FileSystemException
|
|
|
|
|
*/
|
2023-09-09 10:04:20 +02:00
|
|
|
private function loadCheckList(bool $include_manual = false): array
|
2023-04-22 21:23:12 +08:00
|
|
|
{
|
2024-06-20 14:46:08 +08:00
|
|
|
foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\doctor\item') as $class) {
|
2023-04-22 21:23:12 +08:00
|
|
|
$ref = new \ReflectionClass($class);
|
|
|
|
|
foreach ($ref->getMethods() as $method) {
|
2023-09-09 10:04:20 +02:00
|
|
|
foreach ($method->getAttributes() as $a) {
|
2023-06-28 18:38:14 +08:00
|
|
|
if (is_a($a->getName(), AsCheckItem::class, true)) {
|
|
|
|
|
/** @var AsCheckItem $instance */
|
|
|
|
|
$instance = $a->newInstance();
|
|
|
|
|
if (!$include_manual && $instance->manual) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$instance->callback = [new $class(), $method->getName()];
|
|
|
|
|
$this->check_list[] = $instance;
|
|
|
|
|
} elseif (is_a($a->getName(), AsFixItem::class, true)) {
|
|
|
|
|
/** @var AsFixItem $instance */
|
|
|
|
|
$instance = $a->newInstance();
|
|
|
|
|
// Redundant fix item
|
|
|
|
|
if (isset($this->fix_map[$instance->name])) {
|
|
|
|
|
throw new RuntimeException('Redundant doctor fix item: ' . $instance->name);
|
|
|
|
|
}
|
|
|
|
|
$this->fix_map[$instance->name] = [new $class(), $method->getName()];
|
2023-04-22 21:23:12 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-09 10:04:20 +02:00
|
|
|
|
2023-04-22 21:23:12 +08:00
|
|
|
// sort check list by level
|
2023-09-09 10:04:20 +02:00
|
|
|
usort($this->check_list, fn (AsCheckItem $a, AsCheckItem $b) => $a->level > $b->level ? -1 : ($a->level == $b->level ? 0 : 1));
|
2023-04-22 21:23:12 +08:00
|
|
|
|
2023-09-09 10:04:20 +02:00
|
|
|
return $this->check_list;
|
2023-04-22 21:23:12 +08:00
|
|
|
}
|
|
|
|
|
}
|