Skip to content

Commit

Permalink
Refactor the whole application kernel
Browse files Browse the repository at this point in the history
  • Loading branch information
lyrixx committed Apr 7, 2024
1 parent 93fc21f commit e8315c2
Show file tree
Hide file tree
Showing 25 changed files with 425 additions and 290 deletions.
9 changes: 9 additions & 0 deletions bin/generate-tests.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,19 @@
$_SERVER['ENDPOINT'] ??= 'http://127.0.0.1:9955';
WebServerHelper::start();

displayTitle('Cleaning');

$fs = new Filesystem();
$fs->remove(PlatformHelper::getCacheDirectory());
$fs->remove(__DIR__ . '/../tests/Generated');
$fs->mkdir(__DIR__ . '/../tests/Generated');
$fs->remove((new Finder())
->in(__DIR__ . '/../tests/fixtures')
->in(__DIR__ . '/../tests/fixtures')
->path('composer.installed')
->ignoreDotFiles(false)
);
echo "\nDone.\n";

displayTitle('Retrieving example tasks');

Expand Down
111 changes: 8 additions & 103 deletions src/Console/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,15 @@

namespace Castor\Console;

use Castor\Console\Command\SymfonyTaskCommand;
use Castor\Console\Output\VerbosityLevel;
use Castor\Container;
use Castor\Context;
use Castor\ContextRegistry;
use Castor\Event\AfterApplicationInitializationEvent;
use Castor\Event\BeforeApplicationBootEvent;
use Castor\Event\BeforeApplicationInitializationEvent;
use Castor\Factory\TaskCommandFactory;
use Castor\Function\FunctionLoader;
use Castor\Helper\PlatformHelper;
use Castor\Import\Importer;
use Castor\Import\Kernel;
use Castor\Kernel;
use Symfony\Component\Console\Application as SymfonyApplication;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\ExceptionInterface;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/** @internal */
class Application extends SymfonyApplication
Expand All @@ -34,15 +21,8 @@ class Application extends SymfonyApplication
private Command $command;

public function __construct(
private readonly string $rootDir,
private readonly ContainerBuilder $containerBuilder,
private readonly EventDispatcherInterface $eventDispatcher,
#[Autowire(lazy: true)]
private readonly Importer $importer,
private readonly FunctionLoader $functionLoader,
private readonly Kernel $kernel,
private readonly ContextRegistry $contextRegistry,
private readonly TaskCommandFactory $taskCommandFactory,
) {
parent::__construct(static::NAME, static::VERSION);
}
Expand All @@ -55,8 +35,6 @@ public function getCommand(bool $allowNull = false): ?Command
return $this->command ?? ($allowNull ? null : throw new \LogicException('Command not available yet.'));
}

// We do all the logic as late as possible to ensure the exception handler
// is registered
public function doRun(InputInterface $input, OutputInterface $output): int
{
$this->containerBuilder->set(InputInterface::class, $input);
Expand All @@ -65,39 +43,7 @@ public function doRun(InputInterface $input, OutputInterface $output): int
// @phpstan-ignore-next-line
Container::set($this->containerBuilder->get(Container::class));

$this->eventDispatcher->dispatch(new BeforeApplicationBootEvent($this));

$currentFunctions = get_defined_functions()['user'];
$currentClasses = get_declared_classes();

$functionsRootDir = $this->rootDir;
if (class_exists(\RepackedApplication::class)) {
$functionsRootDir = \RepackedApplication::ROOT_DIR;
}
$this->importer->require($functionsRootDir);

$this->eventDispatcher->dispatch(new BeforeApplicationInitializationEvent($this));

$taskDescriptorCollection = $this->functionLoader->load($currentFunctions, $currentClasses);
$taskDescriptorCollection = $taskDescriptorCollection->merge($this->kernel->mount());

$this->initializeApplication($input);

// Must be done after the initializeApplication() call, to ensure all
// contexts have been created; but before the adding of task, because we
// may want to seek in the context to know if the command is enabled
$this->configureContext($input, $output);

$event = new AfterApplicationInitializationEvent($this, $taskDescriptorCollection);
$this->eventDispatcher->dispatch($event);
$taskDescriptorCollection = $event->taskDescriptorCollection;

foreach ($taskDescriptorCollection->taskDescriptors as $taskDescriptor) {
$this->add($this->taskCommandFactory->createTask($taskDescriptor));
}
foreach ($taskDescriptorCollection->symfonyTaskDescriptors as $symfonyTaskDescriptor) {
$this->add(SymfonyTaskCommand::createFromDescriptor($symfonyTaskDescriptor));
}
$this->kernel->boot($input, $output);

return parent::doRun($input, $output);
}
Expand All @@ -109,26 +55,11 @@ protected function doRunCommand(Command $command, InputInterface $input, OutputI
return parent::doRunCommand($command, $input, $output);
}

