laravel 中实现注解注入

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

与依赖注入比较

功能 注解注入 依赖注入
标量类型 已支持 未支持
传参 已支持 未支持

相关连接

原文连接

本作品采用《CC 协议》,转载必须注明作者和本文链接
No practice, no gain in one's wit. 我的 Gitub
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 3

那么问题来了,我用new关键字来实例化类你怎么办

5个月前 评论
laradocs 5个月前
guanguans (楼主) 5个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
58
粉丝
130
喜欢
991
收藏
1349
排名:45
访问:15.5 万
私信
所有博文
社区赞助商