Skip to content

Commit

Permalink
Merge pull request #1017 from spiral/feature/bootloader-rules
Browse files Browse the repository at this point in the history
[spiral/boot] Added the ability to configure bootloaders via BootloadConfig
  • Loading branch information
butschster authored Nov 23, 2023
2 parents e09951d + 2d2886f commit 441f912
Show file tree
Hide file tree
Showing 34 changed files with 1,123 additions and 98 deletions.
3 changes: 2 additions & 1 deletion src/Boot/src/AbstractKernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ public function run(?EnvironmentInterface $environment = null): ?self
function (Container $container): void {
$registry = $container->get(BootloaderRegistryInterface::class);

$this->bootloader->bootload($registry->getSystemBootloaders());
/** @psalm-suppress TooManyArguments */
$this->bootloader->bootload($registry->getSystemBootloaders(), [], [], false);
$this->fireCallbacks($this->runningCallbacks);

$this->bootload($registry->getBootloaders());
Expand Down
20 changes: 20 additions & 0 deletions src/Boot/src/Attribute/BootloadConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Spiral\Boot\Attribute;

use Spiral\Attributes\NamedArgumentConstructor;

#[\Attribute(\Attribute::TARGET_CLASS), NamedArgumentConstructor]
class BootloadConfig
{
public function __construct(
public array $args = [],
public bool $enabled = true,
public array $allowEnv = [],
public array $denyEnv = [],
public bool $override = true,
) {
}
}
13 changes: 9 additions & 4 deletions src/Boot/src/BootloadManager/AbstractBootloadManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,17 @@ public function getClasses(): array
return $this->initializer->getRegistry()->getClasses();
}

public function bootload(array $classes, array $bootingCallbacks = [], array $bootedCallbacks = []): void
{
public function bootload(
array $classes,
array $bootingCallbacks = [],
array $bootedCallbacks = [],
bool $useConfig = true
): void {
$this->scope->runScope(
[self::class => $this],
function () use ($classes, $bootingCallbacks, $bootedCallbacks): void {
$this->boot($classes, $bootingCallbacks, $bootedCallbacks);
function () use ($classes, $bootingCallbacks, $bootedCallbacks, $useConfig): void {
/** @psalm-suppress TooManyArguments */
$this->boot($classes, $bootingCallbacks, $bootedCallbacks, $useConfig);
}
);
}
Expand Down
27 changes: 27 additions & 0 deletions src/Boot/src/BootloadManager/Checker/BootloaderChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Spiral\Boot\BootloadManager\Checker;

use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Boot\Bootloader\BootloaderInterface;

final class BootloaderChecker implements BootloaderCheckerInterface
{
public function __construct(
private readonly CheckerRegistryInterface $registry = new CheckerRegistry(),
) {
}

public function canInitialize(BootloaderInterface|string $bootloader, ?BootloadConfig $config = null): bool
{
foreach ($this->registry->getCheckers() as $checker) {
if (!$checker->canInitialize($bootloader, $config)) {
return false;
}
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Spiral\Boot\BootloadManager\Checker;

use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Boot\Bootloader\BootloaderInterface;

interface BootloaderCheckerInterface
{
/**
* @param class-string<BootloaderInterface>|BootloaderInterface $bootloader
*/
public function canInitialize(string|BootloaderInterface $bootloader, ?BootloadConfig $config = null): bool;
}
27 changes: 27 additions & 0 deletions src/Boot/src/BootloadManager/Checker/CanBootedChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Spiral\Boot\BootloadManager\Checker;

use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Boot\Bootloader\BootloaderInterface;
use Spiral\Boot\BootloadManager\ClassesRegistry;

final class CanBootedChecker implements BootloaderCheckerInterface
{
public function __construct(
private readonly ClassesRegistry $bootloaders,
) {
}

public function canInitialize(BootloaderInterface|string $bootloader, ?BootloadConfig $config = null): bool
{
$ref = new \ReflectionClass($bootloader);

return !$this->bootloaders->isBooted($ref->getName())
&& !$ref->isAbstract()
&& !$ref->isInterface()
&& $ref->implementsInterface(BootloaderInterface::class);
}
}
26 changes: 26 additions & 0 deletions src/Boot/src/BootloadManager/Checker/CheckerRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace Spiral\Boot\BootloadManager\Checker;

final class CheckerRegistry implements CheckerRegistryInterface
{
/**
* @var array<BootloaderCheckerInterface>
*/
private array $checkers = [];

public function register(BootloaderCheckerInterface $checker): void
{
$this->checkers[] = $checker;
}

/**
* @return array<BootloaderCheckerInterface>
*/
public function getCheckers(): array
{
return $this->checkers;
}
}
15 changes: 15 additions & 0 deletions src/Boot/src/BootloadManager/Checker/CheckerRegistryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Spiral\Boot\BootloadManager\Checker;

interface CheckerRegistryInterface
{
public function register(BootloaderCheckerInterface $checker): void;

/**
* @return array<BootloaderCheckerInterface>
*/
public function getCheckers(): array;
}
28 changes: 28 additions & 0 deletions src/Boot/src/BootloadManager/Checker/ClassExistsChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Spiral\Boot\BootloadManager\Checker;

use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Boot\Bootloader\BootloaderInterface;
use Spiral\Boot\Exception\ClassNotFoundException;

final class ClassExistsChecker implements BootloaderCheckerInterface
{
/**
* @throws ClassNotFoundException
*/
public function canInitialize(BootloaderInterface|string $bootloader, ?BootloadConfig $config = null): bool
{
if (!\is_string($bootloader)) {
return true;
}

if (!\class_exists($bootloader)) {
throw new ClassNotFoundException(\sprintf('Bootloader class `%s` is not exist.', $bootloader));
}

return true;
}
}
44 changes: 44 additions & 0 deletions src/Boot/src/BootloadManager/Checker/ConfigChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Spiral\Boot\BootloadManager\Checker;

use Spiral\Boot\Attribute\BootloadConfig;
use Spiral\Boot\Bootloader\BootloaderInterface;
use Spiral\Boot\EnvironmentInterface;

final class ConfigChecker implements BootloaderCheckerInterface
{
public function __construct(
private readonly EnvironmentInterface $environment,
) {
}

public function canInitialize(BootloaderInterface|string $bootloader, ?BootloadConfig $config = null): bool
{
if ($config === null) {
return true;
}

if (!$config->enabled) {
return false;
}

foreach ($config->denyEnv as $env => $denyValues) {
$value = $this->environment->get($env);
if ($value !== null && \in_array($value, (array) $denyValues, true)) {
return false;
}
}

foreach ($config->allowEnv as $env => $allowValues) {
$value = $this->environment->get($env);
if ($value === null || !\in_array($value, (array) $allowValues, true)) {
return false;
}
}

return true;
}
}
11 changes: 8 additions & 3 deletions src/Boot/src/BootloadManager/DefaultInvokerStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ public function __construct(
) {
}

public function invokeBootloaders(array $classes, array $bootingCallbacks, array $bootedCallbacks): void
{
$bootloaders = \iterator_to_array($this->initializer->init($classes));
public function invokeBootloaders(
array $classes,
array $bootingCallbacks,
array $bootedCallbacks,
bool $useConfig = true
): void {
/** @psalm-suppress TooManyArguments */
$bootloaders = \iterator_to_array($this->initializer->init($classes, $useConfig));

foreach ($bootloaders as $data) {
$this->invokeBootloader($data['bootloader'], Methods::INIT, $data['options']);
Expand Down
Loading

0 comments on commit 441f912

Please sign in to comment.