Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[9.x] Helper - Set tag on object based on instanceof #45910

Conversation

wimulkeman
Copy link
Contributor

Purpose

Allow tagging an object based on its parent(s), interface(s) or its own FQCN.

This enables a developer to tag multiple objects dynamically at once without having to manually add new objects when they are added and extend a certain interface or parent.

Dynamically tagging can be an useful addition when you use a strategy pattern in your code and want, for example, register a new handler.

How is it used

Its usage would result in line with the following sample code.

tag_instance_of(BaseServiceTypeInterface::class, 'my-tag');

$this->app
    ->when(MyDynamicHandlerService::class)
    ->needs(BaseServiceTypeInterface::class)
    ->giveTagged('my-tag');

// ...

class MyDynamicHandlerService
{
    public function __construct(BaseServiceTypeInterface ...$handlers)
    {
        $this->handlers = $handlers;
    }

    public function handle(string $type, array $config)
    {
        foreach ($this->handlers as $handler) {
            if (! $handler->supports($type)) {
                continue;
            }

            return $handler->handle($config);
        }

        throw new \LogicException('Missing handler for type '.$type);
    }
}

Configuration options

The developer can influence certain parts by configuration:

  • app.instance_of.cache (bool) - Wether or not to cache the result of the tagging. This can be used in non-development environments to speed-up the proces.
  • app.instance_of.namespaces.include (string[]) - The class namespaces that should be considered to be tagged. By default this is only done for App\.
  • app.instance_of.namespaces.exclude (string[]) - The class namespaces that should be ignored to be tagged. By default this one is empty.
  • app.instance_of.classmap (array<string, string[]> - A custom created classmap. By default a classmap and the classes and interfaces it implements/extends are created based on the Composer classmap.

Based on
This MR is a rework of #44164. In this MR the functionality has been moved to a user land function and the codebase has been updated to boost its performance (the result of running it in production environments).

This update has been inspired by the Symfony methology to tag services through an instanceof provided value. @see https://symfony.com/doc/current/service_container/tags.html#autoconfiguring-tags

Resolves #42499

Allow tagging an object based on its parent(s), interface(s) or its own FQCN.

This enables a developer to tag multiple objects dynamically at once without having to manually add new objects when they are added and extend a certain interface or parent.

Dynamically tagging can be an useful addition when you use a strategy pattern in your code and want, for example, register a new handler.

This update has been inspired by the Symfony methology to tag services through an instanceof provided value.
@see https://symfony.com/doc/current/service_container/tags.html#autoconfiguring-tags

Resolves laravel#42499
@ankurk91
Copy link
Contributor

ankurk91 commented Feb 2, 2023

It is a huge function.
It will be rarely used.
It can go into a dedicated class instead.

@wimulkeman
Copy link
Contributor Author

In which namespace does that need to go if it would not be a function? I would say that it would sit in Illuminate\Container with something like TagInstance as classname. Or perhaps as a trait which developers can set upon their Application?

@taylorotwell
Copy link
Member

Since it looks like this entire thing can be done in user-land, I would suggest releasing this as a Composer package. That way the functionality can be shared with others but we don't have to maintain it in the core. 👍

@luninroman
Copy link

luninroman commented Feb 6, 2023

Since it looks like this entire thing can be done in user-land, I would suggest releasing this as a Composer package. That way the functionality can be shared with others but we don't have to maintain it in the core. +1

Such a useful function would never end up in the main package? That's nonsense. Symfony has been using it for years

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants