diff --git a/composer.json b/composer.json index d2d14aa4..5473736d 100644 --- a/composer.json +++ b/composer.json @@ -55,8 +55,9 @@ "roave/infection-static-analysis-plugin": "^1.23", "spatie/phpunit-watcher": "^1.23", "vimeo/psalm": "^5.13", - "yiisoft/active-record": "^3.0@dev", + "yiisoft/active-record": "dev-master", "yiisoft/cache": "^3.0", + "yiisoft/db": "1.2 as dev-master", "yiisoft/db-sqlite": "^1.0", "yiisoft/di": "^1.1", "yiisoft/dummy-provider": "^1.0", @@ -98,7 +99,8 @@ "sort-packages": true, "allow-plugins": { "infection/extension-installer": true, - "composer/package-versions-deprecated": true + "composer/package-versions-deprecated": true, + "php-http/discovery": false } }, "scripts": { diff --git a/config/di.php b/config/di.php index fe6fc121..09054a1a 100644 --- a/config/di.php +++ b/config/di.php @@ -4,6 +4,7 @@ use Yiisoft\Injector\Injector; use Yiisoft\Yii\Gii\GeneratorInterface; +use Yiisoft\Yii\Gii\GeneratorProxy; use Yiisoft\Yii\Gii\Gii; use Yiisoft\Yii\Gii\GiiInterface; use Yiisoft\Yii\Gii\ParametersProvider; @@ -14,7 +15,7 @@ return [ GiiInterface::class => function (Injector $injector) use ($params): GiiInterface { - $generatorsInstances = []; + $proxies = []; $generators = $params['yiisoft/yii-gii']['generators']; foreach ($generators as $generator) { @@ -22,10 +23,13 @@ /** * @var $loader Closure(): GeneratorInterface */ - $loader = fn() => $injector->make($class, $generator['parameters'] ?? []); - $generatorsInstances[$class] = $loader; + $loader = new GeneratorProxy( + fn() => $injector->make($class, $generator['parameters'] ?? []), + $class, + ); + $proxies[$class::getId()] = $loader; } - return new Gii($generatorsInstances); + return new Gii($proxies, []); }, ParametersProvider::class => [ 'class' => ParametersProvider::class, diff --git a/config/routes.php b/config/routes.php index b457c242..2f5b8dd9 100644 --- a/config/routes.php +++ b/config/routes.php @@ -10,6 +10,7 @@ use Psr\Http\Message\ResponseFactoryInterface; use Yiisoft\Csrf\CsrfMiddleware; use Yiisoft\DataResponse\Middleware\FormatDataResponseAsJson; +use Yiisoft\RequestProvider\RequestCatcherMiddleware; use Yiisoft\Router\Group; use Yiisoft\Router\Route; use Yiisoft\Validator\ValidatorInterface; @@ -47,14 +48,17 @@ static function (ResponseFactoryInterface $responseFactory, ValidatorInterface $ ->name('generator'), Route::post('/{generator}/preview') ->middleware(BodyParamsMiddleware::class) + ->middleware(RequestCatcherMiddleware::class) ->action([DefaultController::class, 'preview']) ->name('preview'), Route::post('/{generator}/generate') ->middleware(BodyParamsMiddleware::class) + ->middleware(RequestCatcherMiddleware::class) ->action([DefaultController::class, 'generate']) ->name('generate'), Route::post('/{generator}/diff') ->middleware(BodyParamsMiddleware::class) + ->middleware(RequestCatcherMiddleware::class) ->action([DefaultController::class, 'diff']) ->name('diff') ) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4ce94439..691f57cb 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -6,6 +6,7 @@ colors="true" failOnRisky="true" failOnWarning="true" + displayDetailsOnTestsThatTriggerWarnings="true" stopOnFailure="false" executionOrder="random" resolveDependencies="true" diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index 24ff60d0..cce96e35 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -20,6 +20,7 @@ use Yiisoft\Yii\Gii\Generator\CommandHydrator; use Yiisoft\Yii\Gii\GeneratorCommandInterface; use Yiisoft\Yii\Gii\GeneratorInterface; +use Yiisoft\Yii\Gii\GeneratorProxy; use Yiisoft\Yii\Gii\GiiInterface; use Yiisoft\Yii\Gii\ParametersProvider; use Yiisoft\Yii\Gii\Request\GeneratorRequest; @@ -37,7 +38,17 @@ public function list(GiiInterface $gii): ResponseInterface $generators = $gii->getGenerators(); return $this->responseFactory->createResponse([ - 'generators' => array_map($this->serializeGenerator(...), array_values($generators)), + 'generators' => array_map( + $this->serializeGenerator(...), + array_values( + array_map( + fn (GeneratorInterface|GeneratorProxy $generator) => $generator instanceof GeneratorProxy + ? $generator->getClass() + : $generator::class, + $generators + ) + ), + ), ]); } @@ -46,7 +57,7 @@ public function get(GeneratorRequest $request): ResponseInterface $generator = $request->getGenerator(); return $this->responseFactory->createResponse( - $this->serializeGenerator($generator) + $this->serializeGenerator($generator::class) ); } @@ -150,12 +161,12 @@ private function serializeCodeFile(CodeFile $file): array ]; } - private function serializeGenerator(GeneratorInterface $generator): array + /** + * @psalm-param class-string $generatorClass + */ + private function serializeGenerator(string $generatorClass): array { - /** - * @psalm-var class-string $commandClass - */ - $commandClass = $generator::getCommandClass(); + $commandClass = $generatorClass::getCommandClass(); $dataset = new AttributesRulesProvider($commandClass); $rules = $dataset->getRules(); @@ -182,12 +193,12 @@ private function serializeGenerator(GeneratorInterface $generator): array } return [ - 'id' => $generator::getId(), - 'name' => $generator::getName(), - 'description' => $generator::getDescription(), + 'id' => $generatorClass::getId(), + 'name' => $generatorClass::getName(), + 'description' => $generatorClass::getDescription(), 'commandClass' => $commandClass, 'attributes' => $attributesResult, - 'templates' => $this->parametersProvider->getTemplates($generator::getId()), + 'templates' => $this->parametersProvider->getTemplates($generatorClass::getId()), ]; } diff --git a/src/GeneratorProxy.php b/src/GeneratorProxy.php new file mode 100644 index 00000000..2ceec9ab --- /dev/null +++ b/src/GeneratorProxy.php @@ -0,0 +1,32 @@ + $class + */ + public function __construct(private readonly Closure $loader, private readonly string $class) + { + } + + /** + * @return class-string + */ + public function getClass(): string + { + return $this->class; + } + + public function loadGenerator(): GeneratorInterface + { + return $this->generator ??= ($this->loader)(); + } +} diff --git a/src/Gii.php b/src/Gii.php index 002b8a98..755feec1 100644 --- a/src/Gii.php +++ b/src/Gii.php @@ -4,40 +4,37 @@ namespace Yiisoft\Yii\Gii; -use Closure; use Yiisoft\Yii\Gii\Exception\GeneratorNotFoundException; -/** - * @psalm-import-type LazyGenerator from GiiInterface - */ final class Gii implements GiiInterface { /** - * @param array $generators + * @param array $proxies + * @param array $instances */ - public function __construct(private array $generators) - { + public function __construct( + private readonly array $proxies, + private array $instances, + ) { } public function addGenerator(GeneratorInterface $generator): void { - $this->generators[$generator::getId()] = $generator; + $this->instances[$generator::getId()] = $generator; } public function getGenerator(string $id): GeneratorInterface { - if (!isset($this->generators[$id])) { - throw new GeneratorNotFoundException('Generator "' . $id . '" not found'); - } - - return $this->generators[$id] instanceof Closure ? $this->generators[$id]() : $this->generators[$id]; + return $this->instances[$id] ?? (isset($this->proxies[$id]) + ? $this->proxies[$id]->loadGenerator() + : throw new GeneratorNotFoundException('Generator "' . $id . '" not found')); } public function getGenerators(): array { - return array_map( - fn (Closure|GeneratorInterface $generator) => $generator instanceof Closure ? $generator() : $generator, - $this->generators - ); + return [ + ...$this->instances, + ...$this->proxies, + ]; } } diff --git a/src/GiiInterface.php b/src/GiiInterface.php index 614435d3..76d6fa4e 100644 --- a/src/GiiInterface.php +++ b/src/GiiInterface.php @@ -4,12 +4,8 @@ namespace Yiisoft\Yii\Gii; -use Closure; use Yiisoft\Yii\Gii\Exception\GeneratorNotFoundException; -/** - * @psalm-type LazyGenerator = Closure(): GeneratorInterface - */ interface GiiInterface { /** @@ -23,7 +19,7 @@ public function addGenerator(GeneratorInterface $generator): void; public function getGenerator(string $id): GeneratorInterface; /** - * @return GeneratorInterface[] + * @return GeneratorInterface[]|GeneratorProxy[] */ public function getGenerators(): array; } diff --git a/src/Request/GeneratorRequest.php b/src/Request/GeneratorRequest.php index e63568a0..e6adeff2 100644 --- a/src/Request/GeneratorRequest.php +++ b/src/Request/GeneratorRequest.php @@ -4,9 +4,9 @@ namespace Yiisoft\Yii\Gii\Request; -use Yiisoft\Hydrator\Temp\RouteArgument; use Yiisoft\Input\Http\Attribute\Parameter\Body; use Yiisoft\Input\Http\RequestInputInterface; +use Yiisoft\Router\HydratorAttribute\RouteArgument; use Yiisoft\Yii\Gii\GeneratorInterface; use Yiisoft\Yii\Gii\GiiInterface; diff --git a/src/Validator/TemplateRuleHandler.php b/src/Validator/TemplateRuleHandler.php index b779f6ea..ab5ad734 100644 --- a/src/Validator/TemplateRuleHandler.php +++ b/src/Validator/TemplateRuleHandler.php @@ -12,6 +12,7 @@ use Yiisoft\Validator\ValidationContext; use Yiisoft\Yii\Gii\GeneratorCommandInterface; use Yiisoft\Yii\Gii\GeneratorInterface; +use Yiisoft\Yii\Gii\GeneratorProxy; use Yiisoft\Yii\Gii\GiiInterface; use Yiisoft\Yii\Gii\ParametersProvider; @@ -79,9 +80,12 @@ public function validate(mixed $value, object $rule, ValidationContext $context) private function getGenerator(GeneratorCommandInterface $dataSet): GeneratorInterface { foreach ($this->gii->getGenerators() as $generator) { - if ($generator::getCommandClass() === $dataSet::class) { + if ($generator instanceof GeneratorInterface && $generator::getCommandClass() === $dataSet::class) { return $generator; } + if ($generator instanceof GeneratorProxy && $generator->getClass()::getCommandClass() === $dataSet::class) { + return $generator->loadGenerator(); + } } throw new RuntimeException(sprintf('Unknown generator "%s".', $dataSet::class)); } diff --git a/tests/TestCase.php b/tests/TestCase.php index 9dfc3aa5..35019d00 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -28,6 +28,7 @@ use Yiisoft\Validator\Validator; use Yiisoft\Validator\ValidatorInterface; use Yiisoft\Yii\Gii\Generator as Generators; +use Yiisoft\Yii\Gii\GeneratorProxy; use Yiisoft\Yii\Gii\Gii; use Yiisoft\Yii\Gii\GiiInterface; @@ -54,15 +55,16 @@ protected function getContainer(array $definitions = []): ContainerInterface $config = ContainerConfig::create() ->withDefinitions([ GiiInterface::class => function (ContainerInterface $container) { - $generators = [ - Generators\Controller\Generator::getId() => Generators\Controller\Generator::class, - Generators\ActiveRecord\Generator::getId() => Generators\ActiveRecord\Generator::class, + $proxies = [ + Generators\Controller\Generator::getId() => new GeneratorProxy( + fn() => $container->get(Generators\Controller\Generator::class), + Generators\Controller\Generator::class, + ), ]; - $generatorsInstances = []; - foreach ($generators as $class) { - $generatorsInstances[] = $container->get($class); - } - return new Gii($generatorsInstances); + $instances = [ + Generators\ActiveRecord\Generator::getId() => $container->get(Generators\ActiveRecord\Generator::class), + ]; + return new Gii($proxies, $instances); }, Aliases::class => new Aliases( [