Yo guys! Welcome to episode 2 of our Symfony 4 series! This is a very important episode because we're going to take just a little bit of time to really understand how our app works. I'm talking about how configuration works, the significance of different files and a whole lot more.

And yea, there's a reason we're doing this work now and not in a distant, future episode: by understanding a few FUNdamentals, everything else in Symfony will make a lot more sense. So let's dig in and get to work!

Code Download

As always, if you code along with me, we instantly become best friends. Pow! Download the course code from this page and unzip it. Inside, you'll find a start/ directory with the same code you see here.

Open the file for a whimsical space poem... and instructions on how to get the app setup. The last step will be to find a terminal, move into the project and run:

./bin/console server:run

to start the built-in PHP web server. Ok! Let's load up our app! Find your browser and go to http://localhost:8000. Welcome back to... "The Space Bar": the latest and greatest intergalactic news and sharing site for astronauts and non-human-eating aliens across the universe. Or, it will be when we're finished.

Services: Objects that do Work

Let's start off stage 2 of our journey with a pop quiz: in episode 1, what did I say was the most important part of Symfony? If you answered Fabien... you're technically right, but the real answer is: services. Remember: a service is an object that does work: there's a logger service and a Twig service.

To get a list of the services that we can access, you can go to your terminal, open a new tab, and run:

./bin/console debug:autowiring

For example, to get the logger service, we can use the LoggerInterface type-hint:

... lines 1 - 4
use Psr\Log\LoggerInterface;
... lines 6 - 11
class ArticleController extends AbstractController
... lines 14 - 39
* @Route("/news/{slug}/heart", name="article_toggle_heart", methods={"POST"})
public function toggleArticleHeart($slug, LoggerInterface $logger)
... lines 45 - 46
$logger->info('Article is being hearted!');
... lines 48 - 49

You can see this in our controller: yep, as soon as we add an argument with the LoggerInterface type-hint, Symfony knows to pass us the logger service.

Where do Services Come From? Bundles

But... where do these service objects come from? I mean, somebody must be creating them in the background for us, right? Totally! It's not very important yet, but every service is stored inside another object called the container. And each service has an internal name, just like routes.

And what exactly puts these services into the container? The answer: bundles. Bundles are Symfony's plugin system. Look inside config/ and open a bundles.php file there:

... lines 1 - 2
return [
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true],
Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true, 'test' => true],

Yep, our app has seven bundles so far - basically seven plugins. We installed 6 of these in episode 1: the recipe system automatically updates this file when you require a bundle. Sweet!

So let's put this all together: Symfony is really nothing more than a collection of services. And bundles are what actually prepare those service objects and put them into the container. For example, MonologBundle is responsible for giving us the logger service.

Bundles can also do other things - like add routes. But they really have one main job: bundles give you services. If you add a bundle, you get more services. And remember, services are tools.

So let's install a new bundle and play with some new tools!

