merged branch Tobion/generator-dumper (PR #3894)

Commits
-------

382b083 [Routing] improved generated class by PhpGeneratorDumper
27a05f4 [Routing] small optimization of PhpGeneratorDumper

Discussion
----------

[Routing] improved PhpGeneratorDumper

Test pass: yes
BC break: no

The first commit only replaces arrays with strings and makes some cosmetic changes, so that it's more readable. This makes the `PhpGeneratorDumper` consistent in style with `PhpMatcherDumper` that I fixed recently and should slightly improve performance of the generation of the class.

The second commit changes the output of the `PhpGeneratorDumper->dump` and tries to optimize the resulting class that is used to generate URLs. It's best explained with an example.

Before my changes:

```php
class ProjectUrlGenerator extends Symfony\Component\Routing\Generator\UrlGenerator
{
    static private $declaredRouteNames = array(
       'Test' => true,
       'Test2' => true,
    );

    /**
     * Constructor.
     */
    public function __construct(RequestContext $context)
    {
        $this->context = $context;
    }

    public function generate($name, $parameters = array(), $absolute = false)
    {
        if (!isset(self::$declaredRouteNames[$name])) {
            throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name));
        }

        $escapedName = str_replace('.', '__', $name);

        list($variables, $defaults, $requirements, $tokens) = $this->{'get'.$escapedName.'RouteInfo'}();

        return $this->doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute);
    }

    private function getTestRouteInfo()
    {
        return array(array (  0 => 'foo',), array (), array (), array (  0 =>   array (    0 => 'variable',    1 => '/',    2 => '[^/]+?',    3 => 'foo',  ),  1 =>   array (    0 => 'text',    1 => '/testing',  ),));
    }

    private function getTest2RouteInfo()
    {
        return array(array (), array (), array (), array (  0 =>   array (    0 => 'text',    1 => '/testing2',  ),));
    }
}
```

After my changes in second commit:

```php
class ProjectUrlGenerator extends Symfony\Component\Routing\Generator\UrlGenerator
{
    static private $declaredRoutes = array(
        'Test' => array (  0 =>   array (    0 => 'foo',  ),  1 =>   array (  ),  2 =>   array (  ),  3 =>   array (    0 =>     array (      0 => 'variable',      1 => '/',      2 => '[^/]+?',      3 => 'foo',    ),    1 =>     array (      0 => 'text',      1 => '/testing',    ),  ),),
        'Test2' => array (  0 =>   array (  ),  1 =>   array (  ),  2 =>   array (  ),  3 =>   array (    0 =>     array (      0 => 'text',      1 => '/testing2',    ),  ),),
    );

    /**
     * Constructor.
     */
    public function __construct(RequestContext $context)
    {
        $this->context = $context;
    }

    public function generate($name, $parameters = array(), $absolute = false)
    {
        if (!isset(self::$declaredRoutes[$name])) {
            throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name));
        }

        list($variables, $defaults, $requirements, $tokens) = self::$declaredRoutes[$name];

        return $this->doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute);
    }
}
```

As you can see, there is no need to escape the route name and invoke a special method anymore. Instead the route properties are included in the static route array directly, that existed anyway. Is also easier to read as defined routes and their properties are in the same place.
This commit is contained in:
Fabien Potencier 2012-04-12 12:27:45 +02:00
commit 5ab006aca2

View File

@ -17,6 +17,7 @@ use Symfony\Component\Routing\Route;
* PhpGeneratorDumper creates a PHP class able to generate URLs for a given set of routes.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Tobias Schultze <http://tobion.de>
*
* @api
*/
@ -43,92 +44,22 @@ class PhpGeneratorDumper extends GeneratorDumper
'base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
), $options);
return
$this->startClass($options['class'], $options['base_class']).
$this->addConstructor().
$this->addGenerator().
$this->endClass()
;
}
private function addGenerator()
{
$methods = array();
foreach ($this->getRoutes()->all() as $name => $route) {
$compiledRoute = $route->compile();
$variables = str_replace("\n", '', var_export($compiledRoute->getVariables(), true));
$defaults = str_replace("\n", '', var_export($compiledRoute->getDefaults(), true));
$requirements = str_replace("\n", '', var_export($compiledRoute->getRequirements(), true));
$tokens = str_replace("\n", '', var_export($compiledRoute->getTokens(), true));
$escapedName = str_replace('.', '__', $name);
$methods[] = <<<EOF
private function get{$escapedName}RouteInfo()
{
return array($variables, $defaults, $requirements, $tokens);
}
EOF
;
}
$methods = implode("\n", $methods);
return <<<EOF
public function generate(\$name, \$parameters = array(), \$absolute = false)
{
if (!isset(self::\$declaredRouteNames[\$name])) {
throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', \$name));
}
\$escapedName = str_replace('.', '__', \$name);
list(\$variables, \$defaults, \$requirements, \$tokens) = \$this->{'get'.\$escapedName.'RouteInfo'}();
return \$this->doGenerate(\$variables, \$defaults, \$requirements, \$tokens, \$parameters, \$name, \$absolute);
}
$methods
EOF;
}
private function startClass($class, $baseClass)
{
$routes = array();
foreach ($this->getRoutes()->all() as $name => $route) {
$routes[] = " '$name' => true,";
}
$routes = implode("\n", $routes);
return <<<EOF
<?php
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Exception\RouteNotFoundException;
/**
* $class
* {$options['class']}
*
* This class has been auto-generated
* by the Symfony Routing Component.
*/
class $class extends $baseClass
class {$options['class']} extends {$options['base_class']}
{
static private \$declaredRouteNames = array(
$routes
);
static private \$declaredRoutes = {$this->generateDeclaredRoutes()};
EOF;
}
private function addConstructor()
{
return <<<EOF
/**
* Constructor.
*/
@ -137,14 +68,55 @@ EOF;
\$this->context = \$context;
}
{$this->generateGenerateMethod()}
}
EOF;
}
private function endClass()
/**
* Generates PHP code representing an array of defined routes
* together with the routes properties (e.g. requirements).
*
* @return string PHP code
*/
private function generateDeclaredRoutes()
{
$routes = "array(\n";
foreach ($this->getRoutes()->all() as $name => $route) {
$compiledRoute = $route->compile();
$properties = array();
$properties[] = $compiledRoute->getVariables();
$properties[] = $compiledRoute->getDefaults();
$properties[] = $compiledRoute->getRequirements();
$properties[] = $compiledRoute->getTokens();
$routes .= sprintf(" '%s' => %s,\n", $name, str_replace("\n", '', var_export($properties, true)));
}
$routes .= ' )';
return $routes;
}
/**
* Generates PHP code representing the `generate` method that implements the UrlGeneratorInterface.
*
* @return string PHP code
*/
private function generateGenerateMethod()
{
return <<<EOF
}
public function generate(\$name, \$parameters = array(), \$absolute = false)
{
if (!isset(self::\$declaredRoutes[\$name])) {
throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', \$name));
}
list(\$variables, \$defaults, \$requirements, \$tokens) = self::\$declaredRoutes[\$name];
return \$this->doGenerate(\$variables, \$defaults, \$requirements, \$tokens, \$parameters, \$name, \$absolute);
}
EOF;
}
}