Skip to content

Commit

Permalink
Merge pull request #870 from spiral/feature/core-scopes: container wi…
Browse files Browse the repository at this point in the history
…th isolated scopes
  • Loading branch information
roxblnfk committed Feb 8, 2023
2 parents cb64a00 + 5d8bd38 commit 36f795d
Show file tree
Hide file tree
Showing 59 changed files with 2,012 additions and 260 deletions.
17 changes: 9 additions & 8 deletions src/Cookies/tests/CookiesTest.php
Expand Up @@ -12,6 +12,7 @@
use Spiral\Cookies\CookieQueue;
use Spiral\Cookies\Middleware\CookiesMiddleware;
use Spiral\Core\Container;
use Spiral\Core\ContainerScope;
use Spiral\Encrypter\Config\EncrypterConfig;
use Spiral\Encrypter\Encrypter;
use Spiral\Encrypter\EncrypterFactory;
Expand Down Expand Up @@ -55,12 +56,12 @@ public function testScope(): void
$core->setHandler(function ($r) {
$this->assertInstanceOf(
CookieQueue::class,
$this->container->get(ServerRequestInterface::class)
ContainerScope::getContainer()->get(ServerRequestInterface::class)
->getAttribute(CookieQueue::ATTRIBUTE)
);

$this->assertSame(
$this->container->get(ServerRequestInterface::class)
ContainerScope::getContainer()->get(ServerRequestInterface::class)
->getAttribute(CookieQueue::ATTRIBUTE),
$r->getAttribute(CookieQueue::ATTRIBUTE)
);
Expand All @@ -77,7 +78,7 @@ public function testSetEncryptedCookie(): void
{
$core = $this->httpCore([CookiesMiddleware::class]);
$core->setHandler(function ($r) {
$this->container->get(ServerRequestInterface::class)
ContainerScope::getContainer()->get(ServerRequestInterface::class)
->getAttribute(CookieQueue::ATTRIBUTE)->set('name', 'value');

return 'all good';
Expand All @@ -99,7 +100,7 @@ public function testSetNotProtectedCookie(): void
{
$core = $this->httpCore([CookiesMiddleware::class]);
$core->setHandler(function ($r) {
$this->container->get(ServerRequestInterface::class)
ContainerScope::getContainer()->get(ServerRequestInterface::class)
->getAttribute(CookieQueue::ATTRIBUTE)->set('PHPSESSID', 'value');

return 'all good';
Expand Down Expand Up @@ -172,10 +173,10 @@ public function testDelete(): void
{
$core = $this->httpCore([CookiesMiddleware::class]);
$core->setHandler(function ($r) {
$this->container->get(ServerRequestInterface::class)
ContainerScope::getContainer()->get(ServerRequestInterface::class)
->getAttribute(CookieQueue::ATTRIBUTE)->set('name', 'value');

$this->container->get(ServerRequestInterface::class)
ContainerScope::getContainer()->get(ServerRequestInterface::class)
->getAttribute(CookieQueue::ATTRIBUTE)->delete('name');

return 'all good';
Expand All @@ -200,7 +201,7 @@ public function testUnprotected(): void

$core = $this->httpCore([CookiesMiddleware::class]);
$core->setHandler(function ($r) {
$this->container->get(ServerRequestInterface::class)
ContainerScope::getContainer()->get(ServerRequestInterface::class)
->getAttribute(CookieQueue::ATTRIBUTE)->set('name', 'value');

return 'all good';
Expand Down Expand Up @@ -249,7 +250,7 @@ public function testHMAC(): void

$core = $this->httpCore([CookiesMiddleware::class]);
$core->setHandler(function ($r) {
$this->container->get(ServerRequestInterface::class)
ContainerScope::getContainer()->get(ServerRequestInterface::class)
->getAttribute(CookieQueue::ATTRIBUTE)->set('name', 'value');

return 'all good';
Expand Down
3 changes: 2 additions & 1 deletion src/Core/psalm.xml
Expand Up @@ -3,14 +3,15 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"

errorLevel="4"
hoistConstants="true"
resolveFromConfigFile="true"
findUnusedPsalmSuppress="true"
findUnusedVariablesAndParams="true"
ensureArrayStringOffsetsExist="true"
addParamDefaultToDocblockType="true"
findUnusedBaselineEntry="true"
findUnusedCode="false"
>
<projectFiles>
<directory name="src" />
Expand Down
19 changes: 19 additions & 0 deletions src/Core/src/Attribute/Finalize.php
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Spiral\Core\Attribute;

/**
* Define a finalize method for the class.
*
* @internal We are testing this feature, it may be changed in the future.
*/
#[\Attribute(\Attribute::TARGET_CLASS)]
final class Finalize
{
public function __construct(
public string $method,
) {
}
}
19 changes: 19 additions & 0 deletions src/Core/src/Attribute/Scope.php
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace Spiral\Core\Attribute;

/**
* Set a scope in which the dependency can be resolved.
*
* @internal We are testing this feature, it may be changed in the future.
*/
#[\Attribute(\Attribute::TARGET_CLASS)]
final class Scope
{
public function __construct(
public string $name,
) {
}
}
13 changes: 13 additions & 0 deletions src/Core/src/Attribute/Singleton.php
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Spiral\Core\Attribute;

/**
* Mark class as singleton.
*/
#[\Attribute(\Attribute::TARGET_CLASS)]
final class Singleton
{
}
2 changes: 1 addition & 1 deletion src/Core/src/BinderInterface.php
Expand Up @@ -18,7 +18,7 @@ interface BinderInterface
* every method call), function array or Closure (executed every call). Only object resolvers
* supported by this method.
*
* @psalm-param TResolver $resolver
* @param TResolver $resolver
*/
public function bind(string $alias, string|array|callable|object $resolver): void;

Expand Down
31 changes: 29 additions & 2 deletions src/Core/src/Config.php
Expand Up @@ -7,22 +7,32 @@
use IteratorAggregate;
use Psr\Container\ContainerInterface;
use Spiral\Core\Internal\Binder;
use Spiral\Core\Internal\Container;
use Spiral\Core\Internal\Factory;
use Spiral\Core\Internal\Invoker;
use Spiral\Core\Internal\Resolver;
use Spiral\Core\Internal\Scope;
use Spiral\Core\Internal\State;
use Spiral\Core\Internal\Container;
use Spiral\Core\Internal\Tracer;
use Traversable;

/**
* Container configuration that will be used not only in the root container but also in all child containers.
* The {@see self::$scopedBindings} property is internal and common for all containers.
* By the reason you can access to bindings for any scope from any container.
*
* @implements IteratorAggregate<
* non-empty-string,
* class-string<State>|class-string<ResolverInterface>|class-string<FactoryInterface>|class-string<ContainerInterface>|class-string<BinderInterface>|class-string<InvokerInterface>|class-string<Tracer>
* class-string<State>|class-string<ResolverInterface>|class-string<FactoryInterface>|class-string<ContainerInterface>|class-string<BinderInterface>|class-string<InvokerInterface>|class-string<Tracer>|class-string<Scope>
* >
*/
class Config implements IteratorAggregate
{
/** @var class-string<Scope> */
public readonly string $scope;
public readonly Internal\Config\StateStorage $scopedBindings;
private bool $rootLocked = true;

/**
* @param class-string<State> $state
* @param class-string<ResolverInterface> $resolver
Expand All @@ -41,6 +51,8 @@ public function __construct(
public readonly string $invoker = Invoker::class,
public readonly string $tracer = Tracer::class,
) {
$this->scope = Scope::class;
$this->scopedBindings = new Internal\Config\StateStorage();
}

public function getIterator(): Traversable
Expand All @@ -52,5 +64,20 @@ public function getIterator(): Traversable
yield 'binder' => $this->binder;
yield 'invoker' => $this->invoker;
yield 'tracer' => $this->tracer;
yield 'scope' => $this->scope;
}

/**
* Mutex lock for root container.
* First run of the method will return {@see true}, all subsequent calls will return {@see false}.
* The parent container must call the method once and before any child container.
*/
public function lockRoot(): bool
{
try {
return $this->rootLocked;
} finally {
$this->rootLocked = false;
}
}
}

0 comments on commit 36f795d

Please sign in to comment.