Skip to content

Commit

Permalink
Merge pull request #419 from vladgorenkin/lazy-loaded-commands
Browse files Browse the repository at this point in the history
Implement lazy-loading of cli commands
  • Loading branch information
SerafimArts committed Jun 10, 2021
2 parents 04a567b + 8aab1a6 commit 992e53f
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 16 deletions.
9 changes: 6 additions & 3 deletions composer.json
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
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
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
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
@@ -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
@@ -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
@@ -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
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
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
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
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
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

0 comments on commit 992e53f

Please sign in to comment.