The container started to cost more maintenance than the features it provided, so we tried an experimental switch to Laravel. To our surprise, this helped us to make our tests run 7x faster.
It's all about the Downgrade
How is this possible? Apart from the 3 changes we mentioned in the article regarding downgrading and scoping - the size matters.
During downgrade and scoping, all the /vendor is shipped with the Rector project. That means every dependency is scoped and downgraded - line by line.
What about features? Rector is a CLI app, and it needs a simple dependency injection container:
- autowire service constructor,
- pass tagged services,
- callback to avoid circular dependencies.
If two packages are compatible, the more lines mean only more features we will never use.
Downgrading is a challenging process and sometimes it's a dead end - e.g., downgrading PHP code that uses PHP 8.0 attributes and doesn't provide a fallback feature on PHP 7.2 cannot be automated. Fewer lines mean fewer pitfalls to worry about.
Smaller is Better
Let's compare container package sizes without knowing the package name.
Guess which is which ↓
Filesystem count
Directories ......................................... 14
Files .............................................. 186
Lines of code count / relative
Code ................................... 17 694 / 79 %
Comments ................................ 4 698 / 21 %
Total .................................. 22 392 / 100 %
- 17 694 lines of code
Then the other:
Filesystem count
Directories .......................................... 0
Files ................................................ 6
Lines of code count / relative
Code .................................... 1 091 / 56.1 %
Comments .................................. 855 / 43.9 %
Total ................................... 1 946 / 100 %
- 1 091 lines of code
That means one is 16 x size greater than the other.
Results revealed:
- the symfony/dependency-injection 6.3 ..... 17 694 lines
- the illuminate/container 10.20 ..................... 1 081 lines
We also used symfony/http-kernel for the kernel test case to run, but we skipped transitional packages for the sake of comparison simplicity.
How to Upgrade your Extensions?
Rector 0.18 is the first release with a Laravel container. Now, we're testing in the wild.
If you've used bare RectorConfig class to set up your configuration, no upgrade is needed.
Few changes are required if you've used Symfony internal methods or maintain a custom Rector extension.
The way services are registered:
- Before, you had to register every single service, even if fully autowired.
- Now, you only register the services requiring a scalar parameter, factory, or tagged services.
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
- $services = $rectorConfig->services();
- $services->set(App\SomeService::class);
+ $rectorConfig->singleton(App\SomeService::class, function () {
+ return new SomeService('some parameter');
+ });
};
Tag service:
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
- $services = $rectorConfig->services();
- $services->set(App\SomeService::class)
- ->tag(SomeInterface::class);
+ $rectorConfig->singleton(SomeService::class);
+ $rectorConfig->tag(SomeService::class, SomeInterface::class);
};
You can drop the $services->defaults() calls ultimately, as this is now included in the core:
- $services = $rectorConfig->services();
- $services->defaults()
- ->public()
- ->autowire()
- ->autoctonfigure();
Also, PSR-4 autodiscovery is not needed anymore, as services are created for you:
- $services->load('Rector\\', __DIR__ . '/../packages')
- ->exclude([...]);
That's it! Find more in Laravel documentation and pull-request with container switch in Rector.
Is there something missing? Let us know to update this post. Thank you!
Happy coding!