[TwigBundle] added the Twig bundle (proof of concept)

This commit is contained in:
Fabien Potencier 2010-05-20 19:11:09 +02:00
parent 2f4c9a32e9
commit 87143b3dd3
13 changed files with 660 additions and 0 deletions

View File

@ -0,0 +1,41 @@
<?php
namespace Symfony\Framework\TwigBundle;
use Symfony\Foundation\Bundle\Bundle as BaseBundle;
use Symfony\Components\DependencyInjection\ContainerInterface;
use Symfony\Components\DependencyInjection\Loader\Loader;
use Symfony\Components\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Components\DependencyInjection\BuilderConfiguration;
use Symfony\Framework\TwigBundle\DependencyInjection\TwigExtension;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Bundle.
*
* @package Symfony
* @subpackage Framework_TwigBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Bundle extends BaseBundle
{
/**
* Customizes the Container instance.
*
* @param Symfony\Components\DependencyInjection\ContainerInterface $container A ContainerInterface instance
*
* @return Symfony\Components\DependencyInjection\BuilderConfiguration A BuilderConfiguration instance
*/
public function buildContainer(ContainerInterface $container)
{
Loader::registerExtension(new TwigExtension());
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace Symfony\Framework\TwigBundle\DependencyInjection;
use Symfony\Components\DependencyInjection\Loader\LoaderExtension;
use Symfony\Components\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Components\DependencyInjection\BuilderConfiguration;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* TwigExtension.
*
* @package Symfony
* @subpackage Framework_TwigBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class TwigExtension extends LoaderExtension
{
public function configLoad($config)
{
$configuration = new BuilderConfiguration();
$loader = new XmlFileLoader(__DIR__.'/../Resources/config');
$configuration->merge($loader->load('twig.xml'));
$configuration->setParameter('twig_options', array_replace($configuration->getParameter('twig_options'), $config));
return $configuration;
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return __DIR__.'/../Resources/config/';
}
public function getNamespace()
{
return 'http://www.symfony-project.org/schema/dic/twig';
}
public function getAlias()
{
return 'twig';
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace Symfony\Framework\TwigBundle;
use Symfony\Components\DependencyInjection\ContainerInterface;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* TwigExtension.
*
* @package Symfony
* @subpackage Framework_TwigBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Environment extends \Twig_Environment
{
public function __construct(ContainerInterface $container, \Twig_LoaderInterface $loader = null, $options = array())
{
parent::__construct($loader, $options);
foreach ($container->findAnnotatedServiceIds('twig.extension') as $id => $attributes) {
$this->addExtension($container->getService($id));
}
}
}

View File

@ -0,0 +1,52 @@
<?php
namespace Symfony\Framework\TwigBundle\Extension;
use Symfony\Components\Templating\Engine;
use Symfony\Framework\TwigBundle\TokenParser\StylesheetTokenParser;
use Symfony\Framework\TwigBundle\TokenParser\StylesheetsTokenParser;
use Symfony\Framework\TwigBundle\TokenParser\RouteTokenParser;
use Symfony\Framework\TwigBundle\TokenParser\RenderTokenParser;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
*
* @package Symfony
* @subpackage Framework_TwigBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Helpers extends \Twig_Extension
{
/**
* Returns the token parser instance to add to the existing list.
*
* @return array An array of Twig_TokenParser instances
*/
public function getTokenParsers()
{
return array(
new StylesheetTokenParser(),
new StylesheetsTokenParser(),
new RouteTokenParser(),
new RenderTokenParser(),
);
}
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
public function getName()
{
return 'symfony.helpers';
}
}

View File

@ -0,0 +1,96 @@
<?php
namespace Symfony\Framework\TwigBundle\Loader;
use Symfony\Components\Templating\Engine;
use Symfony\Components\Templating\Storage\Storage;
use Symfony\Components\Templating\Storage\FileStorage;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
*
* @package Symfony
* @subpackage Framework_TwigBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Loader implements \Twig_LoaderInterface
{
protected $engine;
public function setEngine(Engine $engine)
{
$this->engine = $engine;
}
/**
* Gets the source code of a template, given its name.
*
* @param string $name string The name of the template to load
*
* @return string The template source code
*/
public function getSource($name)
{
if ($name instanceof Storage) {
return $name->getContent();
}
list($name, $options) = $this->engine->splitTemplateName($name);
$template = $this->engine->getLoader()->load($name, $options);
if (false === $template) {
throw new \InvalidArgumentException(sprintf('The template "%s" does not exist (renderer: %s).', $name, $options['renderer']));
}
return $template->getContent();
}
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name string The name of the template to load
*
* @return string The cache key
*/
public function getCacheKey($name)
{
if ($name instanceof Storage) {
return (string) $name;
}
list($name, $options) = $this->engine->splitTemplateName($name);
return $name.'_'.serialize($options);
}
/**
* Returns true if the template is still fresh.
*
* @param string $name The template name
* @param timestamp $time The last modification time of the cached template
*/
public function isFresh($name, $time)
{
if ($name instanceof Storage) {
if ($name instanceof FileStorage)
{
return filemtime((string) $name) < $time;
}
return false;
}
list($name, $options) = $this->engine->splitTemplateName($name);
return $this->engine->getLoader()->isFresh($name, $options, $time);
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace Symfony\Framework\TwigBundle\Node;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
*
* @package Symfony
* @subpackage Framework_TwigBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class HelperNode extends \Twig_Node implements \Twig_NodeListInterface
{
protected $name;
protected $method;
protected $arguments;
protected $echo;
public function __construct($name, $method, \Twig_NodeList $arguments, $echo, $lineno, $tag = null)
{
parent::__construct($lineno, $tag);
$this->name = $name;
$this->method = $method;
$this->arguments = $arguments;
$this->echo = $echo;
}
public function __toString()
{
return get_class($this).'('.$this->arguments.')';
}
public function getNodes()
{
return array($this->arguments);
}
public function setNodes(array $nodes)
{
$this->arguments = $nodes[0];
}
public function compile($compiler)
{
$compiler->addDebugInfo($this);
if ($this->echo) {
$compiler->raw('echo ');
}
$compiler->write('$context[\'_view\']->'.$this->name.'->'.$this->method.'(');
$count = count($this->arguments->getNodes());
foreach ($this->arguments->getNodes() as $i => $node) {
$compiler->subcompile($node);
if ($i !== $count - 1) {
$compiler->raw(', ');
}
}
$compiler->raw(");\n");
}
public function getName()
{
return $this->name;
}
public function getMethod()
{
return $this->method;
}
public function getArguments()
{
return $this->arguments;
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace Symfony\Framework\TwigBundle\Renderer;
use Symfony\Components\Templating\Renderer\Renderer as BaseRenderer;
use Symfony\Components\Templating\Storage\Storage;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
*
* @package Symfony
* @subpackage Framework_TwigBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Renderer extends BaseRenderer
{
protected $environment;
public function __construct(\Twig_Environment $environment)
{
$this->environment = $environment;
}
/**
* Evaluates a template.
*
* @param Storage $template The template to render
* @param array $parameters An array of parameters to pass to the template
*
* @return string|false The evaluated template, or false if the renderer is unable to render the template
*/
public function evaluate(Storage $template, array $parameters = array())
{
$parameters['_view'] = $this->engine;
return $this->environment->loadTemplate($template)->render($parameters);
}
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://www.symfony-project.org/schema/dic/twig"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.symfony-project.org/schema/dic/twig"
elementFormDefault="qualified">
<xsd:element name="config" type="config" />
<xsd:complexType name="config">
<xsd:attribute name="charset" type="xsd:string" />
<xsd:attribute name="debug" type="xsd:string" />
<xsd:attribute name="cache" type="xsd:string" />
<xsd:attribute name="trim_blocks" type="xsd:string" />
<xsd:attribute name="auto_reload" type="xsd:string" />
<xsd:attribute name="base_template_class" type="xsd:string" />
</xsd:complexType>
</xsd:schema>

View File

@ -0,0 +1,44 @@
<?xml version="1.0" ?>
<container xmlns="http://www.symfony-project.org/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.symfony-project.org/schema/dic/services http://www.symfony-project.org/schema/dic/services/services-1.0.xsd">
<parameters>
<parameter key="twig.class">Symfony\Framework\TwigBundle\Environment</parameter>
<parameter key="twig_options" type="collection">
<parameter key="charset">%kernel.charset%</parameter>
<parameter key="debug">%kernel.debug%</parameter>
<parameter key="cache">%kernel.cache_dir%/twig</parameter>
</parameter>
<parameter key="twig_loader.class">Symfony\Framework\TwigBundle\Loader\Loader</parameter>
<parameter key="twig_renderer.class">Symfony\Framework\TwigBundle\Renderer\Renderer</parameter>
</parameters>
<services>
<service id="twig" class="%twig.class%">
<argument type="service" id="service_container" />
<argument type="service" id="twig_loader" />
<argument>%twig_options%</argument>
</service>
<service id="twig_loader" class="%twig_loader.class%">
<call method="setEngine">
<argument type="service" id="templating" />
</call>
</service>
<service id="twig_renderer" class="%twig_renderer.class%">
<annotation name="templating.renderer" alias="twig" />
<argument type="service" id="twig" />
</service>
<service id="twig_extension_escaper" class="\Twig_Extension_Escaper">
<annotation name="twig.extension" />
</service>
<service id="twig_extension_helpers" class="Symfony\Framework\TwigBundle\Extension\Helpers">
<annotation name="twig.extension" />
</service>
</services>
</container>

View File

@ -0,0 +1,49 @@
<?php
namespace Symfony\Framework\TwigBundle\TokenParser;
use Symfony\Framework\TwigBundle\Node\HelperNode;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Wrapper for the actions helper render() method.
*
* {% render 'BlogBundle:Post:list' with ['path': ['limit': 2], 'standalone': false, 'alt': 'BlogBundle:Post:error'] %}
*
* @package Symfony
* @subpackage Framework_TwigBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class RenderTokenParser extends \Twig_TokenParser
{
public function parse(\Twig_Token $token)
{
$nodes = array();
$lineno = $token->getLine();
$nodes[] = $this->parser->getExpressionParser()->parseExpression();
if ($this->parser->getStream()->test(\Twig_Token::NAME_TYPE, 'with'))
{
$this->parser->getStream()->expect(\Twig_Token::NAME_TYPE, 'with');
$nodes[] = $this->parser->getExpressionParser()->parseExpression();
}
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
return new HelperNode('actions', 'render', new \Twig_NodeList($nodes), true, $lineno, $this->getTag());
}
public function getTag()
{
return 'render';
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace Symfony\Framework\TwigBundle\TokenParser;
use Symfony\Framework\TwigBundle\Node\HelperNode;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Wrapper for the route helper generate() method.
*
* {% route blog_post with ['id': post.id] %}
*
* @package Symfony
* @subpackage Framework_TwigBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class RouteTokenParser extends \Twig_TokenParser
{
public function parse(\Twig_Token $token)
{
$nodes = array();
$lineno = $token->getLine();
$nodes[] = $this->parser->getExpressionParser()->parseExpression();
if ($this->parser->getStream()->test(\Twig_Token::NAME_TYPE, 'with'))
{
$this->parser->getStream()->expect(\Twig_Token::NAME_TYPE, 'with');
$nodes[] = $this->parser->getExpressionParser()->parseExpression();
}
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
return new HelperNode('router', 'generate', new \Twig_NodeList($nodes), true, $lineno, $this->getTag());
}
public function getTag()
{
return 'route';
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace Symfony\Framework\TwigBundle\TokenParser;
use Symfony\Framework\TwigBundle\Node\HelperNode;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Wrapper for the stylesheets helper add() method.
*
* {% stylesheet 'bundles/blog/css/blog.css' with ['media': 'screen'] %}
*
* @package Symfony
* @subpackage Framework_TwigBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class StylesheetTokenParser extends \Twig_TokenParser
{
public function parse(\Twig_Token $token)
{
$nodes = array();
$lineno = $token->getLine();
$nodes[] = $this->parser->getExpressionParser()->parseExpression();
if ($this->parser->getStream()->test(\Twig_Token::NAME_TYPE, 'with'))
{
$this->parser->getStream()->expect(\Twig_Token::NAME_TYPE, 'with');
$nodes[] = $this->parser->getExpressionParser()->parseExpression();
}
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
return new HelperNode('stylesheets', 'add', new \Twig_NodeList($nodes), false, $lineno, $this->getTag());
}
public function getTag()
{
return 'stylesheet';
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace Symfony\Framework\TwigBundle\TokenParser;
use Symfony\Framework\TwigBundle\Node\HelperNode;
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Wrapper for the stylesheets helper output() method.
*
* {% stylesheets %}
*
* @package Symfony
* @subpackage Framework_TwigBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class StylesheetsTokenParser extends \Twig_TokenParser
{
public function parse(\Twig_Token $token)
{
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
return new HelperNode('stylesheets', 'render', new \Twig_NodeList(array()), true, $token->getLine(), $this->getTag());
}
public function getTag()
{
return 'stylesheets';
}
}