Skip to content

Instantly share code, notes, and snippets.

@tyler-sommer
Last active September 9, 2015 04:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tyler-sommer/9285127 to your computer and use it in GitHub Desktop.
Save tyler-sommer/9285127 to your computer and use it in GitHub Desktop.
Adding closures to compiled symfony 2 containers
<?php
// Generated container classs
class DevProjectContainer extends Container
{
/**
* Gets the 'twig.loader' service.
*
* This service is shared.
* This method always returns the same instance of the service.
*
* @return Twig_Loader_Filesystem A Twig_Loader_Filesystem instance.
*/
protected function getTwig_LoaderService()
{
$this->services['twig.loader'] = $instance = new \Twig_Loader_Filesystem(array());
$container = $this;
$closure = function(\Twig_Loader_Filesystem $loader) use ($container) {
$loader->setPaths($container->getParameter('twig.template_dir'));
};
$closure($instance);
return $instance;
}
}
<?php
/**
* Serializable Closure
*
* Allows the serialization of a closure, specifically a Closure, for storage
*/
class SerializableCallable implements \Serializable
{
/**
* @var callable
*/
protected $closure;
/**
* @param callable $closure Either a Closure object or a valid callable
*/
public function __construct($closure)
{
if (is_array($closure) && is_object($closure[0])) {
throw new \RuntimeException('Closures on object instances is not currently supported.');
}
else if (!is_callable($closure)) {
throw new \RuntimeException('Closure is not callable.');
}
$this->closure = $closure;
}
/**
* Invokes the stored closure
*/
public function __invoke()
{
$args = func_get_args();
return call_user_func_array($this->closure, $args);
}
/**
* Gets $closure
*
* @return callable
*/
public function getClosure()
{
return $this->closure;
}
public function __toString()
{
if (!($this->closure instanceof \Closure)) {
// Is a normal closure, just serialize it
return serialize(array('closure' => $this->closure));
}
$reflected = new \ReflectionFunction($this->closure);
$code = $this->_getCode($reflected);
$context = $reflected->getStaticVariables();
$code = '$container = $this;' . "\n" . '$closure = ' . $code . ';' . "\n" . '$closure';
return $code;
}
/**
* @see Serializable::serialize()
*/
public function serialize()
{
if (!($this->closure instanceof \Closure)) {
// Is a normal closure, just serialize it
return serialize(array('closure' => $this->closure));
}
$reflected = new \ReflectionFunction($this->closure);
$code = $this->_getCode($reflected);
$context = $reflected->getStaticVariables();
return serialize(array('code' => $code, 'context' => $context));
}
/**
* @see Serializable::unserialize()
*/
public function unserialize($data)
{
$data = unserialize($data);
if (!$data) {
throw new \RuntimeException('Could not unserialize Closure. Unserialization failed.');
}
if (empty($data['closure'])) {
$code = $data['code'];
$context = $data['context'];
extract($context);
@eval("\$_closure = $code;");
if (!isset($_closure) || !is_callable($_closure)) throw new \RuntimeException('Could not unserialize Closure. Invalid Closure specified.');
$this->closure = $_closure;
}
else {
$this->closure = $data['closure'];
}
}
/**
* Iterates on source files to return actual implementation of code
*
* @param \ReflectionFunction $reflected
*/
protected function _getCode(\ReflectionFunction $reflected)
{
$file = new \SplFileObject($reflected->getFileName());
$file->seek($reflected->getStartLine() - 1);
$code = '';
while ($file->key() < $reflected->getEndLine()) {
$code .= $file->current();
$file->next();
}
$begin = strpos($code, 'function');
$end = strrpos($code, '}');
$code = substr($code, $begin, $end - $begin + 1);
return $code;
}
}
<?php
// Inside AppKernel.php somewhere
$getTemplateDir = new SerializableCallable(function(\Twig_Loader_Filesystem $loader) use ($container) {
$loader->setPaths($container->getParameter('twig.template_dir'));
});
$container->setParameter('twig.template_dir', $this->getRootDir() . '/views');
$container->register('twig.loader', 'Twig_Loader_Filesystem')
->addArgument(array())
->setConfigurator($getTemplateDir);
$container->compile();
$this->dumpContainer($cache, $container, $class, 'Container');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment