Symfony 3.3: Upgrade, Autowiring & Autoconfigure

Buy Access

Symfony 3.3 comes with some FRESH new dependency injection changes! Yes, autowiring, autoconfigure, auto-registration of services! Autobots, roll out! In this tutorial, we will:

  • Upgrade to Symfony 3.3
  • Learn about the new _defaults keyword
  • Update service ids to be class names
  • Discuss public versus private services
  • Upgrade to the new services.yml format without breaking our project
  • Auto-register all classes in src/AppBundle as services (what!?)
  • Handling classes that have multiple services registered
  • Understanding autowiring and aliases
  • Using named arguments when autowiring fails

Say hello to the bigger, faster, strong way of service configuration!


Your Guides

Ryan Weaver

Questions? Conversation?

  • 2017-07-31 weaverryan

    Hi Igor!

    Ah, then I think we have the cause! It looks like JMSDIExtraBundle creates a proxy for the entity manager. And - I believe because that proxy is just saved to a file in the cache directory - this class_exists() check fails and the entire container rebuilds... constantly. To know if this is the issue, add this config to config.yml


    jms_di_extra:
    doctrine_integration: false

    This will remove the proxy. However, this also removes the ability to inject dependencies into your repository (http://jmsyst.com/bundles/J.... Try the config to see if it helps. I believe this is a bug in JMSDiExtraBundle - a bug with its compatibility with something new in Symfony 3.3 (but I'm not 100% sure - could be a subtle Symfony bug). If you're interested, I would open an issue on the JMSDiExtraBundle repo.

    Let me know if it works!

  • 2017-07-26 Igor Weigel

    Hi Ryan,

    A) I do have the long proxy class: EntityManager597857be7d816_546a8d27f194334ee012bfe64f629947b07e4919\__CG__\Doctrine\ORM\EntityManager

    B) I do have JMSDIExtraBundle

    Thanks

  • 2017-07-10 weaverryan

    Hi Igor!

    Apologies for my slow reply! Ok, this is very interesting, and you've done some great debugging! Your ClassExistenceResource dump is really interesting: something is actually looking for the EntityManager as a proxy class. I don't see that in my config (I tried on this project). So, I have a few questions:

    A) If you run ./bin/console debug:container doctrine.orm.default_entity_manager, what is the class? Is it Doctrine\ORM\EntityManager or is it this long EntityManager...\__CG__\... proxy class?

    B) Do you have any special bundles installed that might be adding any other magic. Like JMSDIExtraBundle? I'm trying to figure out what causes the EntityManager to become proxied like this. I expect the proxy is at the root of the problem, but I can't repeat it.

    Cheers!

  • 2017-07-05 Diego Aguiar

    Hey Igor Weigel

    We are investigating your situation, it's really interesting :)
    Can you show us how look's like your test code ? the whole file if possible

    Cheers!

  • 2017-06-30 Igor Weigel

    I debugged a bit more and here is what happens in phpunit.
    I have some @dataProvider for some of my tests, which are called in early stage. If I debug the Kernel $cache->isFresh() at this point (while in a @dataProvider), the cache is fresh (all good).
    But once I'm in a test (so not in the @dataProvider anymore), $cache->isFresh() returns false, and the problems begin...^^"
    But I have no idea why..

  • 2017-06-30 Igor Weigel

    I tried to debug it for a while, and here is the difference with and without the autowiring:
    docroot/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php:541


    if (!$cache->isFresh()) {

    with autowiring, the test result is true, therefore cache is rebuilt. without autowiring it doesn't go in the if statement.
    I've debug a little more to see what changed the result, and it seems that it comes from here:
    vendor/symfony/symfony/src/Symfony/Component/Config/ResourceCheckerConfigCache.php:113


    if ($checker->isFresh($resource, $time)) {


    and when I print the $resource, here is what I have:


    object(Symfony\Component\Config\Resource\ClassExistenceResource)#14539 (2) {
    ["resource":"Symfony\Component\Config\Resource\ClassExistenceResource":private]=>
    string(101) "EntityManager59564fd03a693_546a8d27f194334ee012bfe64f629947b07e4919\__CG__\Doctrine\ORM\EntityManager"
    ["existsStatus":"Symfony\Component\Config\Resource\ClassExistenceResource":private]=>
    int(0)
    }

    I was not able to understand more from here.. or to change it..

  • 2017-06-30 Igor Weigel

    I seem to face a major problem, with tests (unit and functional).
    Since now all dependencies are handled on container build (which takes more time with autowire), and phpunit rebuild the container on every request with


    \Symfony\Bundle\FrameworkBundle\Test\KernelTestCase::bootKernel()


    my tests take forever now, and when I mean forever, I mean that I was still not able to run them all (over night..).
    Do you have any solution for that by chance?

  • 2017-06-28 weaverryan

    Hey Igor Weigel!

    GREAT question - and it's something we need to document. The proper solution would be to create a public alias for the service you want to test in the test environment only:


    # app/config/config_test.yml
    services:
    test_alias.AppBundle\Service\MyService: ' @AppBundle\Service\MyService'

    Then, you would actually fetch test_alias.AppBundle\Service\MyService out of the container in your unit test. It's a nice idea too: you only need to expose your service as public in the test environment.

    Cheers!

  • 2017-06-28 Igor Weigel

    What if you do container->get() some services in tests.
    I see one example from the Symfony documentation here http://symfony.com/doc/curr...
    How would this be handled with private services?

  • 2017-06-15 Lasha Kitia

    As always, Knp creates useful courses and everything is clearly explained.
    Thank you Ryan for this!

  • 2017-05-26 Dan Costinel

    Looking forward to it :)