Skip to content

Commit

Permalink
Merge pull request #1149: Container scope fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
roxblnfk authored Sep 23, 2024
2 parents 1aa287a + 85636f7 commit 905f06c
Show file tree
Hide file tree
Showing 22 changed files with 283 additions and 56 deletions.
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
# CHANGELOG

## 3.14.4 - 2024-09-23

- **Bug Fixes**
- [spiral/router] Router now uses proxied container to create middlewares in a right scope.
- [spiral/router] Better binding for the interceptor handler.
- `DebugBootloader` now uses a Factory Proxy to resolve collectors.
Unresolved collectors don't break state populating flow.

## 3.14.3 - 2024-09-11

- **Bug Fixes**
- [spiral/core] Improved introspecting of Container when a Container Proxy is provided into the `Introspector`.
- [spiral/http] Improved exception message when Input Manager can't get a Request in because of wrong scope.
- `GuardScope` has been deprecated. Use `GuardInterface` directly instead.

## 3.14.2 - 2024-09-10

- **Bug Fixes**
- [spiral/core] Added a proxy recursion detection a dependency on resolving: a `RecursiveProxyException` will be
thrown in this case.
- [spiral/boot] Fixed concurrent writing and reading cached data on workers boot.
- Increased code quality by Rector.

## 3.14.1 - 2024-09-09

- **Bug Fixes**
- [spiral/router] Fixed fallback interceptors handler in `AbstractTarget`.
- Increased code quality by Rector.

## 3.14.0 - 2024-09-03

- **High Impact Changes**
- Scopes
- Deeper integration with Container Scopes: contextual scopes are created inside dispatcher scopes.
- Interceptors
- Added a new package `spiral/interceptors`.
- `spira/hmvc` is deprecated now.

## 3.13.0 - 2024-05-22

- **Other Features**
Expand Down
9 changes: 9 additions & 0 deletions src/Bridge/Monolog/tests/ListenersTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Spiral\Tests\Monolog;

use Monolog\Logger;
use PHPUnit\Framework\Attributes\DoesNotPerformAssertions;
use PHPUnit\Framework\TestCase;
use Psr\Log\LogLevel;
use Spiral\Core\Container;
Expand Down Expand Up @@ -78,4 +79,12 @@ public function testListenError(): void
$other->alert('alert', ['context']);
$this->assertCount(1, $records);
}

