If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Autoloading: it's like plumbing. You forget it's there, but when it's gone, well, let's just say you have to go outside a bit more often.
Autoloading is the magic that lets us use classes without needing to require
or include
the file that holds them first. We used to have include
statements everywhere, and well, it was terrible.
But an autoloader has a tricky job: given any class name, it needs to know the exact location of the file that holds that class. In many modern projects, including ours, Composer handles this, and there are two pieces to understanding how it figures out what file a class lives in.
When we create an Event object, Composer's autoloader knows that this class
lives inside src/Yoda/EventBundle/Entity/Event.php
. How? It just takes
the full class name, flips the slashes, and adds .php
to the end of it:
Yoda\EventBundle\Entity\Event
src/Yoda/EventBundle/Entity/Event.php
As long as the namespace matches the directory and the class name matches
the filename plus .php
, autoloading just works. Let's mess this up - let's
rename the Entity
directory to Entity2
:
use Yoda\EventBundle\Entity\Event;
// the src/Yoda/EventBundle/Entity/Event.php file is "included"
// .. so the file better exist and "house" the Event class!
new Event();
If we run play.php
now, it fails big:
Class
Yoda\EventBundle\Entity\Event
not found
The autoloader is looking for an Entity
directory. Rename the directory
back to Entity
to fix things.
Right now, it almost looks like the autoloader assumes that everything must
live in the src/
directory. So how are vendor classes - like Symfony - loaded?
That's the second part. When we fetch a library with Composer, it configures its autoloader to look for the new classes in the directory it just downloaded.
Open up the vendor/composer/autoload_namespaces.php
file. This is generated
by Composer and it has a map of namespaces to the directories where those
classes can be found:
// vendor/composer/autoload_namespaces.php
// ...
return array(
'Symfony\\' => array($vendorDir . '/symfony/symfony/src'),
// ...
'Doctrine\\ORM' => $vendorDir . '/doctrine/orm/lib/',
'Doctrine\\DBAL' => $vendorDir . '/doctrine/dbal/lib/',
'Doctrine\\Common\\DataFixtures' => $vendorDir . '/doctrine/data-fixtures/lib/',
// ...
);
So when we reference a Symfony
class, it does the slash-flipping trick,
and then looks for the file starting in vendor/symfony/symfony/src
:
Symfony\Component\HttpFoundation\Response
vendor/symfony/symfony/src/Symfony/Component/HttpFoundation/Response.php
Now you know all the secrets about the autoloader. And when you see a class
not found error, it's your fault. Sorry! The most common mistake is easily
a missing use
statement. If it's not that, check for a typo in your class
and filename.
Yo Ivan!
Cool question :). There is and is not a way to do this. This file specifically is static - you can't control what goes in there. However, there is another file called var/cache/dev/classes.php
that is just like this, and this one is dynamic. Currently, you need to create a dependency injection extension class to add more stuff to this: http://symfony.com/doc/current/bundles/extension.html#creating-an-extension-class. Then, you can add classes to it via addClassesToCompile
http://symfony.com/doc/current/bundles/extension.html#adding-classes-to-compile
Just make sure you don't put TOO much inside there :). It does speed things up a little bit, but it can come at a cost: if you add a class to this file, but you do not need that class on every request, then it is unnecessarily loaded into memory.
Have fun!
// composer.json
{
"require": {
"php": ">=5.3.3",
"symfony/symfony": "~2.4", // v2.4.2
"doctrine/orm": "~2.2,>=2.2.3", // v2.4.2
"doctrine/doctrine-bundle": "~1.2", // v1.2.0
"twig/extensions": "~1.0", // v1.0.1
"symfony/assetic-bundle": "~2.3", // v2.3.0
"symfony/swiftmailer-bundle": "~2.3", // v2.3.5
"symfony/monolog-bundle": "~2.4", // v2.5.0
"sensio/distribution-bundle": "~2.3", // v2.3.4
"sensio/framework-extra-bundle": "~3.0", // v3.0.0
"sensio/generator-bundle": "~2.3", // v2.3.4
"incenteev/composer-parameter-handler": "~2.0", // v2.1.0
"doctrine/doctrine-fixtures-bundle": "~2.2.0" // v2.2.0
}
}
Sorry but I have a tricky question.
There is a file called /app/bootstrap.php.cache which contains all Symfony Components classes for the purpose of faster loading.
But what if I want to add all /vendor/ classes to this cache? Any possible way to do that?