Skip to content

Commit

Permalink
Merge pull request #1077: [spiral/core] Added ability to define inter…
Browse files Browse the repository at this point in the history
…face proxy via binding
  • Loading branch information
roxblnfk committed Feb 15, 2024
2 parents 72934fb + 3e5281d commit dfb4061
Show file tree
Hide file tree
Showing 5 changed files with 279 additions and 43 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- **Other Features**
- Added `Spiral\Scaffolder\Command\InfoCommand` console command for getting information about available scaffolder
commands.
- [spiral/core] Added the ability to bind the interface as a proxy via `Spiral\Core\Config\Proxy` or `Spiral\Core\Config\DeprecationProxy`.

## 3.11.1 - 2023-12-29

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

declare(strict_types=1);

namespace Spiral\Core\Config;

/**
* @internal
*/
final class DeprecationProxy extends Proxy
{
/**
* @param class-string $interface
*/
public function __construct(
string $interface,
bool $singleton = false,
private readonly string|\BackedEnum|null $scope = null,
private readonly ?string $version = null,
private readonly ?string $message = null,
) {
if (($scope === null || $version === null) && $message === null) {
throw new \InvalidArgumentException('Scope and version or custom message must be provided.');
}

parent::__construct($interface, $singleton);
}

/**
* @return class-string
*/
public function getInterface(): string
{
$message = $this->message ?? \sprintf(
'Using `%s` outside of the `%s` scope is deprecated and will be impossible in version %s.',
$this->interface,
$this->scope,
$this->version
);

@trigger_error($message, \E_USER_DEPRECATED);

return parent::getInterface();
}
}
33 changes: 33 additions & 0 deletions src/Core/src/Config/Proxy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Spiral\Core\Config;