Leave a comment!

  • 2018-12-21 Victor Bocharsky

    Hey Abelardo,

    No problem! I'm glad we don't have any issues on our side.


  • 2018-12-21 AbelardoLG

    My fault!
    I was wrong with my last comment since I bought another course and not this one!
    Sorry for the inconveniences. Best regards.

  • 2018-12-21 Victor Bocharsky

    Hey Abelardo,

    We're sorry about that! Such requests you better write directly to our email because it's not a good idea to post important credentials in comments. Btw, I've already replied to your previous email about this problem to clarify some things first and waiting feedback from you.


  • 2018-12-21 AbelardoLG

    Hi everyone!

    I paid for this course but I can't download the code for it.

    Could anybody help me with this issue, please?

    Best regards.

  • 2018-12-05 Usuri

    Success.:) I didn't realised there is no .htaccess in public folder. I added it manually and everything works. :)

    Thank you for help.

  • 2018-12-05 Vladimir Sadicov

    Now I think we need to remove previous changes. and best solution for you will be to run

    composer require symfony/apache-pack

    It will add default .htaccess file and I think it would work after it

  • 2018-12-05 Usuri

    It works. So what should I change in mod_rewrite?
    I checked for url "kurssymfony.lok/index.php" and then debug toolbar works.

  • 2018-12-05 Vladimir Sadicov

    This looks good. Now I see it's mod_rewrite error

    Try to open


    if it works, than it's 100% problem in rewrite rules

  • 2018-12-05 Usuri

    Apache error log contains this:

    "[Wed Dec 05 09:34:14.784030 2018] [ssl:warn] [pid 20324:tid 360] AH01909: server certificate does NOT include an ID which matches the server name
    [Wed Dec 05 09:34:14.828072 2018] [core:warn] [pid 20324:tid 360] AH00098: pid file C:/xampp/apache/logs/ overwritten -- Unclean shutdown of previous Apache run?
    [Wed Dec 05 09:34:14.830074 2018] [ssl:warn] [pid 20324:tid 360] AH01909: server certificate does NOT include an ID which matches the server name
    [Wed Dec 05 09:34:14.855098 2018] [mpm_winnt:notice] [pid 20324:tid 360] AH00455: Apache/2.4.29 (Win32) OpenSSL/1.1.0g PHP/7.2.0 configured -- resuming normal operations
    [Wed Dec 05 09:34:14.855098 2018] [mpm_winnt:notice] [pid 20324:tid 360] AH00456: Apache Lounge VC15 Server built: Nov 3 2017 10:30:36
    [Wed Dec 05 09:34:14.855098 2018] [core:notice] [pid 20324:tid 360] AH00094: Command line: 'c:\\xampp\\apache\\bin\\httpd.exe -d C:/xampp/apache'
    [Wed Dec 05 09:34:14.856099 2018] [mpm_winnt:notice] [pid 20324:tid 360] AH00418: Parent: Created child process 10848
    AH00548: NameVirtualHost has no effect and will be removed in the next release C:/xampp/apache/conf/extra/httpd-vhosts.conf:44
    [Wed Dec 05 09:34:15.271498 2018] [ssl:warn] [pid 10848:tid 460] AH01909: server certificate does NOT include an ID which matches the server name
    [Wed Dec 05 09:34:15.309534 2018] [ssl:warn] [pid 10848:tid 460] AH01909: server certificate does NOT include an ID which matches the server name
    [Wed Dec 05 09:34:15.335559 2018] [mpm_winnt:notice] [pid 10848:tid 460] AH00354: Child: Starting 150 worker threads."

    Browser network log shows "404 - object not found".

  • 2018-12-05 Vladimir Sadicov

    You can check it in error log configured in your xampp instance and Network panel of your browser dev tools.

  • 2018-12-05 Usuri

    Now debug bar appeared but only with error message: "An error occurred while loading the web debug toolbar." Is there any log where I can check what is wrong wit toolbar?

    BTW. Similar debug toolbar for laravel works without problems.

  • 2018-12-04 Vladimir Sadicov

    Hey Usuri, I think you need to enable apache mod_rewrite and add to your config into Directory block this code or something like this:

    <ifmodule mod_rewrite.c="">
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php [QSA,L]

    this is just example it could be little different depending on your apache version.

    Hope this will help


  • 2018-12-04 Usuri

    Thank you.

    This is my config in "httpd-vhosts.conf":

    <virtualhost *:80="">
    DocumentRoot "C:/xampp/htdocs/skeleton_symfony/public"
    ServerName kurssymfony.lok

    <directory "c:="" xampp="" htdocs="" skeleton_symfony="" public"="">
    Options Indexes FollowSymLinks Includes ExecCGI
    AllowOverride All
    Order allow,deny
    Allow from all

  • 2018-12-04 Vladimir Sadicov

    Hi Usuri

    It should work no matter what are you using symfony server or xampp or other server. It looks like your virtual host are not fully configured. If you share it with us we can try to look together and fix everything.


  • 2018-12-04 Usuri

    I have a small question about technical issue. On my windows system I use XAMPP and configure virtual host for localhosts domains. So I created virtual host for this tutorial with local domain address "symfonytut.loc" and it works. But site doesn't shows me debug bottom bar on page. If I run symfony web server (php bin/console server:run) debug bar appeared. Is there any chance to see that debug bar when use virtual host?
    Thank you
    BTW. Great tutorial :)

  • 2018-08-06 Victor Bocharsky

    Hey Mike,

    Good question, let's figure out! Actually, we didn't install it explicitly, but it was installed as a dependency. Let me show you, we did install "symfony/debug-pack" of version v1.0.4 that requires "symfony/phpunit-bridge" which in turns put that phpunit.xml.dist file for us. But this extra dependency was removed in the newer version v.1.0.5, so if you install the newer version of "symfony/debug-pack" - you won't have phpunit.xml.dist file, i.e. you need to require "symfony/phpunit-bridge" explicitly, see


  • 2018-08-04 Mike

    In the main directory of this app is a phpunit.xml.dist file, which wasnt available in the first chapter. How have you "installed" PHPUnit for this project?

  • 2018-06-06 Diego Aguiar

    Good explanation :)

  • 2018-06-06 toporovvv

    Your controller extends AbstractController (/vendor/symfony/framework-bundle/Controller/AbstractController.php), which uses ControllerTrait (/vendor/symfony/framework-bundle/Controller/ControllerTrait.php). So render(), redirect(), getDoctrine(), getUser() and lots of other useful functions, that we use often in our actions, live there.

    You can also find method getSubscribedServices() in AbstractController which allow you to understand what services do we have out of the box in every action. Other services you need to inject via constructor or methods parameters as Ryan said in a first course of this track.

  • 2018-06-04 Diego Aguiar

    Hey Peter Kosak

    Calling "$this->render()" works because your controller extends from Symfony's base controller, in that class, you can see the implementation of the method, In theory, all your services must be private, and you should use dependency injection in order to use them, but, there are still some public services, like Twig, that you can fetch them out from the container.

    I hope it helps you clarifying things a bit :)

  • 2018-06-04 Peter Kosak

    This is probably stupid question but I am still thinking about it.

    Why do we need to inject LoggerInterface (service) into route to get its methods but we are not injecting Twig (environment service).

    Basically my question is how is that possible that when I call $this->render() it knows that it is a twig method so it is looking in Environemt service but when I call $this->info() it is failing?

    I try it again because it is not clear:
    Why we are not injecting into each route Environment but we have to inject Logger?

    Where is this specified what services have to be injected and which one does not have to be?

  • 2018-04-13 Diego Aguiar

    Hey Vlad

    Some times we do a couple of changes from course to course, but usually we mention that. I believe, this time you can safely continue with your project from the past course


  • 2018-04-13 Vlad

    Is the start source code of this tutorial builds on the finish source code from the previous tutorial or there have been changes in between?

  • 2018-02-14 Victor Bocharsky

    Hey kribo ,

    Well, the process is the same as for Symfony 3.x. Well, actually, you can look at any third-party Symfony bundle, like and steal the skeleton from it. Also, Flex can make installing your bundle more smooth if you will create a recipe for it and publish it here: - it has a link about how to create your own recipe.


  • 2018-02-13 kribo

    How does one create a reusable bundle in symfony 4.0?