Symfony bundle dependencies
Once we have talked about PHP components, let’s now talk about Symfony bundles. This is something much more complicated, because a Symfony Bundle is a PHP library that is co-existing in a framework, so it is not as easy to check all our PHP class dependencies.
The question we must ask ourselves when trying to resolve any Symfony bundle dependency map is… What do I really need to make this bundle work in any Symfony project?
This is one of the things Symfony doesn’t solve yet. How can a bundle depend on another bundle, but not only in the composer layer but as well in the application layer?
Well, there is a package for that (remember to star it if turns out useful for you).
This package allows you to create bundles with other bundle dependencies very easily. By using this package you will be able to say… okay composer, download this bundles, I need them to instantiate my bundle… and Symfony application, as soon you instantiate my bundle, please, install these other bundles as well before without any need to modify the kernel. Of course, this is only possible if the project works with that package as well.
If the project is not using this package, then the behavior of your bundle won’t change at all.
To resolve all your bundle dependencies you need to take a look as well at your Dependency Injection definition. Let’s imagine we have a bundle with this DIC definition.
When Symfony tries to resolve this file, it needs as well all the definitions of the arguments (dependencies). The bundles that have these definitions automatically become your bundle dependencies.
The hard work here is to know which bundles have all these service definitions, and that is not always that simple. In that case, for example… Which package has the @twig service? We could think easily… well, twig/twig for sure has the class we are injecting here.
And you’re right, so if any of your classes, in that case My\Service\Class needs a class from the package twig/twig, this package will have to be required by your bundle.
But is that the real answer we need right now? Not at all. The question is not which package provides me with the Twig class, but with the @twig service, and this one is not twig\twig as this is only a PHP library, framework agnostic.
For this reason, we have a bundle called TwigBundle. This bundle, as well as other needed things, creates a new service called twig. This Bundle is required not only because we need the code under our vendor folder, but also because it has to be instantiated when our bundle is instantiated.
If you decide to work with the symfony bundle dependency package, then this code is for you.
If you don’t use this package, then you should add the TwigBundle instance in your AppKernel.
Requiring the Framework
How about this services file? What dependencies do you think your package has?
Some people can say if quickly… the EventDispatcher is a requirement… but we have the same problem as before. The Event Dispatcher is a Symfony component, and has nothing to do with the exposure of their classes in the Symfony Framework dependency injection definition.
Symfony provides as well a bundle called FrameworkBundle. Its mission is, in addition to creating all the working environment for your project (the framework itself), to expose all needed services to the DIC. One of them is the Event Dispatcher from the component (if you check the composer.json file of that bundle you will discover that the symfony/event-dispatcher package is a requirement).
So, some of your bundle services should require as well this bundle.
This bundle is almost always required by all bundles (at least, it should), so make sure you’re really tolerant with its version, or you will make your bundle less usable than it could be.
Many packages are actually requiring a very restrictive version of Symfony. This fact has not been a problem during the latest 4 years, but nowadays Symfony v3.0.0 is going to be a reality soon, so all these packages need to make two easy things
– Check if your bundle introduces a Symfony ~2.8 deprecated feature.
– If it does, update your bundle to avoid this deprecation
– Update your requirements to work as well with Symfony ^3.0.
Check that your Symfony requirements then are still valid. For example:
Applying this new requirement with Symfony ^3.0 maybe you had to use a new feature that was introduced in Symfony ^2.7.3. In that case, your composer.json is invalid, and if you have covered your class with tests and you run your tests with –prefer-lowest, then you will have some fails there.
You will have to update your dependency properly.
As you may already noticed, development dependencies are not loaded recursively. This means that the require-dev block of your require-dev packages is completely ignored.
In some way, this is great because you can define specifically what packages you need for your development (testing, mostly), without being worried about all the packages that require you.
In some other way, this can be bad… well, yes, you must know exactly all your dependencies for testing (there should be only a few…), so in that case, just make sure you know your application 😃
And then the question is… should I require PHPUnit or other testing libraries, as well as lints and formatters?
Again, some people will tell you… don’t do that! Your development and testing deployment will require more disk and more resources for composer. Well, sure, but if you depend on the pre-installed PHPUnit version, then you can have some trouble when testing.
I really need these versions. No others but these. For example, some lints can add some logic, or even change it. Because we’re responsible for our bundles, components or code, we should trust as less as possible in what other people can do to our dependencies (somehow we really trust a lot of packages by adding the ^2.2 symbol, but in that case we do it not for us but for our users). In testing mode, and because fortunately require-dev block is not recursive, we can perfectly be restrictive with the version we want, and as long as we want/need to change it… just do it 🙂
Trusting open source is something you cannot do blindly. Your project is your business, and you need to know that is safe from third party version errors and issues.
If you trust a package, like I do for example in Symfony, then use the semantic version notation in your requirements. Believe that the community will never allow back compatibilities breaks, or they will fix them all as soon as possible when introduced.
If you trust a library because it is tested, but you don’t trust their version policy, then just block the version (knowing that this restricts the compatibility with other packages), or make some push to this community for a really semantic version policy.
If you don’t trust a library at all, then don’t use it. That simple.
So, that’s it. 😎
I highly recommend you, open source lover, to take as much care as possible of your package dependencies. A healthy and useful package is a package used by tons of people. Offer them some confidence and you will get a lot of feedback in return.
Share your work as much as you can, and don’t be afraid of your errors, they will be your biggest reasons for being a better developer day after day, and remember that all of us were inexperienced once.
Error is first step to success…