-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[RFC] Define services via annotations #21103
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
DX wise it might seem nice, but it will also allow people to write a lot of code in a bad way, making it harder for them to decouple it at a later stage. It will surely be nice for new developers as they will have less files to configure, but introduces a path they will go down on which will make them think less in terms of libraries and will start seeing classes as services even more. Services are nothing but a reference to something in a container and people often refer to their class as a service. This feature would make that line even thinner and I'm afraid it would confuse developers even more. |
At this point, what is the benefit against DunglasActionBundle ? |
Your second alternative does not work, as the container cannot inject dependencies into private parameters. Your third and fourth alternatives depend on #20973 (and possibly on #21031), which should be clarified, as it is not yet accepted. @GuilhemN DunglasActionBundle is about defining controllers differently. This is about services (which might not be controllers themselves) @javiereguiluz there is one thing to consider there though: defining your services by annotations on the source code directly means that your entire source code now needs to be checked when rebuilding the container. This can have a very negative effect on DX if the container cache is considered as invalidated each time you edit your source code (we used to have this concern with autowired classes at the beginning btw). The implementation must be done in an efficient way (but invalidating the cache when you add the |
Some weeks ago we had such a kind of discussion in our local user group (with the JMSDiExtraBundle). Beside of this I don't think it is really much easier for newcomers. I think it is easier for them to say "I must have a look into a services.yml|xml|php file. Or search through them." |
@stof actually, it is possible to inject dependencies into private properties if the class isn't private, just use an approach similar to what @nicolas-grekas did in #20973 |
I'm 👍 for the first alternative, I'm not fond of the feature itself but it would have the merit to be more consistent with the annotations system elsewhere. I would also add: namespace App;
/** @Service */
class NewsletterManager
{
//...
} which would be equivalent to: namespace App;
/** @Service('App\NewsletterManager') */
class NewsletterManager
{
//...
} As it would be more in line with #21133 and auto-wiring. @javiereguiluz any plan for other services parameters, e.g. auto-wired, private & co.? |
I'm not sure that promoting the use of annotations for service definitions is a good idea:
IMO, the PHP syntax should be sufficient to specify dependencies of a class (and it's what we do with autowiring, in most cases type hints are enough to inject the good service). |
@theofidry: i would like it if without name, |
No you cannot. The only way to access private properties is from the class itself (out of our control) or through Reflection (which is bypassing the API of the class, and so a no-go IMO). |
@dunglas No, that would be bad as the annotation parser breaks on unknown annotations, which would force to install all containers all the time.
that's kind of the reason why I asked whether |
I agree with @dunglas. Coupling domain objects with ORM is quite OK. You can hardly switch ORM. In case of validation it's mostly OK. The objects are mostly coupled with the validators. In case of routing it's worse but controllers are mostly container aware and depend on Symfony services because it's comfort so it's acceptable tradeoff for me. But in case of DI... Many if not most classes are reusable and framework independent. Actually DI container independent. |
Imho, the question of defining services through annotation is not whether or not it is a good idea for libs and bundles to uses them because the answer is no. For me, the libs and bundle question is irrelevant and the only question to ask is whether or not it is useful for the services you write in your application and can make DI usage easier and faster (don't forget: your application is already coupled to the framework, you won't change it easily). And if I don't really like it for some reasons, I think it is useful and makes a lot of sense with autowiring and the direction DI is taking. |
I'm really not a big fan of configuring services with annotations. It seems to be simplier at first sight but it adds a lots of magic from a DX point of view. To me it's even more longer and complicated to write than a simple YAML file. |
Hey @dunglas, there is a start of a discussion of a PSR/standard of |
What would be a nice first step however is to have native annotations in PHP... |
I don't know why we still don't have those :/ |
i don't know if that does target this issue too, but i would like to hear your guys opinions about it. i often do this in my bundles because i need some object repositories in my other services: app.base_repository:
abstract: true
class: Doctrine\ORM\EntityRepository
factory: ['@doctrine.orm.entity_manager', getRepository]
app.xyz_repository:
parent: app.base_repository
arguments:
- AppBundle\Entity\Xyz whould such a or would some kind of dynamic Service Provider that does look for "app.%s_repository" help? |
The annotation would not help here as you need a factory. Btw, be careful that defining the doctrine repository as a service is incompatible with resetting the entity manager (as the repository would not be replaced by the new repository created by the new entity manager), so I would advice against this. |
@stof if you have to reset your entity manager, you probably have a bigger issue. I always recommend that if the entity manager is closed, it thus crashed somewhere and it's not safe to continue the normal flow. |
@iltar there is a very valid use case for resetting the manager: long-running process where you want to be able to process the next message in the queue even if the processing of the previous message failed (having to make your consumer process crash all the time is not good). |
@stof and what about all the (now) un-managed entities floating around everywhere and nowhere? If you try to flush an entity used by another uow, it will most likely cause a bunch of issues and there are probably a dozen more side effects. However, this is slightly going off-topic. My opinion on service annotations in a class? Bad idea for the aforementioned reasons, I don't think it's worth it. I think autowiring is a better path to go down on and is not too complex to add features to. |
@iltar I'm also clearing the entity manager between processing of different queue messages anyway (otherwise I would be leaking memory). But anyway, this is off-topic for this issue. |
Alright. I can understand Annotations for Asserts. Yeah, they done lot of nice job. In my, opinion, code looks better, because assert statement is above statement and for developer is easier to read. Even for ORM, annotations looks barely good enough, but this often generate lot of them. To this day, I was thinking, that Route annotations are the most... stupid. You have to read every controller to find routes to your commands... |
Not a big fan of annotations, if we inject the service using annotations, we link the logic of services to the framework and the class become dependant of the framework logic, i support the vision @dunglas on this part. Same thinking about getters injection, this way, the logic of the class become dependant of the framework component, what if we want to inject some classes coming from Zend ? Or just moving our service into Zend ? I always inject using the constructor, this way, if i need to change the dependencies, i just change the constructor. |
Lets be honest, you don't change the framework of your project every morning. And when you do change the foundation of your project (yes,, the framework is the foundation of the project) it's a massive task and 2 annotations won't change that much especially if the annotation is only used with autowiring (to not have to write the same 4 lines of yaml for every service) |
For sure, it's just about linking the logic to the framework, not complaining about the changement of framework on a project, i don't change the foundation every morning. |
Just for reference, this is how autowiring works in Spring Framework ... and still most developers consider that framework professional, great, etc. // setter injection
public class Customer
{
// ...
@Inject
public void setPerson(Person person) {
this.person = person;
}
}
// Constructor injection
public class Customer
{
@Inject
public Customer(Person person) {
this.person = person;
}
// ...
}
// Property injection
public class Customer
{
@Inject
private Person person;
// ...
} The Symfony autowiring feels like "half-done": you want to go fast with autowiring ... but you still need to waste your time with config files. If you want autowiring, you shouldn't have to deal with that. |
@javiereguiluz it will not be the case anymore if/when we'll integrate ActionBundle. |
About Spring, |
I'm in favour of this proposal. I have a project that is very heavy on server-side processing (not a simple CRUD REST backend) with lots of services, and I'm finding that maintaining both YAML files and the classes is becoming a hassle. I'm trying out JMSDiExtraBundle and I think it's an improvement, but this proposal seems to have a simpler syntax, which I like. Specifically, I don't like complex nested annotations because my IDE (PhpStorm) can't syntax-check them:
In cases where type-hints are not sufficient (eg, when autowiring doesn't work), why not something more like this:
|
I've added a possible implementation at #21376 |
Closing because the related PR (#21376) was closed as "won't fix" and we're exploring other ways to make the DI better. |
Context
One of the goals of Symfony 3.3 is to make the DependencyInjection configuration simpler for newcomers. In Symfony we already have service autowiring and soon we could also have getter injection.
Proposal
However, in my opinion they are missing something. They try to reduce the config needed to define services ... but maybe we should instead remove the config needed to define services. We've done this multiple times in Symfony:
@Route
removes the need forrouting.yml | .xml | .php
@Assert
removes the need forvalidation.yml | .xml | .php
@ORM
removes the need forEntity.orm.yml | .xml
So, why not define a new
@Service
annotation to remove the need forservices.yml | .xml
.In practice
It would be very similar to JMSDiExtraBundle. You can configure the services in the PHP file of the related class, without having to define any file.
Alternative 1
Alternative 2
Alternative 3 (PHP 7+)
Alternative 3 (PHP 5)
Comparison
For comparison purposes, this is the traditional Symfony service configuration (which requires 1 PHP file + 1 config file):
And this is the autowiring configuration, which also requires 1 PHP file + 1 config file and the work to do is almost the same as before:
Mandatory reminder for anything related to autowiring: this feature would be 100% optional and it won't have any impact in your application if you don't want to.
The text was updated successfully, but these errors were encountered: