Spiral 框架提供了一种使用注入接口来控制任何接口或抽象类子类的创建过程的方法。
使用它有几个好处:
本指南演示了如何创建一个类实例并为其分配一个唯一的值,而无论哪个子类实现了它。
让我们假设我们有一个接口 Psr\SimpleCache\CacheInterface
,它提供一个简单的缓存接口。
注入器类应该实现 Spiral\Core\Container\InjectorInterface
,它提供一个名为 createInjection
的方法。 每次从容器请求特定类时都会使用此方法。
在我们的示例中,我们可以将一个引导程序和一个注入器组合成一个实例:
namespace App\Application\Bootloader;
use Psr\Container\ContainerInterface;
use Psr\SimpleCache\CacheInterface;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Core\BinderInterface;
use Spiral\Core\Container\InjectorInterface;
class CacheBootloader extends Bootloader implements InjectorInterface
{
public function __construct(
private readonly ContainerInterface $container,
) {}
public function boot(BinderInterface $binder): void
{
// 注册可注入的类
$binder->bindInjector(CacheInterface::class, self::class);
}
public function createInjection(\ReflectionClass $class, string $context = null): CacheInterface
{
return match ($context) {
'redis' => new RedisCache(...),
'memcached' => new MemcachedCache(...),
default => new ArrayCache(...),
};
}
}
注意 不要忘记激活引导程序。
现在,我们可以使用注入器根据上下文获取特定的缓存实现。
当容器解析 CacheInterface
时,它将使用 createInjection
方法从注入器请求它。该方法接受两个参数 $class
和 $context
。$class
参数返回所请求类的 ReflectionClass
对象,而 $context
参数返回一个参数或别名名称(例如,请求可注入类的的方法或函数的参数名称)。
以下是如何使用注入器的示例:
namespace App\Endpoint\Web;
use Psr\SimpleCache\CacheInterface;
class BlogController
{
public function __construct(
private readonly CacheInterface $redis,
private readonly CacheInterface $memcached,
private readonly CacheInterface $cache,
) {
\assert($redis instanceof RedisCache);
\assert($memcached instanceof MemcachedCache);
\assert($cache instanceof ArrayCache);
}
}
在此示例中,BlogController
类接受三个属性 $redis
,$memcached
和 $cache
。 这些属性都属于 CacheInterface
类型,但它们对应于接口的不同实现,具体取决于传递给注入器的 createInjection
方法的上下文。
使用注入器可以进行类继承。
注意 目前,注入器仅支持扩展基类的类(而不是接口),但未来的 Spiral 版本也将支持接口继承。
abstract class RedisCacheInterface implements CacheInterface
{
}
在此示例中,RedisCacheInterface
是一个实现 CacheInterface
的抽象类。
例如,在 createInjection
方法中,我们可以检查所请求的类是否是 RedisCacheInterface
的子类,并返回一个 RedisCache
实例,或者检查所请求的类是否是 MemcachedCacheInterface
的子类,并返回一个 MemcachedCache
实例。
public function createInjection(\ReflectionClass $class, string $context = null): CacheInterface
{
if ($class->isSubclassOf(RedisCacheInterface::class)) {
return new RedisCache(...);
}
return match ($context) {
'redis' => new RedisCache(...),
'memcached' => new MemcachedCache(...),
default => new ArrayCache(...),
};
}
spiral/boot
组件提供了一种使用 Spiral\Boot\Injector\InjectableEnumInterface
接口解析枚举类的便捷方法。当容器请求一个枚举时,它将调用一个指定的方法来确定该枚举的当前值并注入任何必需的依赖项。
使用枚举注入有几个好处:
让我们创建一个 Enum
,我们可以轻松地确定应用程序的环境。
use Spiral\Boot\EnvironmentInterface;
use Spiral\Boot\Injector\ProvideFrom;
use Spiral\Boot\Injector\InjectableEnumInterface;
#[ProvideFrom(method: 'detect')]
enum AppEnvironment: string implements InjectableEnumInterface
{
case Production = 'prod';
case Stage = 'stage';
case Testing = 'testing';
case Local = 'local';
public function isProduction(): bool
{
return $this === self::Production;
}
public function isTesting(): bool
{
return $this === self::Testing;
}
public function isLocal(): bool
{
return $this === self::Local;
}
public function isStage(): bool
{
return $this === self::Stage;
}
public static function detect(EnvironmentInterface $environment): self
{
$value = $environment->get('APP_ENV');
return \is_string($value)
? (self::tryFrom($value) ?? self::Local)
: self::Local;
}
}
ProvideFrom
属性用于指定一个 detect
方法,该方法用于确定枚举的当前值。当容器请求枚举时,将调用该方法,并且来自容器的任何必需依赖项都将被注入到该方法中。
容器将自动注入具有正确值的枚举。
final class MigrateCommand extends Command
{
const NAME = '...';
public function perform(AppEnvironment $appEnv): int
{
if ($appEnv->isProduction()) {
// 拒绝
}
// 执行迁移
}
}