PHP: attributes vs annotations. Optimising Doctrine metadata
Attributes are one of the new features of PHP 8.0. Attributes contain metadata for classes, fields and methods, which can be accessed using Reflection API. It sounds the same as annotations, what’s so special in this feature?
The difference is how both are structured. Annotations are simply phpDoc strings, so developers have to implement their own functionality to parse it in order to retrieve desired information.
Runtime parsing appears to be needed. How is it bad? Let’s take a look at how it affects performance using Doctrine as an example.
Like any ORM, Doctrine widely uses metadata for work. Particularly for mappings of business entities into DB tables. There are multiple implementations of metadata storage driver:
- XML driver
- YAML driver
- Static PHP driver
- PHP driver
- Annotations driver
- Attributes driver
We will examine drivers of annotations and attributes. Additionally we will test static and simple PHP drivers to compare manual metadata initialisation with previous options. Also we won’t forget to check annotations with APCu and Redis cache to find out whether they can solve potential performance issues.
In the test scenario 1 active user during 10 minutes requests metadata of all classes. Application consists of Nginx + PHP-fpm with enabled Opcache. The sources are available here.
Attributes have shown good performance even compared to PHP drivers. The difference between AttributeDriver, StaticPHPDriver and PHPDriver can be explained by the inaccuracy of measurements. The important result is that annotations are 3 times slower!
It is an interesting fact that cache didn’t manage to boost performance of the application. In Doctrine metadata for each class is stored in a separate cache record. Turns out, fetching and unserialising each record one by one isn’t much faster than simple annotation parsing.
If you are looking for a way to optimise your legacy application and all requests to DB have already been checked, annotations of controllers or ORM entities can be your next target.
However, there is a problem: legacy — it’s when you have a huge pile of code and now you have to refactor it all? This issue can be resolved using one more option, which you could have seen in the results of the benchmark. Code generation:
This option allows you to continue using annotations without need to refactor the whole project and at the same time to increase performance as annotations now are generated into code which is stored in Opcache!
Summarising:
- Is it worth using attributes instead of annotations everywhere possible in whole new projects? Definitely.
- Should you refactor annotations into attributes in your legacy application? It depends on non functional requirements (e.g. if you have the goal to optimise the project).
Code generation can be helpful: we use a similar script in our production environment (also you may want to try rector).
I hope this article was useful and introduced to you another advantage of attributes in comparison to annotations.