Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement lazy-loading of cli commands #419

Merged
merged 4 commits into from
Jun 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@
"psr/log": "^1.0",
"psr/simple-cache": ">=1.0",
"spiral/composer-publish-plugin": "^1.0",
"symfony/console": "^5.1",
"symfony/console": "^5.3",
"symfony/finder": "^5.1",
"symfony/mailer": "^5.1",
"symfony/polyfill-php73": "^1.18",
"symfony/polyfill-php80": "^1.18",
"symfony/polyfill-php80": "^1.22",
"symfony/translation": "^5.1",
"vlucas/phpdotenv": "^3.6"
},
Expand Down Expand Up @@ -118,12 +118,15 @@
}
},
"require-dev": {
"aws/aws-sdk-php": "^3.0",
"cycle/annotated": "^2.0.6",
"cycle/migrations": "^1.0.1",
"cycle/orm": "^1.2.6",
"cycle/proxy-factory": "^1.2",
"cycle/schema-builder": "^1.1",
"guzzlehttp/psr7": "^1.7",
"jetbrains/phpstorm-attributes": "^1.0",
"laminas/laminas-diactoros": "^2.3",
"laminas/laminas-hydrator": "^3.0",
"league/flysystem-async-aws-s3": "^2.0",
"league/flysystem-aws-s3-v3": "^2.0",
Expand All @@ -136,7 +139,7 @@
"spiral/code-style": "^1.0",
"spiral/database": "^2.7.3",
"spiral/jobs": "^2.2",
"spiral/migrations": "^2.1",
"spiral/migrations": "^2.2",
"spiral/php-grpc": "^1.4",
"spiral/roadrunner": "^1.9.2",
"symfony/var-dumper": "^5.2",
Expand Down
2 changes: 1 addition & 1 deletion monorepo-builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
'mockery/mockery' => '^1.3',
'spiral/code-style' => '^1.0',
'spiral/database' => '^2.7.3',
'spiral/migrations' => '^2.1',
'spiral/migrations' => '^2.2',
'spiral/roadrunner' => '^1.9.2',
'spiral/php-grpc' => '^1.4',
'spiral/jobs' => '^2.2',
Expand Down
2 changes: 1 addition & 1 deletion src/Console/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"require": {
"php": ">=7.2",
"spiral/core": "^2.9",
"symfony/console": "^5.1"
"symfony/console": "^5.3"
},
"require-dev": {
"phpunit/phpunit": "^8.5|^9.0",
Expand Down
15 changes: 10 additions & 5 deletions src/Console/src/StaticLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,27 @@
namespace Spiral\Console;

use Psr\Container\ContainerInterface;
use Spiral\Console\Traits\LazyTrait;
use Spiral\Core\Container;

final class StaticLocator implements LocatorInterface
{
/** @var []string */
use LazyTrait;

/** @var string[] */
private $commands;

/** @var ContainerInterface */
private $factory;
private $container;

/**
* @param array $commands
* @param ContainerInterface $container
* @param ContainerInterface $container
*/
public function __construct(array $commands, ContainerInterface $container = null)
{
$this->commands = $commands;
$this->factory = $container ?? new Container();
$this->container = $container ?? new Container();
}

/**
Expand All @@ -39,7 +42,9 @@ public function locateCommands(): array
{
$commands = [];
foreach ($this->commands as $command) {
$commands[] = $this->factory->get($command);
$commands[] = $this->supportsLazyLoading($command)
? $this->createLazyCommand($command)
: $this->container->get($command);
}

return $commands;
Expand Down
59 changes: 59 additions & 0 deletions src/Console/src/Traits/LazyTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

/**
* Spiral Framework.
*
* @license MIT
* @author Vladislav Gorenkin (vladgorenkin)
*/

declare(strict_types=1);

namespace Spiral\Console\Traits;

use Psr\Container\ContainerInterface;
use Spiral\Console\Command as SpiralCommand;
use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Command\LazyCommand;

trait LazyTrait
{
/** @var ContainerInterface */
private $container;

/**
* Check if command can be lazy-loaded.
*
* @param class-string $class
* @return bool
*/
private function supportsLazyLoading(string $class): bool
{
return is_subclass_of($class, SpiralCommand::class)
&& \defined(sprintf('%s::%s', $class, 'NAME'));
}

/**
* Wrap given command into LazyCommand which will be executed only on run.
*
* @param class-string $class
* @return LazyCommand
*/
private function createLazyCommand(string $class): LazyCommand
{
return new LazyCommand(
$class::NAME,
[],
\defined(sprintf('%s::%s', $class, 'DESCRIPTION'))
? $class::DESCRIPTION
: '',
false,
function () use ($class): SymfonyCommand {
$command = $this->container->get($class);
$command->setContainer($this->container);

return $command;
}
);
}
}
27 changes: 27 additions & 0 deletions src/Console/tests/Fixtures/LazyLoadedCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

/**
* Spiral Framework, SpiralScout LLC.
*
* @author Vladislav Gorenkin (vladgorenkin)
*/

declare(strict_types=1);

namespace Spiral\Tests\Console\Fixtures;

use Exception;
use Spiral\Console\Command;

class LazyLoadedCommand extends Command
{
public const NAME = 'lazy';
public const DESCRIPTION = 'Lazy description';

public function perform(): int
{
$this->write('OK');

return self::SUCCESS;
}
}
59 changes: 59 additions & 0 deletions src/Console/tests/LazyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

/**
* Spiral Framework, SpiralScout LLC.
*
* @author Vladislav Gorenkin (vladgorenkin)
*/

declare(strict_types=1);

namespace Spiral\Tests\Console;

use Spiral\Console\CommandLocator;
use Spiral\Console\StaticLocator;
use Spiral\Tests\Console\Fixtures\LazyLoadedCommand;
use Spiral\Tokenizer\ClassesInterface;
use Symfony\Component\Console\Command\LazyCommand;

class LazyTest extends BaseTest
{
public function testLazyCommandCreationInCommandLocator(): void
{
$locator = new CommandLocator(
new class() implements ClassesInterface {
public function getClasses($target = null): array
{
return [
new \ReflectionClass(LazyLoadedCommand::class),
];
}
},
$this->container
);
$commands = $locator->locateCommands();
$command = reset($commands);

$this->assertInstanceOf(LazyCommand::class, $command);
$this->assertSame('lazy', $command->getName());
$this->assertSame('Lazy description', $command->getDescription());
}

public function testLazyCommandCreationInStaticLocator(): void
{
$locator = new StaticLocator([LazyLoadedCommand::class]);
$commands = $locator->locateCommands();
$command = reset($commands);

$this->assertInstanceOf(LazyCommand::class, $command);
$this->assertSame('lazy', $command->getName());
$this->assertSame('Lazy description', $command->getDescription());
}

public function testLazyCommandExecution(): void
{
$core = $this->getCore(new StaticLocator([LazyLoadedCommand::class]));
$output = $core->run('lazy');
$this->assertSame('OK', $output->getOutput()->fetch());
}
}
7 changes: 6 additions & 1 deletion src/Framework/Console/CommandLocator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
namespace Spiral\Console;

use Psr\Container\ContainerInterface;
use Spiral\Console\Traits\LazyTrait;
use Spiral\Tokenizer\ClassesInterface;
use Symfony\Component\Console\Command\Command as SymfonyCommand;

final class CommandLocator implements LocatorInterface
{
use LazyTrait;

/** @var ClassesInterface */
private $classes;

Expand Down Expand Up @@ -44,7 +47,9 @@ public function locateCommands(): array
continue;
}

$commands[] = $this->container->get($class->getName());
$commands[] = $this->supportsLazyLoading($class->getName())
? $this->createLazyCommand($class->getName())
: $this->container->get($class->getName());
}

return $commands;
Expand Down
2 changes: 1 addition & 1 deletion src/Http/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"psr/http-message": "^1.0",
"psr/http-factory": "^1.0",
"psr/http-server-middleware": "^1.0",
"symfony/polyfill-php80": "^1.18"
"symfony/polyfill-php80": "^1.22"
},
"require-dev": {
"phpunit/phpunit": "^8.5|^9.0",
Expand Down
4 changes: 2 additions & 2 deletions src/Scaffolder/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@
"spiral/core": "^2.9",
"spiral/filters": "^2.9",
"spiral/http": "^2.9",
"spiral/migrations": "^2.9",
"spiral/migrations": "^2.2",
"spiral/prototype": "^2.9",
"spiral/jobs": "^2.9"
"spiral/jobs": "^2.2"
},
"autoload": {
"files": [
Expand Down
2 changes: 1 addition & 1 deletion src/SendIt/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"require-dev": {
"phpunit/phpunit": "^8.5|^9.0",
"mockery/mockery": "^1.3",
"spiral/jobs": "^2.9",
"spiral/jobs": "^2.2",
"spiral/views": "^2.9",
"spiral/stempler-bridge": "^2.9"
},
Expand Down
2 changes: 1 addition & 1 deletion src/Storage/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"require": {
"php": ">=7.2",
"spiral/distribution": "^2.9",
"symfony/polyfill-php80": "^1.18",
"symfony/polyfill-php80": "^1.22",
"league/flysystem": "^2.0"
},
"autoload": {
Expand Down