private function initializeApplication(InputInterface $input): void
protected function getDefaultInputDefinition(): InputDefinition
{
$this->contextRegistry->setDefaultIfEmpty();

$contextNames = $this->contextRegistry->getNames();

if ($contextNames) {
$defaultContext = PlatformHelper::getEnv('CASTOR_CONTEXT') ?: $this->contextRegistry->getDefaultName();
$definition = parent::getDefaultInputDefinition();

$this->getDefinition()->addOption(new InputOption(
'context',
'_complete' === $input->getFirstArgument() || 'list' === $input->getFirstArgument() ? null : 'c',
InputOption::VALUE_REQUIRED,
sprintf('The context to use (%s)', implode('|', $contextNames)),
$defaultContext,
$contextNames,
));
}

$this->getDefinition()->addOption(
$definition->addOption(
new InputOption(
'no-remote',
null,
Expand All @@ -137,41 +68,15 @@ private function initializeApplication(InputInterface $input): void
)
);

$this->getDefinition()->addOption(
$definition->addOption(
new InputOption(
'update-remotes',
null,
InputOption::VALUE_NONE,
'Force the update of remote packages',
)
);
}

private function configureContext(InputInterface $input, OutputInterface $output): void
{
try {
$input->bind($this->getDefinition());
} catch (ExceptionInterface) {
// not an issue if parsing gone wrong, we'll just use the default
// context and it will fail later anyway
}

// occurs when running `castor -h`, or if no context is defined
if (!$input->hasOption('context')) {
$this->contextRegistry->setCurrentContext(new Context());

return;
}

$context = $this
->contextRegistry
->get($input->getOption('context'))
;

if ($context->verbosityLevel->isNotConfigured()) {
$context = $context->withVerbosityLevel(VerbosityLevel::fromSymfonyOutput($output));
}

$this->contextRegistry->setCurrentContext($context->withName($input->getOption('context')));
return $definition;
}
}
2 changes: 2 additions & 0 deletions src/Console/ApplicationFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Castor\Console\Command\DebugCommand;
use Castor\Console\Command\RepackCommand;
use Castor\Container;
use Castor\Descriptor\DescriptorsCollection;
use Castor\Event\AfterApplicationInitializationEvent;
use Castor\Helper\PathHelper;
use Castor\Helper\PlatformHelper;
Expand Down Expand Up @@ -78,6 +79,7 @@ private static function configureDebug(): ErrorHandler

AbstractCloner::$defaultCasters[self::class] = ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'];
AbstractCloner::$defaultCasters[AfterApplicationInitializationEvent::class] = ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'];
AbstractCloner::$defaultCasters[DescriptorsCollection::class] = ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'];

return $errorHandler;
}
Expand Down
12 changes: 10 additions & 2 deletions src/Console/Command/RepackCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,26 @@

namespace Castor\Console\Command;

use Castor\Function\FunctionFinder;
use Castor\Helper\PathHelper;
use Castor\Import\Importer;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\Process;

/** @internal */
class RepackCommand extends Command
{
public function __construct(
#[Autowire(lazy: true)]
private readonly Importer $importer,
) {
parent::__construct();
}

protected function configure(): void
{
$this
Expand Down Expand Up @@ -94,7 +102,7 @@ class RepackedApplication extends Application
$boxConfig['files'] = [
...array_map(
fn (string $file): string => str_replace(PathHelper::getRoot() . '/', '', $file),
FunctionFinder::$files,
$this->importer->getImports(),
),
...$boxConfig['files'] ?? [],
];
Expand Down
3 changes: 1 addition & 2 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Castor\Helper\Notifier;
use Castor\Helper\Waiter;
use Castor\Import\Importer;
use Castor\Import\Kernel;
use Castor\Runner\ParallelRunner;
use Castor\Runner\ProcessRunner;
use Castor\Runner\SshRunner;
Expand Down Expand Up @@ -38,7 +37,7 @@ public function __construct(
public readonly EventDispatcherInterface $eventDispatcher,
public readonly Filesystem $fs,
public readonly FingerprintHelper $fingerprintHelper,
public readonly FunctionFinder $functionFinder,
// public readonly FunctionFinder $functionFinder,
public readonly HttpClientInterface $httpClient,
public readonly Importer $importer,
public readonly InputInterface $input,
Expand Down
23 changes: 23 additions & 0 deletions src/Descriptor/DescriptorsCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Castor\Descriptor;

/** @internal */
final class DescriptorsCollection
{
/**
* @param list<TaskDescriptor> $taskDescriptors
* @param list<ContextDescriptor> $contextDescriptors
* @param list<ContextGeneratorDescriptor> $contextGeneratorDescriptors
* @param list<ListenerDescriptor> $listenerDescriptors
* @param list<SymfonyTaskDescriptor> $symfonyTaskDescriptors
*/
public function __construct(
public readonly array $taskDescriptors,
public readonly array $contextDescriptors,
public readonly array $contextGeneratorDescriptors,
public readonly array $listenerDescriptors,
public readonly array $symfonyTaskDescriptors,
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use Castor\Console\Application;

/** @internal */
class BeforeApplicationBootEvent
class BeforeBootEvent
{
public function __construct(
public readonly Application $application,
Expand Down
7 changes: 7 additions & 0 deletions src/Event/EntryPointLoadedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

namespace Castor\Event;

class EntryPointLoadedEvent
{
}
13 changes: 13 additions & 0 deletions src/Event/FunctionsResolvedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Castor\Event;

use Castor\Descriptor\DescriptorsCollection;

class FunctionsResolvedEvent
{
public function __construct(
public DescriptorsCollection $descriptorsCollection,
) {
}
}
13 changes: 13 additions & 0 deletions src/Exception/CouldNotFindEntrypointException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Castor\Exception;

class CouldNotFindEntrypointException extends \RuntimeException
{
public function __construct(
string $message = 'Could not find root "castor.php" or ".castor/castor.php" file.',
?\Throwable $previous = null,
) {
parent::__construct($message, 0, $previous);
}
}
Loading

0 comments on commit e8315c2

Please sign in to comment.