laravel 中实现注解注入
创建注解类
<?php
declare(strict_types=1);
namespace App\Support\Attributes;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
readonly class Injection
{
public function __construct(
public ?string $propertyType = null,
public array $parameters = []
) {}
}
引导解析注解
<?php
declare(strict_types=1);
namespace App\Providers;
use App\Support\Attributes\Injection;
use Illuminate\Foundation\Application;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register(): void {}
public function boot(): void
{
$this->injection();
}
private function injection(): void
{
$this->app->resolving(static function (mixed $object, Application $app): void {
if (! \is_object($object)) {
return;
}
$class = str($object::class);
if (
! $class->is(config('services.injection.only'))
|| $class->is(config('services.injection.except'))
) {
return;
}
$reflectionObject = new \ReflectionObject($object);
foreach ($reflectionObject->getProperties() as $reflectionProperty) {
if (! $reflectionProperty->isDefault() || $reflectionProperty->isStatic()) {
continue;
}
$attributes = $reflectionProperty->getAttributes(Injection::class);
if ($attributes === []) {
continue;
}
/** @var Injection $injection */
$injection = $attributes[0]->newInstance();
$propertyType = value(static function () use ($injection, $reflectionProperty, $reflectionObject): string {
if ($injection->propertyType) {
return $injection->propertyType;
}
$reflectionPropertyType = $reflectionProperty->getType();
if ($reflectionPropertyType instanceof \ReflectionNamedType && ! $reflectionPropertyType->isBuiltin()) {
return $reflectionPropertyType->getName();
}
throw new \LogicException(\sprintf(
'Attribute [%s] of %s miss a argument, or %s must be a non-built-in named type.',
Injection::class,
$property = "property [{$reflectionObject->getName()}::\${$reflectionProperty->getName()}]",
$property,
));
});
$reflectionProperty->isPublic() or $reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($object, $app->make($propertyType, $injection->parameters));
}
});
}
}
配置解析范围(可选)
config/services.php
<?php
return [
// ...
'injection' => [
'only' => [
'App\*',
],
'except' => [
'App\Support\Macros\*',
],
],
];
使用
示例
<?php
declare(strict_types=1);
namespace App\Console\Commands;
use App\Support\Attributes\Injection;
use App\Support\HmacSigner;
use Illuminate\Config\Repository;
class TestCommand extends \Illuminate\Console\Command
{
protected $signature = 'test';
#[Injection('path.storage')]
private string $storagePath;
#[Injection(parameters: ['secret' => 'secret...'])]
private HmacSigner $hmacSigner;
#[Injection(Repository::class)]
private Repository $repositoryOfInjectionPropertyType;
#[Injection('config')]
private Repository $repositoryOfInjectionInstanceKey;
#[Injection]
private Repository $repositoryOfReflectionPropertyType;
public function handle(): void
{
dump(
$this->storagePath,
$this->hmacSigner,
$this->repositoryOfInjectionPropertyType->get('services.injection'),
$this->repositoryOfInjectionInstanceKey->get('services.injection.only'),
$this->repositoryOfReflectionPropertyType->get('services.injection.except'),
);
}
}
输出
╰─ ./artisan test ─╯
"/Users/yaozm/Documents/wwwroot/laravel-skeleton/storage" // app/Console/Commands/TestCommand.php:32
App\Support\HmacSigner {#2045
-secret: "secret..."
-algo: "sha256"
} // app/Console/Commands/TestCommand.php:32
array:2 [
"only" => array:1 [
0 => "App\*"
]
"except" => array:1 [
0 => "App\Support\Macros\*"
]
] // app/Console/Commands/TestCommand.php:32
array:1 [
0 => "App\*"
] // app/Console/Commands/TestCommand.php:32
array:1 [
0 => "App\Support\Macros\*"
] // app/Console/Commands/TestCommand.php:32
与依赖注入比较
功能 | 注解注入 | 依赖注入 |
---|---|---|
标量类型 | 已支持 | 未支持 |
传参 | 已支持 | 未支持 |
相关连接
- app/Providers/AppServiceProvider.php#L680-L747
- app/Support/Attributes/Injection.php
- app/Support/HmacSigner.php