Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Contracts][DependencyInjection] Add ServiceCollectionInterface #53163

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

Merged
merged 1 commit into from
Jan 2, 2024

Conversation

kbond
Copy link
Member

@kbond kbond commented Dec 20, 2023

Q A
Branch? 7.1
Bug fix? no
New feature? yes
Deprecations? no
Issues n/a
License MIT

I commonly require a service locator which is also an iterator. I noticed during @weaverryan's most recent livestream he could have benefited from this as well. There are currently three ways to achieve:

  1. Decorate a ServiceLocator and use ServiceLocator::getProvidedServices() in the decorator's getIterator()
  2. Decorate a service iterable and add your own ContainerInterface::get() method which iterates over all items until it finds a match (this is what Ryan did in his livestream)
  3. Inject a tagged iterator in addition to your locator.

This PR proposes a new ServiceCollectionInterface which is a countable, iterable, ContainerInterface.

I'm implemented on the current ServiceLocator so any place this is used in your code, you can now type-hint ServiceCollectionInterface:

public function someAction(
    #[TaggedLocator('some.tag')]
    ServiceCollectionInterface $container,
) {
    foreach ($container as $id => $item) {
        // do something with each service/id
    }

    $container->count(); // # of services
    
    $container->get('some.id');
}

@kbond kbond force-pushed the feat/service-collection-interface branch from 18cf8fe to 6e48c4c Compare December 20, 2023 18:38
Copy link
Member

@GromNaN GromNaN left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I commonly require a service locator which is also an iterator.

Could you provide examples where you need the service locator to be iterable. Maybe a link to the video with a point in time where "he could have benefited from this".

@kbond
Copy link
Member Author

kbond commented Dec 20, 2023

Could you provide examples where you need the service locator to be iterable.

Here's a good example: https://github.com/symfony/ux/pull/1347/files#diff-6487ef7c39e106956904930b49958df26d90de6e025cd0c69a1a9a84e31ed064 (the entire class could be replaced with a ServiceLocator). He needs to iterate over a collection of component services for the index page, then, on each component page, he needs to get a specific one.

@kbond kbond force-pushed the feat/service-collection-interface branch from 6e48c4c to ca47fd0 Compare December 20, 2023 21:35
@kbond kbond changed the title [DependencyInjection] Introduce ServiceCollectionInterface [DependencyInjection] Make ServiceLocator iterable Dec 20, 2023
@kbond
Copy link
Member Author

kbond commented Dec 20, 2023

Ok, I've removed the interface and just made ServiceLocator iterable.

@kbond kbond force-pushed the feat/service-collection-interface branch from ca47fd0 to 8db521a Compare December 20, 2023 21:43
@derrabus
Copy link
Member

There are currently two ways to achieve:

  1. Inject a tagged iterator in addition to your locator.

Comment on lines 88 to 89
foreach (array_keys($this->getProvidedServices()) as $id) {
yield $id => $this->get($id);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This basically means that by iterating over the locator you initialize all of its services. To me, this kinda defeats the purpose of a service locator: I could've just injected a tagged iterator and called iterator_to_array() on it.

If we really want to make the locator traversable (not sure if we should yet), maybe we'd rather yield a service closure instead?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but because this is a generator, this still does it kinda lazily so it's OK?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, this still looks like a pitfall to me. I wouldn't do it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kbond WDYT of this concern? I don't feel the need for this extra safety myself but maybe your experience could help best decide?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, this still looks like a pitfall to me. I wouldn't do it.

Does the new interface alleviate the concern at all?

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The added method is just a helper: userland can do this iteration with any ServiceProviderInterface.

IoC principle would encourage using the interface.

Still worth it?

@nicolas-grekas
Copy link
Member

symfony/ux#1347 (files) (the entire class could be replaced with a ServiceLocator)

that's quite common needless overhead - we already simplified many components by using a container
this PR is not needed to kill that class :)

@kbond
Copy link
Member Author

kbond commented Dec 22, 2023

that's quite common needless overhead - we already simplified many components by using a container
this PR is not needed to kill that class :)

I guess you mean, inject a tagged iterator into the index action and the tagged locator into the show method? Indeed that would work.

The added method is just a helper: userland can do this iteration with any ServiceProviderInterface.

This is true, and in fact what I've been doing, but in userland, ServiceProviderInterface::getProvidedServices() is weird. It's keyed by the service id which, imo, is 95% of the reason to call by end users (the array values aren't important at this stage). It's a bit of a papercut imo as I don't think it's entirely clear this is possible (iterating over all the services).

What about a ServiceProviderInterface::getProvidedServiceIds(): string[] method?

Still worth it?

I do still feel this improves the DX as the need for a service locator/iterator is a common pattern I'm seeing (but let's close if there isn't agreement).

@nicolas-grekas
Copy link
Member

To make this full "IoC", one can type hint ServiceProviderInteface&Traversable, isn't it? Then LGTM.

@kbond kbond force-pushed the feat/service-collection-interface branch from 8db521a to 33d14d0 Compare December 26, 2023 16:09
@kbond kbond changed the title [DependencyInjection] Make ServiceLocator iterable [DependencyInjection] Add ServiceCollectionInterface Dec 26, 2023
@carsonbot carsonbot changed the title [DependencyInjection] Add ServiceCollectionInterface [Contracts][DependencyInjection] Add ServiceCollectionInterface Dec 26, 2023
@kbond
Copy link
Member Author

kbond commented Dec 26, 2023

As discussed with Nicolas on Slack, I've switched back to a ServiceCollectionInterface (in symfony/service-contracts).

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me, thanks.
Please update the root composer.json for 3.5 also.

@kbond kbond force-pushed the feat/service-collection-interface branch from 33d14d0 to a47e84f Compare December 28, 2023 16:09
@kbond
Copy link
Member Author

kbond commented Dec 28, 2023

Please update the root composer.json for 3.5 also.

Done, but please confirm it's updated correctly.

@kbond kbond force-pushed the feat/service-collection-interface branch from a47e84f to 578a99a Compare December 28, 2023 16:34
@kbond kbond force-pushed the feat/service-collection-interface branch from 578a99a to 4a9e874 Compare January 2, 2024 14:07
@chalasr
Copy link
Member

chalasr commented Jan 2, 2024

Thank you @kbond.

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

Successfully merging this pull request may close these issues.

10 participants