-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[RFC] Making services private by default in Symfony 4? #20048
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
Comments
👍 but for that imo we have to allow using private services with any tag (create a public alias when needed for example). |
I'd be very happy to see this, but it would be a big issue for people extending the Controller. Imo Controller as a Service is the way to go but not everyone agrees with me. |
@iltar imo symfony should create its own public aliases when it needs to, instead of forcing the user to make his services public... but i'm not sure everyone agrees. Edit: actually that looks hard to do for controllers as they are resolved at runtime... |
@Ener-Getick the problem is that Symfony can't guess the service you're going to need if you use a Service Locator. |
An alternative would be to encourage using DunglasActionBundle but again not everyone agrees... |
You'd have to do static code analysis of the file and try to determine all locator calls. This works up to a point where calls become dynamic. |
@iltar i meant as a header of the config files 😉 |
Ah, well then I would rather recommend making everything private by default and recommending controller as a service instead. |
I would vote for but that's a major BC break and it will probably prevent people from upgrading to Symfony 4. I don't know if we can make this behavior globally configurable (enable or disable). |
@hhamon that's why we use bc layers and deprecations. For apps respecting best practices, it shouldn't be that hard to upgrade. But i'm wondering if it wouldn't make services even harder to understand for newcomers. I'm 👍 for making this globally configurable. |
Yes it will be harder for newcomers to understand this concept. When I do training sessions, including advanced ones, it's often rare that people know the differences between private and public services. And the consequences private service definitions have on the container compilation. So IMO, we have to be careful with this. If we decide to switch to private services by default, it has to be very very well documented. But again, I would prefer having a global configuration setting to enable or disable this behavior. |
@hhamon for me, we should promote autowiring for newcomers (or at worst methods hiding the calls to the container such as the base |
It can't be a global flag: definitions can't rely on some outside context! But it could be changed locally for a whole file for sure. Having everything private by default would prevent using the dic as a service locator, which is too easy for newcomers today. They'd better learn very soon that if they do this, they must optin to the not-the-best practice. |
Oh btw, we already planned to prevent fetching private services from the container in 4.0, so we already have the right explanation point for newcomers: the exception message they'll get then. |
@nicolas-grekas even with bad practices, the framework is hard to understand at first. The exception thrown when using private services at runtime will of course be useful but only if the user already understands services. |
What about libraries? If an application the issue is scoped to service locators, in libraries it's much more complex: you definitely don't want a service as private by default. What about a switch to trigger private by default instead (that would apply to application local services as well as an library services)? If the goal is performance here, I don't think it's right to break the BC promise for it, but we should still able to make it possible for people looking for it. |
@theofidry Usually when 3rd party bundles need services, they are being configured via the configuration/extension/compiler pass (in this sequence). At the last 2 points, you can fetch the service and make it public if needed, works well in combination with tagged services. |
@iltar as actually private services are accessible as before even if they come from a third-party bundle it's not a problem, sorry for the confusion. The only place (to my understanding) where it would be a problem are service locators, but it's a concern you already shared. So I'm all for it :) |
Would love to see this feature. |
Well, if we made that steps, we would have to treat controllers that use the DI container as a service locator a bad practice. So effectively you will have to register your controllers as services (or you have to turn services into public ones again). Is that something we can agree on (in the docs we recently made the shift the other way around, i.e. not propagating controllers as services)? |
I guess private services by default do not enforce that behavior, and mark public services as such. |
@xabbuh currently it's recommend to extend the Additionally for 4.0, a bunch of controller helpers (or make the Controller a ControllerHelper with public methods instead) should be created to "ease" Controller as a Service for people. With Autowiring in place, this could still be relatively easy for people to start with. |
@xabbuh I'm still a firm believer that controller as a service is not at all something people should do by default. Too much added complexity for little gain. We need to support the controller as a service pattern for people who like this approach and to make controllers independent of the full-stack framework (to be able to share them with other libs/frameworks built on top of the Symfony components). |
@nicolas-grekas could this be configured via a parameter? |
👍 to make services private by default. @fabpot We can also import the code of ActionBundle in core (or include the bundle in the standard edition symfony/symfony-standard#960). It's about 200 LOC and it allows to create and use controllers as a service as easily as the standard Symfony controller while promoting better practices regarding dependency injection: https://github.com/dunglas/DunglasActionBundle#usage IMO it's even easier for newcomers than the current controller system because it doesn't require to register services in XML or YAML. I added this bundle in the standard edition of API Platform 2 (https://github.com/api-platform/docs/blob/master/core/operations.md#creating-custom-operations-and-controllers), and I got only positive feedback from users for now. |
While I find the configuration for the ActionBundle a bit confusing, I really like the concept of the bundle. Maybe it cold indeed be integrated into Symfony? |
@iltar can you open an issue about the config? What looks confusing to you? It can probably be improved. |
Silex (Pimple) used to mark services as private by default, but changed this for performance reasons. Marking services as private by default seems like a good idea, when you mark them as public it's not a bad-practice perse, it simply means you want to make them accessible elsewhere (private/internal vs public API). But most services should not be accessible by default (like 'form.registry'). Sorry for the off topic, but is it better to mark a service as lazy rather then marking them as public and use the Container for loading them?? PS. Anyone who needs compatibility can simple add a CompilerPass that marks all services as public 😄 but that's a horrible idea. |
@sstok It never did this. Pimple does not even have a concept of private services (as it does not have a compilation step to apply optimizations). The performance reasons made Pimple change the way shared services are stored internally. And this change made it possible to share by default (the old way required sharing to be done on top of non-shared services, and so could not be the default). The change of default behavior was done because 99% of services are actually shared (and Symfony DI always made them shared by default) |
…ame for "public", "tags" & "autowire" (nicolas-grekas, ogizanagi) This PR was merged into the 3.3-dev branch. Discussion ---------- [DI] Add "inherit-tags" with configurable defaults + same for "public", "tags" & "autowire" | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #20048 | License | MIT | Doc PR | - Instead of making services private by default (#20048), which is going to create a big migration burden that might not be worth it, I'd like to propose the idea of changing the default for the current file. Having a place to redefine some defaults, this can also be used to enable autowiring for a file, making it much lighter in the end. This PR handles defaults for "public", "autowired" and "tags". Not sure the other options need a configurable default. Please comment if you think otherwise. Short example (the feat is more interesting with a longer list of services): ```yaml services: _defaults: public: false autowire: ['_construct', 'set*'] foo: class: Foo ``` ```xml <?xml version="1.0" encoding="utf-8"?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <defaults public="false"> <autowire>__construct</autowire> <tag name="foo"/> </defaults> </services> </container> ``` ping @dunglas @weaverryan Commits ------- beec1cf [DI] Allow definitions to inherit tags from parent context 05f24d5 [DI] Add "defaults" tag to XML services configuration 7b4a18b [DI] Add "_defaults" key to Yaml services configuration
…h BC layer (nicolas-grekas) This PR was merged into the 3.4 branch. Discussion ---------- [DI] Turn services and aliases private by default, with BC layer | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | yes | Tests pass? | yes | Fixed tickets | #20048 | License | MIT | Doc PR | - With this PR, all services and aliases are made private by default. This is done in a BC way, thanks to the layer introduced in #24104. We will require bundles to explicitly opt-in for "public", either using "defaults", or stating `public="true"` explicitly. Same in DI extension, where calling `->setPublic(true)` will be required in 4.0. Commits ------- 9948b09 [DI] Turn services and aliases private by default, with BC layer
Just wondering, how about making services private by default in Symfony 4?
That would give us more private services optimizations by default, thus lighter containers.
The path could be as "simple" as deprecating the "public" attribute and replacing it by a "private" one.
Worth pursuing?
The text was updated successfully, but these errors were encountered: