Skip to content

Commit

Permalink
[Routing] serialize the compiled route to speed things up
Browse files Browse the repository at this point in the history
This also makes the CompiledRoute implement Serializable in order to:
1. make the serialization format shorter
2. have no null bytes in there, which the native serializer add for private properties, and thus would complicate saving in databases etc.
3. We should add to our symfony BC promise, that only classes that implement Serializable are ensured to be deserializable correctly with serialized representations of the class in previous symfony versions.
  • Loading branch information
Tobion committed Oct 27, 2014
1 parent 20e7cf1 commit fd88de7
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 4 deletions.
35 changes: 34 additions & 1 deletion src/Symfony/Component/Routing/CompiledRoute.php
Expand Up @@ -16,7 +16,7 @@
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class CompiledRoute
class CompiledRoute implements \Serializable
{
private $variables;
private $tokens;
Expand Down Expand Up @@ -51,6 +51,39 @@ public function __construct($staticPrefix, $regex, array $tokens, array $pathVar
$this->variables = $variables;
}

/**
* {@inheritdoc}
*/
public function serialize()
{
return serialize(array(
'vars' => $this->variables,
'path_prefix' => $this->staticPrefix,
'path_regex' => $this->regex,
'path_tokens' => $this->tokens,
'path_vars' => $this->pathVariables,
'host_regex' => $this->hostRegex,
'host_tokens' => $this->hostTokens,
'host_vars' => $this->hostVariables,
));
}

/**
* {@inheritdoc}
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->variables = $data['vars'];
$this->staticPrefix = $data['path_prefix'];
$this->regex = $data['path_regex'];
$this->tokens = $data['path_tokens'];
$this->pathVariables = $data['path_vars'];
$this->hostRegex = $data['host_regex'];
$this->hostTokens = $data['host_tokens'];
$this->hostVariables = $data['host_vars'];
}

/**
* Returns the static prefix.
*
Expand Down
14 changes: 12 additions & 2 deletions src/Symfony/Component/Routing/Route.php
Expand Up @@ -95,6 +95,9 @@ public function __construct($path, array $defaults = array(), array $requirement
}
}

/**
* {@inheritdoc}
*/
public function serialize()
{
return serialize(array(
Expand All @@ -105,19 +108,26 @@ public function serialize()
'options' => $this->options,
'schemes' => $this->schemes,
'methods' => $this->methods,
'compiled' => $this->compiled,
));
}

public function unserialize($data)
/**
* {@inheritdoc}
*/
public function unserialize($serialized)
{
$data = unserialize($data);
$data = unserialize($serialized);
$this->path = $data['path'];
$this->host = $data['host'];
$this->defaults = $data['defaults'];
$this->requirements = $data['requirements'];
$this->options = $data['options'];
$this->schemes = $data['schemes'];
$this->methods = $data['methods'];
if (isset($data['compiled'])) {
$this->compiled = $data['compiled'];
}
}

/**
Expand Down
37 changes: 36 additions & 1 deletion src/Symfony/Component/Routing/Tests/RouteTest.php
Expand Up @@ -212,12 +212,47 @@ public function testPattern()

public function testSerialize()
{
$route = new Route('/{foo}', array('foo' => 'default'), array('foo' => '\d+'));
$route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+'));

$serialized = serialize($route);
$unserialized = unserialize($serialized);

$this->assertEquals($route, $unserialized);
$this->assertNotSame($route, $unserialized);
}

/**
* Tests that the compiled version is also serialized to prevent the overhead
* of compiling it again after unserialize.
*/
public function testSerializeWhenCompiled()
{
$route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+'));
$route->setHost('{locale}.example.net');
$route->compile();

$serialized = serialize($route);
$unserialized = unserialize($serialized);

$this->assertEquals($route, $unserialized);
$this->assertNotSame($route, $unserialized);
}

/**
* Tests that the serialized representation of a route in one symfony version
* also works in later symfony versions, i.e. the unserialized route is in the
* same state as another, semantically equivalent, route.
*/
public function testSerializedRepresentationKeepsWorking()
{
$serialized = 'C:31:"Symfony\Component\Routing\Route":933:{a:8:{s:4:"path";s:13:"/prefix/{foo}";s:4:"host";s:20:"{locale}.example.net";s:8:"defaults";a:1:{s:3:"foo";s:7:"default";}s:12:"requirements";a:1:{s:3:"foo";s:3:"\d+";}s:7:"options";a:1:{s:14:"compiler_class";s:39:"Symfony\Component\Routing\RouteCompiler";}s:7:"schemes";a:0:{}s:7:"methods";a:0:{}s:8:"compiled";C:39:"Symfony\Component\Routing\CompiledRoute":568:{a:8:{s:4:"vars";a:2:{i:0;s:6:"locale";i:1;s:3:"foo";}s:11:"path_prefix";s:7:"/prefix";s:10:"path_regex";s:30:"#^/prefix(?:/(?P<foo>\d+))?$#s";s:11:"path_tokens";a:2:{i:0;a:4:{i:0;s:8:"variable";i:1;s:1:"/";i:2;s:3:"\d+";i:3;s:3:"foo";}i:1;a:2:{i:0;s:4:"text";i:1;s:7:"/prefix";}}s:9:"path_vars";a:1:{i:0;s:3:"foo";}s:10:"host_regex";s:38:"#^(?P<locale>[^\.]++)\.example\.net$#s";s:11:"host_tokens";a:2:{i:0;a:2:{i:0;s:4:"text";i:1;s:12:".example.net";}i:1;a:4:{i:0;s:8:"variable";i:1;s:0:"";i:2;s:7:"[^\.]++";i:3;s:6:"locale";}}s:9:"host_vars";a:1:{i:0;s:6:"locale";}}}}}';
$unserialized = unserialize($serialized);

$route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+'));
$route->setHost('{locale}.example.net');
$route->compile();

$this->assertEquals($route, $unserialized);
$this->assertNotSame($route, $unserialized);
}
}

0 comments on commit fd88de7

Please sign in to comment.