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
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 3

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

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

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