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

[spiral/prototype] Initialize PrototypeRegistry only when registry requires from container #1005

Merged
merged 5 commits into from
Oct 19, 2023
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ Thumbs.db
clover.xml
.env
builds
build
4 changes: 3 additions & 1 deletion src/Prototype/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
"ext-json": "*",
"nikic/php-parser": "^4.15.5",
"doctrine/inflector": "^1.4|^2.0",
"spiral/attributes": "^2.8|^3.0"
"spiral/attributes": "^2.8|^3.0",
"spiral/config": "^3.9",
"spiral/reactor": "^3.9"
},
"autoload": {
"psr-4": {
Expand Down
2 changes: 2 additions & 0 deletions src/Prototype/src/Annotation/Line.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

/**
* Singular annotation line.
*
* @deprecated since v3.9.0
*/
final class Line
{
Expand Down
2 changes: 2 additions & 0 deletions src/Prototype/src/Annotation/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

/**
* Simple annotation parser and compiler.
*
* @deprecated since v3.9.0
*/
final class Parser
{
Expand Down
33 changes: 24 additions & 9 deletions src/Prototype/src/Bootloader/PrototypeBootloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
use Spiral\Boot\Bootloader;
use Spiral\Boot\MemoryInterface;
use Spiral\Bootloader\Attributes\AttributesBootloader;
use Spiral\Config\ConfiguratorInterface;
use Spiral\Config\Patch\Append;
use Spiral\Console\Bootloader\ConsoleBootloader;
use Spiral\Core\Container;
use Spiral\Prototype\Command;
use Spiral\Prototype\Config\PrototypeConfig;
use Spiral\Prototype\PrototypeLocatorListener;
use Spiral\Prototype\PrototypeRegistry;
use Spiral\Tokenizer\Bootloader\TokenizerListenerBootloader;
Expand All @@ -28,6 +31,10 @@ final class PrototypeBootloader extends Bootloader\Bootloader implements Contain
AttributesBootloader::class,
];

protected const SINGLETONS = [
PrototypeRegistry::class => [self::class, 'initRegistry'],
];

// Default spiral specific shortcuts, automatically checked on existence.
private const DEFAULT_SHORTCUTS = [
'app' => ['resolve' => \Spiral\Boot\KernelInterface::class],
Expand Down Expand Up @@ -72,7 +79,7 @@ final class PrototypeBootloader extends Bootloader\Bootloader implements Contain
];

public function __construct(
private readonly PrototypeRegistry $registry
private readonly ConfiguratorInterface $config
) {
}

Expand All @@ -92,26 +99,32 @@ public function init(ConsoleBootloader $console): void
'prototype:dump',
'<fg=magenta>[prototype]</fg=magenta> <fg=cyan>actualizing prototype injections...</fg=cyan>'
);

$this->config->setDefaults(
PrototypeConfig::CONFIG,
[
'bindings' => self::DEFAULT_SHORTCUTS,
]
);
}

public function boot(
ContainerInterface $container,
TokenizerListenerRegistryInterface $listenerRegistry,
PrototypeLocatorListener $listener
): void {
$this->initDefaults($container);

$listenerRegistry->addListener($listener);
}

public function bindProperty(string $property, string $type): void
{
$this->registry->bindProperty($property, $type);
$this->config->modify(PrototypeConfig::CONFIG, new Append('bindings', $property, $type));
}

private function initDefaults(ContainerInterface $container): void
private function initRegistry(PrototypeConfig $config, ContainerInterface $container): PrototypeRegistry
{
foreach (self::DEFAULT_SHORTCUTS as $property => $shortcut) {
$registry = new PrototypeRegistry($container);

foreach ($config->getBindings() as $property => $shortcut) {
if (\is_array($shortcut) && isset($shortcut['resolve'])) {
if (isset($shortcut['with'])) {
// check dependencies
Expand All @@ -125,7 +138,7 @@ private function initDefaults(ContainerInterface $container): void
try {
$target = $container->get($shortcut['resolve']);
if (\is_object($target)) {
$this->bindProperty($property, $target::class);
$registry->bindProperty($property, $target::class);
}
} catch (ContainerExceptionInterface) {
continue;
Expand All @@ -135,8 +148,10 @@ private function initDefaults(ContainerInterface $container): void
}

if (\is_string($shortcut) && (\class_exists($shortcut, true) || \interface_exists($shortcut, true))) {
$this->bindProperty($property, $shortcut);
$registry->bindProperty($property, $shortcut);
}
}

return $registry;
}
}
10 changes: 7 additions & 3 deletions src/Prototype/src/Command/AbstractCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ abstract class AbstractCommand extends Command

public function __construct(
protected readonly PrototypeLocator $locator,
protected readonly NodeExtractor $extractor,
protected readonly PrototypeRegistry $registry
protected readonly NodeExtractor $extractor
) {
parent::__construct();
}
Expand Down Expand Up @@ -48,6 +47,11 @@ protected function getExtractor(): PropertyExtractor
return $this->container->get(PropertyExtractor::class);
}

protected function getRegistry(): PrototypeRegistry
{
return $this->container->get(PrototypeRegistry::class);
}

/**
* @param non-empty-array<array-key, Dependency|Throwable|null> $properties
*/
Expand Down Expand Up @@ -100,7 +104,7 @@ private function readProperties(\ReflectionClass $class): array
$result = [];
foreach ($proto as $name) {
if (!isset($result[$name])) {
$result[$name] = $this->registry->resolveProperty($name);
$result[$name] = $this->getRegistry()->resolveProperty($name);
}
}

Expand Down
51 changes: 17 additions & 34 deletions src/Prototype/src/Command/DumpCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@

namespace Spiral\Prototype\Command;

use Spiral\Prototype\Annotation;
use Spiral\Prototype\Bootloader\PrototypeBootloader;
use Spiral\Prototype\Traits\PrototypeTrait;
use Spiral\Reactor\FileDeclaration;
use Spiral\Reactor\TraitDeclaration;
use Spiral\Reactor\Writer;

final class DumpCommand extends AbstractCommand
{
Expand All @@ -19,36 +20,25 @@
*
* @throws \ReflectionException
*/
public function perform(PrototypeBootloader $prototypeBootloader): int
public function perform(Writer $writer): int
{
$dependencies = $this->registry->getPropertyBindings();
$dependencies = $this->getRegistry()->getPropertyBindings();
if ($dependencies === []) {
$this->writeln('<comment>No prototyped shortcuts found.</comment>');
$this->comment('No prototyped shortcuts found.');

Check warning on line 27 in src/Prototype/src/Command/DumpCommand.php

View check run for this annotation

Codecov / codecov/patch

src/Prototype/src/Command/DumpCommand.php#L27

Added line #L27 was not covered by tests

return self::SUCCESS;
}

$this->write('Updating <fg=yellow>PrototypeTrait</fg=yellow> DOCComment... ');

$trait = new \ReflectionClass(PrototypeTrait::class);
$docComment = $trait->getDocComment();
if ($docComment === false) {
$this->write('<fg=red>DOCComment is missing</fg=red>');

return self::FAILURE;
}

$filename = $trait->getFileName();
$ref = new \ReflectionClass(PrototypeTrait::class);
$file = FileDeclaration::fromReflection($ref);
$trait = $file->getTrait(PrototypeTrait::class);

try {
\file_put_contents(
$filename,
\str_replace(
$docComment,
$this->buildAnnotation($dependencies),
\file_get_contents($filename)
)
);
$this->buildAnnotation($trait, $dependencies);

$writer->write($ref->getFileName(), $file);
} catch (\Throwable $e) {
$this->write('<fg=red>' . $e->getMessage() . "</fg=red>\n");

Expand All @@ -70,21 +60,14 @@
return self::SUCCESS;
}

private function buildAnnotation(array $dependencies): string
private function buildAnnotation(TraitDeclaration $trait, array $dependencies): void
{
$an = new Annotation\Parser('');
$an->lines[] = new Annotation\Line(
'This DocComment is auto-generated, do not edit or commit this file to repository.'
);
$an->lines[] = new Annotation\Line('');
$trait->setComment(null);
$trait->addComment('This DocComment is auto-generated, do not edit or commit this file to repository.');
$trait->addComment('');

foreach ($dependencies as $dependency) {
$an->lines[] = new Annotation\Line(
\sprintf('\\%s $%s', $dependency->type->fullName, $dependency->var),
'property'
);
$trait->addComment(\sprintf('@property \\%s $%s', $dependency->type->fullName, $dependency->var));
}

return $an->compile();
}
}
10 changes: 3 additions & 7 deletions src/Prototype/src/Command/InjectCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Spiral\Prototype\Injector;
use Spiral\Prototype\NodeExtractor;
use Spiral\Prototype\PrototypeLocator;
use Spiral\Prototype\PrototypeRegistry;
use Symfony\Component\Console\Input\InputOption;

final class InjectCommand extends AbstractCommand
Expand All @@ -21,12 +20,9 @@ final class InjectCommand extends AbstractCommand

private readonly Injector $injector;

public function __construct(
PrototypeLocator $locator,
NodeExtractor $extractor,
PrototypeRegistry $registry
) {
parent::__construct($locator, $extractor, $registry);
public function __construct(PrototypeLocator $locator, NodeExtractor $extractor)
{
parent::__construct($locator, $extractor);
$this->injector = new Injector();
}

Expand Down
2 changes: 1 addition & 1 deletion src/Prototype/src/Command/ListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class ListCommand extends AbstractCommand

public function perform(): int
{
$bindings = $this->registry->getPropertyBindings();
$bindings = $this->getRegistry()->getPropertyBindings();
if ($bindings === []) {
$this->comment('No prototype dependencies found.');

Expand Down
32 changes: 32 additions & 0 deletions src/Prototype/src/Config/PrototypeConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Spiral\Prototype\Config;

use Spiral\Core\InjectableConfig;

/**
* @psalm-type TBinding = class-string|array{resolve: class-string, with: array}
*/
final class PrototypeConfig extends InjectableConfig
{
public const CONFIG = 'prototype';

/**
* @var array{
* bindings: array<non-empty-string, TBinding>
* }
*/
protected array $config = [
'bindings' => [],
];

/**
* @return array<non-empty-string, TBinding>
*/
public function getBindings(): array
{
return (array) ($this->config['bindings'] ?? []);
}
}
5 changes: 3 additions & 2 deletions src/Prototype/src/PrototypeLocatorListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use ReflectionClass;
use Spiral\Attributes\ReaderInterface;
use Spiral\Prototype\Annotation\Prototyped;
use Spiral\Prototype\Bootloader\PrototypeBootloader;
use Spiral\Tokenizer\Attribute\TargetAttribute;
use Spiral\Tokenizer\TokenizationListenerInterface;
use Spiral\Tokenizer\Traits\TargetTrait;
Expand All @@ -21,7 +22,7 @@ final class PrototypeLocatorListener implements TokenizationListenerInterface

public function __construct(
private readonly ReaderInterface $reader,
private readonly PrototypeRegistry $registry
private readonly PrototypeBootloader $prototype
) {
}

Expand All @@ -38,7 +39,7 @@ public function listen(ReflectionClass $class): void
public function finalize(): void
{
foreach ($this->attributes as $property => $class) {
$this->registry->bindProperty($property, $class);
$this->prototype->bindProperty($property, $class);
}
}
}
3 changes: 0 additions & 3 deletions src/Prototype/src/Traits/PrototypeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@
use Spiral\Prototype\Exception\PrototypeException;
use Spiral\Prototype\PrototypeRegistry;

/**
* This DocComment is auto-generated, do not edit or commit this file to repository.
*/
trait PrototypeTrait
{
/**
Expand Down
Loading
Loading