tobento/service-resolver

用于构建PHP应用程序的解析器。

1.0.1 2023-11-29 18:29 UTC

This package is auto-updated.

Last update: 2024-09-29 20:07:37 UTC


README

解析器服务是PSR-11容器的抽象层。

目录

入门

运行以下命令添加解析器服务项目的最新版本。

composer require tobento/service-resolver

要求

  • PHP 8.0或更高版本

亮点

  • 框架无关,可与任何项目一起使用
  • 解耦设计
  • 自动装配

文档

实现

目前有以下实现

解析器工厂

use Tobento\Service\ResolverContainer\ResolverFactory;
use Tobento\Service\Resolver\ResolverFactoryInterface;
use Tobento\Service\Resolver\ResolverInterface;

$resolverFactory = new ResolverFactory();

var_dump($resolverFactory instanceof ResolverFactoryInterface);
// bool(true)

// create resolver
$resolver = $resolverFactory->createResolver();

var_dump($resolver instanceof ResolverInterface);
// bool(true)

解析器

PSR-11

use Tobento\Service\ResolverContainer\ResolverFactory;

class Foo {}

$resolver = (new ResolverFactory())->createResolver();

var_dump($resolver->has(Bar::class));
// bool(false)

var_dump($resolver->get(Foo::class));
// object(Foo)#5 (0) { }

自动装配

解析器通过自动装配解析任何依赖项,除了内置参数需要通过定义来解析。

在联合类型参数上,如果没有通过定义设置,则使用第一个可解析参数。

定义

通过提供解析的对象

use Tobento\Service\ResolverContainer\ResolverFactory;

class Foo
{
    public function __construct(
        protected string $name
    ) {} 
}

$resolver = (new ResolverFactory())->createResolver();

$resolver->set(Foo::class, new Foo('value'));

var_dump($resolver->get(Foo::class));
// object(Foo)#3 (1) { ["name":protected]=> string(5) "value" }

通过定义缺失的参数

use Tobento\Service\ResolverContainer\ResolverFactory;

class Foo
{
    public function __construct(
        protected string $name
    ) {}
}

$resolver = (new ResolverFactory())->createResolver();

// By the construct method:
$resolver->set(Foo::class)->construct('value');

// By the with method using parameter name:
$resolver->set(Foo::class)->with(['name' => 'value']);

// By the with method using parameter position:
$resolver->set(Foo::class)->with([0 => 'value']);

var_dump($resolver->get(Foo::class));
// object(Foo)#9 (1) { ["name":protected]=> string(5) "value" }

通过使用闭包

use Tobento\Service\ResolverContainer\ResolverFactory;

class Foo
{
    public function __construct(
        protected string $name
    ) {}
}

$resolver = (new ResolverFactory())->createResolver();

$resolver->set(Foo::class, function($container) {
    return new Foo('value');
});

var_dump($resolver->get(Foo::class));
// object(Foo)#8 (1) { ["name":protected]=> string(5) "value" }

您可以配置要使用哪个实现

$resolver->set(BarInterface::class, Bar::class);

定义方法调用:如果您想进行自动装配,则只需定义内置参数即可,其他参数会自动装配。

use Tobento\Service\ResolverContainer\ResolverFactory;

class Foo
{
    protected array $called = [];
    
    public function index(Bar $bar, string $name)
    {
        $this->called[] = [$bar, $name];
    } 
}

class Bar {}

$resolver = (new ResolverFactory())->createResolver();

$resolver->set(Foo::class)->callMethod('index', ['name' => 'value']);

$resolver->set(Foo::class)->callMethod('index', [1 => 'value']);

$foo = $resolver->get(Foo::class);

原型定义

您可以声明定义为原型,这意味着总是返回一个新实例。

use Tobento\Service\ResolverContainer\ResolverFactory;

class Foo {}
class Bar {}

$resolver = (new ResolverFactory())->createResolver();

$resolver->set(Foo::class)->prototype();

$resolver->set(Bar::class, function() {
    return new Bar();
})->prototype();

var_dump($resolver->get(Foo::class) === $resolver->get(Foo::class));
// bool(false)

var_dump($resolver->get(Bar::class) === $resolver->get(Bar::class));
// bool(false)

制作

make()方法的工作方式与get()类似,但每次调用时都会解析条目。

use Tobento\Service\ResolverContainer\ResolverFactory;

class Foo
{
    public function __construct(
        private Bar $bar,
        private string $name
    ) {} 
}

class Bar {}

$resolver = (new ResolverFactory())->createResolver();

$foo = $resolver->make(Foo::class, ['name' => 'value']);

调用

更多详细信息请访问:service-autowire#call

use Tobento\Service\ResolverContainer\ResolverFactory;

class Foo
{
    public function index(Bar $bar, string $name): string
    {
        return $name;
    } 
}

class Bar {}

$resolver = (new ResolverFactory())->createResolver();

$name = $resolver->call([Foo::class, 'index'], ['name' => 'value']);

var_dump($name);
// string(5) "value"

在...上

使用on方法,您可以替换、修改或构造对象。

替换对象

替换对象

您可以通过声明一个类简单地替换解析的对象

use Tobento\Service\ResolverContainer\ResolverFactory;

class AdminUser {}
class GuestUser {}

$resolver = (new ResolverFactory())->createResolver();

$resolver->on(AdminUser::class, GuestUser::class);

$user = $resolver->get(AdminUser::class);

var_dump($user);
// object(GuestUser)#9 (0) { }

使用可调用对象替换对象

您可以通过使用可调用对象替换解析的对象。第一个参数总是解析的对象,但您可以为所需的任何其他对象提供类型提示。

use Tobento\Service\ResolverContainer\ResolverFactory;

class AdminUser {}
class GuestUser {}

$resolver = (new ResolverFactory())->createResolver();

$resolver->on(AdminUser::class, function($user) {
    return new GuestUser();
});

// with typehint:
$resolver->on(AdminUser::class, function($user, GuestUser $guest) {
    return $guest;
});
         
$user = $resolver->get(AdminUser::class);

var_dump($user);
// object(GuestUser)#17 (0) { }

修改对象

使用可调用对象修改对象

您可以通过使用可调用对象修改解析的对象。第一个参数总是解析的对象,但您可以为所需的任何其他对象提供类型提示。

use Tobento\Service\ResolverContainer\ResolverFactory;

class AdminUser {}
class GuestUser {}

$resolver = (new ResolverFactory())->createResolver();

$resolver->on(AdminUser::class, function($user) {
    // modify $user
});
         
$user = $resolver->get(AdminUser::class);

var_dump($user);
// object(AdminUser)#9 (0) { }

构造对象

通过提供数组构造对象

您可能希望将不同的实现注入到每个类或注入任何原始值。

use Tobento\Service\ResolverContainer\ResolverFactory;

interface UserInterface {}
class GuestUser implements UserInterface {}
class AdminUser implements UserInterface {}

class ServiceFoo {
    public function __construct(
        protected UserInterface $user
    ) {}
}

class ServiceBar {
    public function __construct(
        protected UserInterface $user
    ) {}
}

$resolver = (new ResolverFactory())->createResolver();

$resolver->set(UserInterface::class, GuestUser::class);

$resolver->on(ServiceFoo::class, ['user' => AdminUser::class]);

var_dump($resolver->get(ServiceFoo::class));
// object(ServiceFoo)#14 (1) { ["user":protected]=> object(AdminUser)#11 (0) { } }

var_dump($resolver->get(ServiceBar::class));
// object(ServiceBar)#9 (1) { ["user":protected]=> object(GuestUser)#13 (0) { } }

使用返回数组的可调用对象构造对象

您可能希望使用返回解析值的可调用对象将不同的实现注入到每个类或注入任何原始值。可调用对象的第一参数总是解析的对象,但您可以为所需的任何其他对象提供类型提示。

use Tobento\Service\ResolverContainer\ResolverFactory;

interface UserInterface {}
class GuestUser implements UserInterface {}
class AdminUser implements UserInterface {}

class ServiceFoo {
    public function __construct(
        protected UserInterface $user
    ) {}
}

class ServiceBar {
    public function __construct(
        protected UserInterface $user
    ) {}
}

$resolver = (new ResolverFactory())->createResolver();

$resolver->set(UserInterface::class, GuestUser::class);

$resolver->on(ServiceFoo::class, function($service, AdminUser $user) {
    return ['user' => $user];
});

var_dump($resolver->get(ServiceFoo::class));
// object(ServiceFoo)#12 (1) { ["user":protected]=> object(AdminUser)#17 (0) { } }

var_dump($resolver->get(ServiceBar::class));
// object(ServiceBar)#11 (1) { ["user":protected]=> object(GuestUser)#14 (0) { } }

