Showing posts with label generated files. Show all posts
Showing posts with label generated files. Show all posts

Monday, August 22, 2016

Symfony magic: generated PHP files

1.  bootstrap.php.cache


In versions 3.x is found at var/bootstrap.php.cache before was in app/ directory.

Loading all of classes from separate files on each request can result in some overhead. To reduce this overhead, the Symfony Standard Edition provides a script to generate a so-called bootstrap file, consisting of multiple classes definitions in a single file. By including this file (which contains a copy of many of the core classes), Symfony no longer needs to include any of the source files containing those classes. This will reduce disc IO quite a bit.

If you're using the Symfony Standard Edition, then you're probably already using the bootstrap file. To be sure, open your front controller (usually app.php) and check to make sure that the following line exists:

include_once __DIR__.'/../var/bootstrap.php.cache'

Note that there are two disadvantages when using a bootstrap file:

 - the file needs to be regenerated whenever any of the original sources change (i.e. when you update the Symfony source or vendor libraries)
 - when debugging, your breakpoints are never reached because the code is executed from bootstrap.php.cache. I wrote in this article about how to handle this.

If you're using the Symfony Standard Edition, the bootstrap file is automatically rebuilt after updating the vendor libraries via the composer install command.

See: http://symfony.com/doc/master/performance.html#use-bootstrap-files


2.  classes.php and classes.map


From Alan Storm blog:  "The generation of classes.php is a bit complicated. First, this file is not generated as part of Symfony's cache warmup. You can call

php app/console cache:clear

all you like, but that won't regenerate classes.php. Instead, you need to load an actual application page, or otherwise boot your application kernel.

#File: vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php
public function boot()
{
    //...
    if ($this->loadClassCache) {
        $this->doLoadClassCache($this->loadClassCache[0], $this->loadClassCache[1]);
    }
    //...
}    

It's the call to doLoadClassCache which triggers a call to ClassCollectionLoader::load

#File: vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php
protected function doLoadClassCache($name, $extension)
{
    if (!$this->booted && is_file($this->getCacheDir().'/classes.map')) {
        ClassCollectionLoader::load(include($this->getCacheDir().'/classes.map'), $this->getCacheDir(), $name, $this->debug, false, $extension);
    }
}

The work of generating classes.php is done in ClassCollectionLoader::load. Simple enough — except you need to pass in a list of classes you want to combine. These classes come from the classes.map file.

app/cache/dev/classes.map
app/cache/prod/classes.map

Of course, now our question is "where does classes.map come from?". The classes.map file is generated at the same time as the app container file, right after the call to $container->compile() in initializeContainer.

#File: vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php
protected function initializeContainer()
{
    //...
    if (!$cache->isFresh()) {
        //...
        $container->compile();
        //...
    }
    //...
}

3.  The Dependency Injection Container(DIC) class file


In a simplified way you can think about the  DIC as an array, having as keys the names of the services, and as values, anonymous functions that create a new instance of the needed class ( have a look at Pimple ). Symfony DIC is more complex, it allows services to interact using tagged services, and this interaction is implemented using some classes called Compiler Passes. Compiler Passes manipulate the services, and they are executed when the DIC is compiled.

In Symfony you do not edit a class to register your services directly into an array, instead you edit different configuration files (YML, XML). Each extra bundle that you install may add its own services that need to be registered. Also Compiler Passes need to be executed. 
All these haveto be "compiled" in order to result a PHP class file containing a big array of services and anonymous functions that instantiate new objects. The compilation process has loaded the services from the configuration, extensions and the compiler passes, the result is dumped so that the cache can be used next time. The dumped version is then used during subsequent requests as it is more efficient. The generated file can be found in the following locations for the dev and prod environments:
var/cache/dev/appDevDebugProjectContainer.php
var/cache/prod/appProdProjectContainer.php

If you're creating new services in Symfony, you'll often need to re-generate you container file. Doing so is as simple as clearing your Symfony cache — the Symfony kernel will regenerate these files automatically. If you're curious, the code that does this starts here

#File: vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Kernel.php
protected function initializeContainer()
{
    $class = $this->getContainerClass();
    $cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug);
    $fresh = true;
    if (!$cache->isFresh()) {
        $container = $this->buildContainer();
        $container->compile();
        $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass());

        $fresh = false;
    }

    require_once $cache;

    $this->container = new $class();
    $this->container->set('kernel', $this);

    if (!$fresh && $this->container->has('cache_warmer')) {
        $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir'));
    }
}

4.  Twig templates


Before inspecting how Twig files are generated, you can read this article from Fabien Potencier on why do we need Twig in first place.

How does Twig work?

   1. Load the template: If the template is already compiled, load it and go to the evaluation step, otherwise:
        1.1. First, the lexer tokenizes the template source code into small pieces for easier processing;
        1.2. Then, the parser converts the token stream into a meaningful tree of nodes (the Abstract Syntax Tree);
        1.3. Eventually, the compiler transforms the AST into PHP code. This step is generating PHP  c    classes that extends Twig_Template class and contain a function called "doDisplay()".
The files are found under cache/twig.
    2. Evaluate the template: It basically means calling the display() method of the compiled template and passing it the context.

Compilation is a costly process, so the result is cached in the directory defined by this configuration option. You can set option "cache" to null to disable Twig template compilation. However, this is not recommended; not even in the dev environment, because the auto_reload option ensures that cached templates which have changed get compiled again.

auto_reload

If true, whenever a template is rendered, Symfony checks first if its source code has changed since it was compiled. If it has changed, the template is compiled again automatically. (source: http://symfony.com/doc/current/reference/configuration/twig.html).

5. Routes


In Symfony you can declare routes in different ways YAML, XML, Annotation, all these are in the end compiled to PHP. You can have a look at the file appDevUrlMatcher.php under /app/cache directory for Symfony 2. In dev environment this file regenerated if you are doing changes to routes, in prod environment you need to clear the cache in order to regenerate it. Have a look at this presentation for more info: http://davidbu.ch/slides/2015-12-03-symfony-routing.html