#[DoesNotPerformAssertions]
public function testRemoveNotExistingListener(): void
{
$registry = new ListenerRegistry();

$registry->removeListener(static fn (LogEvent $e) => null);
}
}
1 change: 0 additions & 1 deletion src/Core/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
"require": {
"php": ">=8.1",
"psr/container": "^1.1|^2.0",
"spiral/core": "^3.15",
"spiral/security": "^3.15"
},
"require-dev": {
Expand Down
2 changes: 1 addition & 1 deletion src/Core/phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
<phpunit bootstrap="tests/bootstrap.php"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
Expand Down
13 changes: 13 additions & 0 deletions src/Core/tests/bootstrap.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Spiral\Framework;

require_once __DIR__ . '/../vendor/autoload.php';

if (!\enum_exists(Spiral::class)) {
enum Spiral: string {
case HttpRequest = 'http-request';
}
}
7 changes: 5 additions & 2 deletions src/Debug/src/State.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,21 @@ final class State implements StateInterface
private array $extras = [];
private array $logEvents = [];

/**
* @param array<array-key, string> $tags
*/
public function setTags(array $tags): void
{
$setTags = [];
foreach ($tags as $key => $value) {
if (!\is_string($value)) {
throw new StateException(\sprintf(
'Invalid tag value, string expected got %s',
get_debug_type($value)
\get_debug_type($value)
));
}

$setTags[(string)$key] = $value;
$setTags[$key] = $value;
}

$this->tags = $setTags;
Expand Down
3 changes: 3 additions & 0 deletions src/Debug/src/StateInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

interface StateInterface
{
/**
* @param array<array-key, string> $tags
*/
public function setTags(array $tags): void;

public function setTag(string $key, string $value): void;
Expand Down
45 changes: 30 additions & 15 deletions src/Framework/Bootloader/DebugBootloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Config\ConfiguratorInterface;
use Spiral\Config\Patch\Append;
use Spiral\Core\Attribute\Proxy;
use Spiral\Core\Attribute\Singleton;
use Spiral\Core\Container\Autowire;
use Spiral\Core\FactoryInterface;
Expand Down Expand Up @@ -34,7 +35,6 @@ final class DebugBootloader extends Bootloader
];

public function __construct(
private readonly FactoryInterface $factory,
private readonly InvokerInterface $invoker,
private readonly ConfiguratorInterface $config,
) {
Expand Down Expand Up @@ -69,8 +69,10 @@ public function addStateCollector(string|StateCollectorInterface|Autowire $colle
/**
* Create state and populate it with collectors.
*/
private function state(DebugConfig $config): StateInterface
{
private function state(
#[Proxy] FactoryInterface $factory,
DebugConfig $config,
): StateInterface {
$state = new State();

foreach ($config->getTags() as $key => $value) {
Expand All @@ -79,34 +81,47 @@ private function state(DebugConfig $config): StateInterface
}

if (!\is_string($value) && !$value instanceof \Stringable) {
throw new StateException(\sprintf(
'Invalid tag value, `string` expected got `%s`',
\is_object($value) ? $value::class : \gettype($value)
));
throw new StateException(
\sprintf(
'Invalid tag value, `string` expected got `%s`',
\is_object($value) ? $value::class : \gettype($value),
),
);
}

$state->setTag((string) $key, (string) $value);
$state->setTag((string)$key, (string)$value);
}

$errors = [];

foreach ($config->getCollectors() as $collector) {
$collector = match (true) {
\is_string($collector) => $this->factory->make($collector),
$collector instanceof Autowire => $collector->resolve($this->factory),
default => $collector,
};
try {
$collector = match (true) {
\is_string($collector) => $factory->make($collector),
$collector instanceof Autowire => $collector->resolve($factory),
default => $collector,
};
} catch (\Throwable) {
$errors[] = \is_string($collector) || $collector instanceof \Stringable
? (string) $collector
: \get_debug_type($collector);
continue;
}

if (!$collector instanceof StateCollectorInterface) {
throw new StateException(
\sprintf(
'Unable to populate state, invalid state collector %s',
\is_object($collector) ? $collector::class : \gettype($collector)
)
\is_object($collector) ? $collector::class : \gettype($collector),
),
);
}

$collector->populate($state);
}

$errors === [] or $state->setTags(['unresolved-collectors' => \implode(', ', $errors)]);

return $state;
}

Expand Down
9 changes: 8 additions & 1 deletion src/Framework/Bootloader/Http/RouterBootloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function defineSingletons(): array
->bindSingleton(RouteInterface::class, [self::class, 'route']);

return [
HandlerInterface::class => Core::class,
HandlerInterface::class => [self::class, 'handler'],
CoreInterface::class => Core::class,
RouterInterface::class => [self::class, 'router'],
RequestHandlerInterface::class => RouterInterface::class,
Expand Down Expand Up @@ -107,6 +107,13 @@ private function router(
);
}

private function handler(?CoreInterface $core, ContainerInterface $container): HandlerInterface
{
return $core instanceof HandlerInterface
? $core
: $container->get(Core::class);
}

/**
* @noRector RemoveUnusedPrivateMethodRector
*/
Expand Down
9 changes: 4 additions & 5 deletions src/Logger/src/ListenerRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@

namespace Spiral\Logger;

use Spiral\Logger\Event\LogEvent;

/**
* Contains all log listeners.
*/
final class ListenerRegistry implements ListenerRegistryInterface
{
/** @var callable[] */
/** @var array<int, callable(LogEvent): void> */
private array $listeners = [];

public function addListener(callable $listener): self
Expand All @@ -24,14 +26,11 @@ public function addListener(callable $listener): self
public function removeListener(callable $listener): void
{
$key = \array_search($listener, $this->listeners, true);
if ($key !== null) {
if ($key !== false) {
unset($this->listeners[$key]);
}
}

/**
* @return callable[]
*/
public function getListeners(): array
{
return $this->listeners;
Expand Down
13 changes: 12 additions & 1 deletion src/Logger/src/ListenerRegistryInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,31 @@

namespace Spiral\Logger;

use Spiral\Logger\Event\LogEvent;

/**
* Registry for log event listeners.
*
* When log event is triggered, all listeners will be executed.
*/
interface ListenerRegistryInterface
{
/**
* Add new even listener.
*
* @param callable(LogEvent): void $listener
*/
public function addListener(callable $listener): self;

/**
* Add LogEvent listener.
*
* @param callable(LogEvent): void $listener
*/
public function removeListener(callable $listener): void;

/**
* @return callable[]
* @return array<callable(LogEvent): void>
*/
public function getListeners(): array;
}
3 changes: 2 additions & 1 deletion src/Router/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
"require-dev": {
"phpunit/phpunit": "^10.1",
"mockery/mockery": "^1.5",
"nyholm/psr7": "^1.5",
"spiral/nyholm-bridge": "^1.3",
"spiral/testing": "^2.8",
"vimeo/psalm": "^5.9"
},
"autoload": {
Expand Down
1 change: 0 additions & 1 deletion src/Router/src/CoreHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Spiral\Core\CoreInterface;
use Spiral\Core\Exception\ControllerException;
use Spiral\Core\Scope;
use Spiral\Core\ScopeInterface;
use Spiral\Http\Exception\ClientException;
Expand Down
5 changes: 4 additions & 1 deletion src/Router/src/Target/AbstractTarget.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Server\RequestHandlerInterface as Handler;
use Spiral\Core\CoreInterface;
use Spiral\Core\Internal\Proxy;
use Spiral\Core\ScopeInterface;
use Spiral\Interceptors\Handler\AutowireHandler;
use Spiral\Interceptors\HandlerInterface;
Expand Down Expand Up @@ -89,6 +90,8 @@ protected function coreHandler(ContainerInterface $container): CoreHandler
return $this->handler;
}

$scope = Proxy::create(new \ReflectionClass(ScopeInterface::class), null, new \Spiral\Core\Attribute\Proxy());

try {
// construct on demand
$this->handler = new CoreHandler(
Expand All @@ -97,7 +100,7 @@ protected function coreHandler(ContainerInterface $container): CoreHandler
$container->has(HandlerInterface::class) => new AutowireHandler($container),
default => $container->get(HandlerInterface::class),
},
$container->get(ScopeInterface::class),
$scope,
$container->get(ResponseFactoryInterface::class),
$container->get(TracerInterface::class)
);
Expand Down
46 changes: 46 additions & 0 deletions src/Router/tests/ContainerScopeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

declare(strict_types=1);

namespace Spiral\Tests\Router;

use Spiral\Bootloader\Http\RouterBootloader;
use Spiral\Nyholm\Bootloader\NyholmBootloader;
use Spiral\Router\Route;
use Spiral\Router\RouterInterface;
use Spiral\Router\Target\Group;
use Spiral\Testing\Attribute\TestScope;
use Spiral\Tests\Router\Fixtures\TestController;
use Spiral\Tests\Router\Stub\IdentityScopedMiddleware;

class ContainerScopeTest extends \Spiral\Testing\TestCase
{
public function defineBootloaders(): array
{
return [
RouterBootloader::class,
NyholmBootloader::class,
];
}

#[TestScope('http')]
public function testRunOpenScopeSameTwice(): void
{
$router = $this->getRouter();

$router->setRoute(
'group',
(new Route('/<controller>[/<action>[/<id>]]', new Group([
'test' => TestController::class,
])))->withMiddleware(IdentityScopedMiddleware::class),
);

$this->fakeHttp()->get('/test/scopes')->assertBodySame('http-request, idenity, http, root');
$this->fakeHttp()->get('/test/scopes')->assertBodySame('http-request, idenity, http, root');
}

private function getRouter(): RouterInterface
{
return $this->getContainer()->get(RouterInterface::class);
}
}
Loading

0 comments on commit 905f06c

Please sign in to comment.