class Proxy extends Binding
{
/**
* @param class-string $interface
*/
public function __construct(
protected readonly string $interface,
public readonly bool $singleton = false,
) {
if (!\interface_exists($interface)) {
throw new \InvalidArgumentException(\sprintf('Interface `%s` does not exist.', $interface));
}
}

public function __toString(): string
{
return \sprintf('Proxy to `%s`', $this->interface);
}

/**
* @return class-string
*/
public function getInterface(): string
{
return $this->interface;
}
}
60 changes: 35 additions & 25 deletions src/Core/src/Internal/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,9 @@
use Psr\Container\ContainerInterface;
use ReflectionFunctionAbstract as ContextFunction;
use ReflectionParameter;
use Spiral\Core\Attribute\Finalize;
use Spiral\Core\Attribute\Scope as ScopeAttribute;
use Spiral\Core\Attribute\Singleton;
use Spiral\Core\Attribute;
use Spiral\Core\BinderInterface;
use Spiral\Core\Config\Injectable;
use Spiral\Core\Config\DeferredFactory;
use Spiral\Core\Config;
use Spiral\Core\Container\InjectorInterface;
use Spiral\Core\Container\SingletonInterface;
use Spiral\Core\Exception\Container\AutowireException;
Expand Down Expand Up @@ -91,18 +88,20 @@ public function make(string $alias, array $parameters = [], Stringable|string|nu

unset($this->state->bindings[$alias]);
return match ($binding::class) {
\Spiral\Core\Config\Alias::class => $this->resolveAlias($binding, $alias, $context, $parameters),
\Spiral\Core\Config\Autowire::class => $this->resolveAutowire($binding, $alias, $context, $parameters),
DeferredFactory::class,
\Spiral\Core\Config\Factory::class => $this->resolveFactory($binding, $alias, $context, $parameters),
\Spiral\Core\Config\Shared::class => $this->resolveShared($binding, $alias, $context, $parameters),
Injectable::class => $this->resolveInjector(
Config\Alias::class => $this->resolveAlias($binding, $alias, $context, $parameters),
Config\Proxy::class,
Config\DeprecationProxy::class => $this->resolveProxy($binding, $alias, $context),
Config\Autowire::class => $this->resolveAutowire($binding, $alias, $context, $parameters),
Config\DeferredFactory::class,
Config\Factory::class => $this->resolveFactory($binding, $alias, $context, $parameters),
Config\Shared::class => $this->resolveShared($binding, $alias, $context, $parameters),
Config\Injectable::class => $this->resolveInjector(
$binding,
new Ctx(alias: $alias, class: $alias, context: $context),
$parameters,
),
\Spiral\Core\Config\Scalar::class => $binding->value,
\Spiral\Core\Config\WeakReference::class => $this
Config\Scalar::class => $binding->value,
Config\WeakReference::class => $this
->resolveWeakReference($binding, $alias, $context, $parameters),
default => $binding,
};
Expand All @@ -117,7 +116,7 @@ public function make(string $alias, array $parameters = [], Stringable|string|nu
* @psalm-suppress UnusedParam
* todo wat should we do with $arguments?
*/
private function resolveInjector(Injectable $binding, Ctx $ctx, array $arguments)
private function resolveInjector(Config\Injectable $binding, Ctx $ctx, array $arguments)
{
$context = $ctx->context;
try {
Expand Down Expand Up @@ -174,7 +173,7 @@ private function resolveInjector(Injectable $binding, Ctx $ctx, array $arguments
}

private function resolveAlias(
\Spiral\Core\Config\Alias $binding,
Config\Alias $binding,
string $alias,
Stringable|string|null $context,
array $arguments,
Expand All @@ -194,8 +193,19 @@ private function resolveAlias(
return $result;
}

private function resolveProxy(Config\Proxy $binding, string $alias, Stringable|string|null $context): mixed
{
$result = Proxy::create(new \ReflectionClass($binding->getInterface()), $context, new Attribute\Proxy());

if ($binding->singleton) {
$this->state->singletons[$alias] = $result;
}

return $result;
}

private function resolveShared(
\Spiral\Core\Config\Shared $binding,
Config\Shared $binding,
string $alias,
Stringable|string|null $context,
array $arguments,
Expand All @@ -210,7 +220,7 @@ private function resolveShared(
}

private function resolveAutowire(
\Spiral\Core\Config\Autowire $binding,
Config\Autowire $binding,
string $alias,
Stringable|string|null $context,
array $arguments,
Expand All @@ -222,14 +232,14 @@ private function resolveAutowire(
}

private function resolveFactory(
\Spiral\Core\Config\Factory|DeferredFactory $binding,
Config\Factory|Config\DeferredFactory $binding,
string $alias,
Stringable|string|null $context,
array $arguments,
): mixed {
$ctx = new Ctx(alias: $alias, class: $alias, context: $context, singleton: $binding->singleton);
try {
$instance = $binding::class === \Spiral\Core\Config\Factory::class && $binding->getParametersCount() === 0
$instance = $binding::class === Config\Factory::class && $binding->getParametersCount() === 0
? ($binding->factory)()
: $this->invoker->invoke($binding->factory, $arguments);
} catch (NotCallableException $e) {
Expand All @@ -244,7 +254,7 @@ private function resolveFactory(
}

private function resolveWeakReference(
\Spiral\Core\Config\WeakReference $binding,
Config\WeakReference $binding,
string $alias,
Stringable|string|null $context,
array $arguments,
Expand Down Expand Up @@ -370,7 +380,7 @@ private function validateNewInstance(
): object {
// Check scope name
$ctx->reflection = new \ReflectionClass($instance);
$scopeName = ($ctx->reflection->getAttributes(ScopeAttribute::class)[0] ?? null)?->newInstance()->name;
$scopeName = ($ctx->reflection->getAttributes(Attribute\Scope::class)[0] ?? null)?->newInstance()->name;
if ($scopeName !== null && $scopeName !== $this->scope->getScopeName()) {
throw new BadScopeException($scopeName, $instance::class);
}
Expand Down Expand Up @@ -405,7 +415,7 @@ private function createInstance(
}

// Check scope name
$scope = ($reflection->getAttributes(ScopeAttribute::class)[0] ?? null)?->newInstance()->name;
$scope = ($reflection->getAttributes(Attribute\Scope::class)[0] ?? null)?->newInstance()->name;
if ($scope !== null && $scope !== $this->scope->getScopeName()) {
throw new BadScopeException($scope, $class);
}
Expand Down Expand Up @@ -503,16 +513,16 @@ private function isSingleton(Ctx $ctx): bool
return true;
}

return $ctx->reflection->getAttributes(Singleton::class) !== [];
return $ctx->reflection->getAttributes(Attribute\Singleton::class) !== [];
}

private function getFinalizer(Ctx $ctx, object $instance): ?callable
{
/**
* @psalm-suppress UnnecessaryVarAnnotation
* @var Finalize|null $attribute
* @var Attribute\Finalize|null $attribute
*/
$attribute = ($ctx->reflection->getAttributes(Finalize::class)[0] ?? null)?->newInstance();
$attribute = ($ctx->reflection->getAttributes(Attribute\Finalize::class)[0] ?? null)?->newInstance();
if ($attribute === null) {
return null;
}
Expand Down
Loading

0 comments on commit dfb4061

Please sign in to comment.