调用方法

您可能希望在对象解析后调用方法

use Tobento\Service\ResolverContainer\ResolverFactory;

class FooService {}
class AdminUser {
    protected array $called = [];
    
    public function set(FooService $service, string $value)
    {
        $this->called[] = [$service, $value];
    }
    
    public function getCalled(): array
    {
        return $this->called;
    }
}

$resolver = (new ResolverFactory())->createResolver();

$resolver->on(AdminUser::class)
         ->callMethod('set', ['value' => 'foo']);

$user = $resolver->get(AdminUser::class);

var_dump($user->getCalled());
// array(1) { [0]=> array(2) { [0]=> object(FooService)#15 (0) { } [1]=> string(3) "foo" } }

声明为特质

当类使用特质时,您可以调用方法

use Tobento\Service\ResolverContainer\ResolverFactory;

trait SomeMethods {
    protected array $called = [];
    
    public function set(string $value)
    {
        $this->called[] = $value;
    }
    
    public function getCalled(): array
    {
        return $this->called;
    }    
}

class AdminUser {
    use SomeMethods;
}

$resolver = (new ResolverFactory())->createResolver();

$resolver->on(SomeMethods::class)
         ->trait()
         ->once(false)
         ->callMethod('set', ['value' => 'foo']);

$user = $resolver->get(AdminUser::class);

var_dump($user->getCalled());
// array(1) { [0]=> string(3) "foo" }

使用Once

on方法默认仅处理一次。您可以使用once方法始终处理

class AdminUser {}
class GuestUser {}

$resolver = (new ResolverFactory())->createResolver();

$resolver->set(AdminUser::class)->prototype();

$resolver->on(AdminUser::class, GuestUser::class);
         ->once(false);

var_dump($resolver->get(AdminUser::class));
// object(GuestUser)#10 (0) { }

var_dump($resolver->get(AdminUser::class));
// object(GuestUser)#10 (0) { }

使用原型

您可以使用prototype方法始终返回一个新实例

use Tobento\Service\ResolverContainer\ResolverFactory;

class AdminUser {}
class GuestUser {}

$resolver = (new ResolverFactory())->createResolver();

$resolver->set(AdminUser::class)->prototype();

$resolver->on(AdminUser::class, GuestUser::class)
         ->once(false)
         ->prototype();

var_dump(
    $resolver->get(AdminUser::class)
    === $resolver->get(AdminUser::class)
);
// bool(false)

使用Instanceof

您可以通过使用instanceof方法根据对象所属的特定类来修改或替换对象

use Tobento\Service\ResolverContainer\ResolverFactory;

interface UserInterface {}
class User implements UserInterface {}
class GuestUser extends User {}
class AdminUser extends User {}

$resolver = (new ResolverFactory())->createResolver();

$resolver->set(UserInterface::class, GuestUser::class);

$resolver->on(User::class, function($user) {
    return new AdminUser();
})->instanceof();

var_dump($resolver->get(UserInterface::class));
// object(AdminUser)#12 (0) { }

使用优先级

您可以使用priority方法处理执行顺序。默认优先级为1000,最高优先级首先处理

use Tobento\Service\ResolverContainer\ResolverFactory;

class AdminUser {}
class GuestUser {}
class EditorUser {}

$resolver = (new ResolverFactory())->createResolver();

$resolver->on(AdminUser::class, EditorUser::class)
         ->priority(500);

$resolver->on(AdminUser::class, GuestUser::class)
         ->priority(1000);

var_dump($resolver->get(AdminUser::class));
// object(EditorUser)#8 (0) { }

规则

您可以使用rule方法添加规则

use Tobento\Service\ResolverContainer\ResolverFactory;
use Tobento\Service\Resolver\RuleInterface;
use Tobento\Service\Resolver\OnRule;

class AdminUser {}
class GuestUser {}

$resolver = (new ResolverFactory())->createResolver();

$rule = new OnRule(AdminUser::class, GuestUser::class);

var_dump($rule instanceof RuleInterface);
// bool(true)

$resolver->rule($rule);

$user = $resolver->get(AdminUser::class);

var_dump($user);
// object(GuestUser)#9 (0) { }

容器

use Tobento\Service\ResolverContainer\ResolverFactory;
use Psr\Container\ContainerInterface;

$resolver = (new ResolverFactory())->createResolver();

var_dump($resolver->container() instanceof ContainerInterface);
// bool(true)

致谢