October 10, 2022

Understanding Shopware 6 PHPDocs annotations

Yireo Blog Post

In any modern PHP codebase, there are PHP comments to enhance the application: Varying from simple comments to annotations and even Attributes. In Shopware 6, this is no different. But there are some annotations that require a bit more details to understand properly. Here it goes.

The basic

The PHP comments of Shopware 6 are stacked with @var, @param, @return, @throws and other regular PHP doc statements. You can find them on Also, there are some PHP 8.1+ Attributes (well, at least #[\ReturnTypeWillChange]). But this blog is not about these.

There are the @OA (Open API) specifications. And the Symfony part of Shopware 6 is using various annotations as well like @Route and @RouteScope.

I've summed all these up as basics. I'm not saying that the above is simple. Instead, I would like to focus upon 3 specific annotations instead: @final, @internal and @deprecated - because they are often misunderstood. And I say annotations but it would actually be more correct to just call them PHP comments, because there is no PHP mechanism that uses these PHP comments to do magic stuff.


First of all, @final. PHP OOP knowledge tells us that a class that is declared as final can't serve as a parent class anymore, so can't be extended by a child class. But the final keyword of PHP is different from the @final annotation. The PHPDocs state the following about this: "Document a class method that should never be overridden in a child class". Sometimes @final is referred to as the PHP4 equivalent of the final keyword for PH 5+ classes.

However, note the difference: With the final keyword, you can't extend the class anymore. But with the @final annotation, you should not extend the class. The difference is whether Shopware wants to enforce the final aspect on to the code. Sometimes it is better to just stick to friendly PHPDocs. For instance when a class was once non-final and is then made final, it could be breaking other code depending upon this. Theoretically, such a final switch could eventually be made with a major version of Shopware. However, such a change will carefully be announced by deprecating things.


Code could be deprecated by adding the @deprecated annotation to it. In a lot of applications, deprecated code will be removed sometime in the future as well. This might be the case with some parts of Shopware code as well, but deprecation does not neccessarily mean removal (in general)!

Interestingly, with some occurrances of the @deprecated comments in Shopware, there is an additional comment behind it to clearify things: For example, @deprecated tag:v6.5.0 - reason:becomes-final - Will be @final - which means that no code is going to be removed but the @final comment will be added with the 6.5.0 release, so that from that point onwards, your code is better off by not extending upon that class.

Another example: @deprecated tag:v6.5.0 - reason:becomes-internal - Will be internal. In this case, code will become internal. But what does that mean?


This is actually the one I find most annoying. Not in general, but in a specific case: The Context. When using controllers, event listeners, page loaders and so on, there could be various dependencies that require a context. For instance, when searching repositories, you need to pass a @context to make this work. Normally, this context is already passed through method arguments or constructor injection.

But in some rare cases, there is no context available to you. Well rare is not rare at all, because every time you write a Symfony command, you are bumping into this: The only way to fetch a context is through the Context::createDefaultContext() ... And that specific call is marked @internal.

There is an issue on GitHub that kind of explains this as well: Using the default context in a Symfony command is not neccessarily bad - actually, it is the best way to get a context in that ... context. But the point is that the @internal comment is hopefully discouraging developers from using Context::createDefaultContext() all the time.

Just like @final, it boils down to the fact that the code keeps on working (for the time being, until the @deprecated comment tells us that the code is literally going to be removed, theoretically), but there are possibly better ways to accomplish things.


All of this was written by me - a non-Shopware employee. It could be that some of the assumptions are not entirely correction. If corrections are needed, I'm happy to incorporate them. Let me know.

Posted on October 10, 2022

About the author

Author Jisse Reitsma

Jisse Reitsma is the founder of Yireo, extension developer, developer trainer and 3x Magento Master. His passion is for technology and open source. And he loves talking as well.

Sponsor Yireo

Looking for a training in-house?

Let's get to it!

We don't write too commercial stuff, we focus on the technology (which we love) and we regularly come up with innovative solutions. Via our newsletter, you can keep yourself up to date on all of this coolness. Subscribing only takes seconds.

Do not miss out on what we say

This will be the most interesting spam you have ever read

We don't write too commercial stuff, we focus on the technology (which we love) and we regularly come up with innovative solutions. Via our newsletter, you can keep yourself up to date on all of this coolness. Subscribing only takes seconds.