made a big refactoring of the templating sub-framework

* better separation of concerns
 * made TwigBundle independant of the PHP Engine from FrameworkBundle (WIP)
 * removed one layer of abstraction in the Templating component (renderers)
 * made it easier to create a new Engine for any templating library
 * made engines lazy-loaded (PHP engine for instance is not started if you only use Twig)
 * reduces memory footprint (if you only use one engine)
 * reduces size of compiled classes.php cache file
This commit is contained in:
Fabien Potencier 2011-01-13 14:58:17 +01:00
parent 6011073e7c
commit 055b6e4d6e
46 changed files with 1012 additions and 874 deletions

View File

@ -1,41 +0,0 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class AddTemplatingRenderersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('templating.engine')) {
return;
}
$renderers = array();
foreach ($container->findTaggedServiceIds('templating.renderer') as $id => $attributes) {
if (isset($attributes[0]['alias'])) {
$renderers[$attributes[0]['alias']] = new Reference($id);
}
}
$helpers = array();
foreach ($container->findTaggedServiceIds('templating.helper') as $id => $attributes) {
if (isset($attributes[0]['alias'])) {
$helpers[$attributes[0]['alias']] = $id;
}
}
$definition = $container->getDefinition('templating.engine');
$arguments = $definition->getArguments();
$definition->setArguments($arguments);
$definition->addMethodCall('setRenderers', array($renderers));
if (count($helpers) > 0) {
$definition->addMethodCall('setHelpers', array($helpers));
}
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
class TemplatingPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if ($container->hasDefinition('templating')) {
return;
}
if ($container->hasDefinition('templating.engine.php')) {
$helpers = array();
foreach ($container->findTaggedServiceIds('templating.helper') as $id => $attributes) {
if (isset($attributes[0]['alias'])) {
$helpers[$attributes[0]['alias']] = $id;
}
}
$definition = $container->getDefinition('templating.engine.php');
$arguments = $definition->getArguments();
$definition->setArguments($arguments);
if (count($helpers) > 0) {
$definition->addMethodCall('setHelpers', array($helpers));
}
}
if ($container->hasDefinition('templating.engine.delegating')) {
$queue = new \SplPriorityQueue();
foreach ($container->findTaggedServiceIds('templating.engine') as $id => $attributes) {
$queue->insert($id, isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0);
}
$engines = array();
foreach ($queue as $engine) {
$engines[] = $engine;
}
$container->getDefinition('templating.engine.delegating')->addMethodCall('setEngineIds', array($engines));
}
}
}

View File

@ -231,21 +231,8 @@ class FrameworkExtension extends Extension
// compilation
$this->addCompiledClasses($container, array(
'Symfony\\Component\\Templating\\Loader\\LoaderInterface',
'Symfony\\Component\\Templating\\Loader\\Loader',
'Symfony\\Component\\Templating\\Loader\\FilesystemLoader',
'Symfony\\Component\\Templating\\Engine',
'Symfony\\Component\\Templating\\Renderer\\RendererInterface',
'Symfony\\Component\\Templating\\Renderer\\Renderer',
'Symfony\\Component\\Templating\\Renderer\\PhpRenderer',
'Symfony\\Component\\Templating\\Storage\\Storage',
'Symfony\\Component\\Templating\\Storage\\FileStorage',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\Engine',
'Symfony\\Component\\Templating\\Helper\\Helper',
'Symfony\\Component\\Templating\\Helper\\SlotsHelper',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\ActionsHelper',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\RouterHelper',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\Helper\\RouterHelper',
'Symfony\\Component\\Templating\\DelegatingEngine',
'Symfony\\Bundle\\FrameworkBundle\\Templating\\EngineInterface',
));
}

View File

@ -3,7 +3,7 @@
namespace Symfony\Bundle\FrameworkBundle;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddConstraintValidatorsPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddTemplatingRenderersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\TemplatingPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\RegisterKernelListenersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\AddSecurityVotersPass;
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\ConverterManagerPass;
@ -67,7 +67,7 @@ class FrameworkBundle extends Bundle
$container->addCompilerPass(new RoutingResolverPass());
$container->addCompilerPass(new ProfilerPass());
$container->addCompilerPass(new RegisterKernelListenersPass());
$container->addCompilerPass(new AddTemplatingRenderersPass());
$container->addCompilerPass(new TemplatingPass());
$container->addCompilerPass(new AddConstraintValidatorsPass());
}
}

View File

@ -5,7 +5,8 @@
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="templating.engine.class">Symfony\Bundle\FrameworkBundle\Templating\Engine</parameter>
<parameter key="templating.engine.delegating.class">Symfony\Bundle\FrameworkBundle\Templating\DelegatingEngine</parameter>
<parameter key="templating.engine.php.class">Symfony\Bundle\FrameworkBundle\Templating\PhpEngine</parameter>
<parameter key="templating.loader.filesystem.class">Symfony\Component\Templating\Loader\FilesystemLoader</parameter>
<parameter key="templating.loader.cache.class">Symfony\Component\Templating\Loader\CacheLoader</parameter>
<parameter key="templating.loader.chain.class">Symfony\Component\Templating\Loader\ChainLoader</parameter>
@ -21,19 +22,25 @@
<parameter key="templating.helper.form.class">Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper</parameter>
<parameter key="templating.assets.version">null</parameter>
<parameter key="templating.assets.base_urls" type="collection"></parameter>
<parameter key="templating.name_converter.class">Symfony\Bundle\FrameworkBundle\Templating\TemplateNameConverter</parameter>
<parameter key="templating.name_parser.class">Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser</parameter>
<parameter key="templating.renderer.php.class">Symfony\Component\Templating\Renderer\PhpRenderer</parameter>
<parameter key="debug.file_link_format">null</parameter>
</parameters>
<services>
<service id="templating.engine" class="%templating.engine.class%" public="false">
<service id="templating.engine.delegating" class="%templating.engine.delegating.class%" public="false">
<argument type="service" id="service_container" />
</service>
<service id="templating.engine.php" class="%templating.engine.php.class%">
<tag name="templating.engine" priority="128" />
<argument type="service" id="service_container" />
<argument type="service" id="templating.loader" />
<call method="setCharset"><argument>%kernel.charset%</argument></call>
</service>
<service id="templating.loader.filesystem" class="%templating.loader.filesystem.class%" public="false">
<argument type="service" id="templating.name_parser" />
<argument>%templating.loader.filesystem.path%</argument>
<call method="setDebugger"><argument type="service" id="templating.debugger" on-invalid="ignore" /></call>
</service>
@ -100,16 +107,12 @@
<argument type="service" id="templating" />
</service>
<service id="templating.name_converter" class="%templating.name_converter.class%">
<service id="templating.name_parser" class="%templating.name_parser.class%">
<argument type="service" id="service_container" />
</service>
<service id="templating.renderer.php" class="%templating.renderer.php.class%">
<tag name="templating.renderer" alias="php" />
</service>
<service id="templating.loader" alias="templating.loader.filesystem" />
<service id="templating" alias="templating.engine" />
<service id="templating" alias="templating.engine.delegating" />
</services>
</container>

View File

@ -0,0 +1,99 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\Templating;
use Symfony\Component\Templating\DelegatingEngine as BaseDelegatingEngine;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\HttpFoundation\Response;
/*
* 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.
*/
/**
* DelegatingEngine selects an engine for a given template.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class DelegatingEngine extends BaseDelegatingEngine implements EngineInterface
{
protected $container;
/**
* Constructor.
*
* @param ContainerInterface $container The DI container
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
$this->engines = array();
}
public function setEngineIds($ids)
{
$this->engines = $ids;
}
/**
* {@inheritdoc}
*/
public function supports($name)
{
foreach ($this->engines as $i => $engine) {
if (is_string($engine)) {
$engine = $this->engines[$i] = $this->container->get($engine);
}
if ($engine->supports($name)) {
return true;
}
}
return false;
}
/**
* {@inheritdoc}
*/
protected function getEngine($name)
{
foreach ($this->engines as $i => $engine) {
if (is_string($engine)) {
$engine = $this->engines[$i] = $this->container->get($engine);
}
if ($engine->supports($name)) {
return $engine;
}
}
throw new \RuntimeException(sprintf('No engine is able to work with the "%s" template.', $name));
}
/**
* Renders a view and returns a Response.
*
* @param string $view The view name
* @param array $parameters An array of parameters to pass to the view
* @param Response $response A Response instance
*
* @return Response A Response instance
*/
public function renderResponse($view, array $parameters = array(), Response $response = null)
{
if (null === $response) {
$response = $this->container->get('response');
}
$response->setContent($this->render($view, $parameters));
return $response;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Symfony\Bundle\FrameworkBundle\Templating;
use Symfony\Component\Templating\EngineInterface as BaseEngineInterface;
use Symfony\Component\HttpFoundation\Response;
/*
* 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.
*/
/**
* EngineInterface is the interface each engine must implement.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
interface EngineInterface extends BaseEngineInterface
{
/**
* Renders a view and returns a Response.
*
* @param string $view The view name
* @param array $parameters An array of parameters to pass to the view
* @param Response $response A Response instance
*
* @return Response A Response instance
*/
function renderResponse($view, array $parameters = array(), Response $response = null);
}

View File

@ -2,7 +2,7 @@
namespace Symfony\Bundle\FrameworkBundle\Templating;
use Symfony\Component\Templating\Engine as BaseEngine;
use Symfony\Component\Templating\PhpEngine as BasePhpEngine;
use Symfony\Component\Templating\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
@ -21,7 +21,7 @@ use Symfony\Component\HttpFoundation\Response;
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Engine extends BaseEngine
class PhpEngine extends BasePhpEngine implements EngineInterface
{
protected $container;
@ -30,7 +30,6 @@ class Engine extends BaseEngine
*
* @param ContainerInterface $container The DI container
* @param LoaderInterface $loader A loader instance
* @param array $renderers All templating renderers
*/
public function __construct(ContainerInterface $container, LoaderInterface $loader)
{
@ -39,9 +38,29 @@ class Engine extends BaseEngine
parent::__construct($loader);
}
public function getContainer()
/**
* @throws \InvalidArgumentException When the helper is not defined
*/
public function get($name)
{
return $this->container;
if (!isset($this->helpers[$name])) {
throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
}
if (is_string($this->helpers[$name])) {
$this->helpers[$name] = $this->container->get($this->helpers[$name]);
$this->helpers[$name]->setCharset($this->charset);
}
return $this->helpers[$name];
}
/**
* {@inheritdoc}
*/
public function setHelpers(array $helpers)
{
$this->helpers = $helpers;
}
/**
@ -63,39 +82,4 @@ class Engine extends BaseEngine
return $response;
}
public function has($name)
{
return isset($this->helpers[$name]);
}
/**
* @throws \InvalidArgumentException When the helper is not defined
*/
public function get($name)
{
if (!isset($this->helpers[$name])) {
throw new \InvalidArgumentException(sprintf('The helper "%s" is not defined.', $name));
}
if (is_string($this->helpers[$name])) {
$this->helpers[$name] = $this->container->get($this->helpers[$name]);
$this->helpers[$name]->setCharset($this->charset);
}
return $this->helpers[$name];
}
public function setHelpers(array $helpers)
{
$this->helpers = $helpers;
}
/**
* {@inheritdoc}
*/
public function splitTemplateName($name, array $defaults = array())
{
return $this->container->get('templating.name_converter')->fromShortNotation($name, $defaults);
}
}

View File

@ -3,6 +3,7 @@
namespace Symfony\Bundle\FrameworkBundle\Templating;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Templating\TemplateNameParser as BaseTemplateNameParser;
/*
* This file is part of the Symfony package.
@ -14,13 +15,13 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/
/**
* TemplateNameConverter converts template name from the short notation
* TemplateNameParser parsers template name from the short notation
* "bundle:section:template(.format).renderer" to a template name
* and an array of options.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class TemplateNameConverter
class TemplateNameParser extends BaseTemplateNameParser
{
protected $container;
@ -35,14 +36,14 @@ class TemplateNameConverter
}
/**
* Converts a short template notation to a template name and an array of options.
* Parses a template to a template name and an array of options.
*
* @param string $name A short template template
* @param string $name A template name
* @param array $defaults An array of default options
*
* @return array An array composed of the template name and an array of options
*/
public function fromShortNotation($name, array $defaults = array())
public function parse($name, array $defaults = array())
{
$parts = explode(':', $name);
if (3 !== count($parts)) {

View File

@ -35,7 +35,7 @@ class FrameworkExtensionTest extends TestCase
// templating
$loader->configLoad(array('templating' => array()), $container);
$this->assertEquals('Symfony\\Bundle\\FrameworkBundle\\Templating\\Engine', $container->getParameter('templating.engine.class'), '->templatingLoad() loads the templating.xml file if not already loaded');
$this->assertEquals('Symfony\\Bundle\\FrameworkBundle\\Templating\\PhpEngine', $container->getParameter('templating.engine.php.class'), '->templatingLoad() loads the templating.xml file if not already loaded');
// validation
$loader->configLoad(array('validation' => array('enabled' => true)), $container);

View File

@ -1,82 +0,0 @@
<?php
/*
* 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.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\Templating;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateNameConverter;
class TemplateNameConverterTest extends TestCase
{
/**
* @dataProvider getFromShortNotationTests
*/
public function testFromShortNotation($name, $parameters)
{
$converter = new TemplateNameConverter($this->getContainerMock(), $this->getLoaderMock(), array());
$this->assertEquals($parameters, $converter->fromShortNotation($name));
}
public function getFromShortNotationTests()
{
return array(
array('BlogBundle:Post:index.php', array('index', array('bundle' => 'BlogBundle', 'controller' => 'Post', 'renderer' => 'php', 'format' => ''))),
array('BlogBundle:Post:index.twig', array('index', array('bundle' => 'BlogBundle', 'controller' => 'Post', 'renderer' => 'twig', 'format' => ''))),
array('BlogBundle:Post:index.xml.php', array('index', array('bundle' => 'BlogBundle', 'controller' => 'Post', 'renderer' => 'php', 'format' => '.xml'))),
);
}
/**
* @dataProvider getFromShortNotationInvalidTests
* @expectedException \InvalidArgumentException
*/
public function testFromShortNotationInvalid($name)
{
$converter = new TemplateNameConverter($this->getContainerMock(), $this->getLoaderMock(), array());
$converter->fromShortNotation($name);
}
public function getFromShortNotationInvalidTests()
{
return array(
array('BlogBundle:Post:index'),
array('BlogBundle:Post'),
array('BlogBundle:Post:foo:bar'),
array('BlogBundle:Post:index.foo.bar.foobar'),
);
}
protected function getContainerMock()
{
$request = $this->getMock('Symfony\Component\HttpFoundation\Request');
$request
->expects($this->any())
->method('getRequestFormat')
->will($this->returnValue('html'))
;
$container = $this->getMock('Symfony\Component\DependencyInjection\ContainerBuilder');
$container
->expects($this->any())
->method('get')
->will($this->returnValue($request))
;
return $container;
}
protected function getLoaderMock()
{
return $this->getMock('Symfony\Component\Templating\Loader\LoaderInterface');
}
}

View File

@ -0,0 +1,58 @@
<?php
/*
* 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.
*/
namespace Symfony\Bundle\FrameworkBundle\Tests\Templating;
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateNameParser;
class TemplateNameParserTest extends TestCase
{
/**
* @dataProvider getParseTests
*/
public function testParse($name, $parameters)
{
$converter = new TemplateNameParser();
$this->assertEquals($parameters, $converter->parse($name));
}
public function getParseTests()
{
return array(
array('BlogBundle:Post:index.php', array('index', array('bundle' => 'BlogBundle', 'controller' => 'Post', 'renderer' => 'php', 'format' => ''))),
array('BlogBundle:Post:index.twig', array('index', array('bundle' => 'BlogBundle', 'controller' => 'Post', 'renderer' => 'twig', 'format' => ''))),
array('BlogBundle:Post:index.xml.php', array('index', array('bundle' => 'BlogBundle', 'controller' => 'Post', 'renderer' => 'php', 'format' => '.xml'))),
);
}
/**
* @dataProvider getParseInvalidTests
* @expectedException \InvalidArgumentException
*/
public function testParseInvalid($name)
{
$converter = new TemplateNameParser();
$converter->parse($name);
}
public function getParseInvalidTests()
{
return array(
array('BlogBundle:Post:index'),
array('BlogBundle:Post'),
array('BlogBundle:Post:foo:bar'),
array('BlogBundle:Post:index.foo.bar.foobar'),
);
}
}

View File

@ -19,6 +19,7 @@ use Symfony\Bundle\TwigBundle\TokenParser\FormThemeTokenParser;
*/
/**
* FormExtension extends Twig with form capabilities.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Bernhard Schussek <bernhard.schussek@symfony-project.com>

View File

@ -15,8 +15,9 @@ use Symfony\Component\Security\SecurityContext;
*/
/**
* SecurityExtension exposes security context features.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class SecurityExtension extends \Twig_Extension
{

View File

@ -47,14 +47,14 @@ class TemplatingExtension extends \Twig_Extension
public function getFilters()
{
return array(
'yaml_encode' => new \Twig_Filter_Method($this, 'yamlEncode'),
'dump' => new \Twig_Filter_Method($this, 'dump'),
'abbr_class' => new \Twig_Filter_Method($this, 'abbrClass', array('is_safe' => array('html'))),
'abbr_method' => new \Twig_Filter_Method($this, 'abbrMethod', array('is_safe' => array('html'))),
'format_args' => new \Twig_Filter_Method($this, 'formatArgs', array('is_safe' => array('html'))),
'format_args_as_text' => new \Twig_Filter_Method($this, 'formatArgsAsText', array('is_safe' => array('html'))),
'file_excerpt' => new \Twig_Filter_Method($this, 'fileExcerpt', array('is_safe' => array('html'))),
'format_file' => new \Twig_Filter_Method($this, 'formatFile', array('is_safe' => array('html'))),
'yaml_encode' => new \Twig_Filter_Method($this, 'yamlEncode'),
'dump' => new \Twig_Filter_Method($this, 'dump'),
'abbr_class' => new \Twig_Filter_Method($this, 'abbrClass', array('is_safe' => array('html'))),
'abbr_method' => new \Twig_Filter_Method($this, 'abbrMethod', array('is_safe' => array('html'))),
'format_args' => new \Twig_Filter_Method($this, 'formatArgs', array('is_safe' => array('html'))),
'format_args_as_text' => new \Twig_Filter_Method($this, 'formatArgsAsText', array('is_safe' => array('html'))),
'file_excerpt' => new \Twig_Filter_Method($this, 'fileExcerpt', array('is_safe' => array('html'))),
'format_file' => new \Twig_Filter_Method($this, 'formatFile', array('is_safe' => array('html'))),
'format_file_from_text' => new \Twig_Filter_Method($this, 'formatFileFromText', array('is_safe' => array('html'))),
);
}
@ -88,6 +88,27 @@ class TemplatingExtension extends \Twig_Extension
return $this->container->get('templating.helper.assets')->getUrl($location);
}
/**
* Returns the Response content for a given controller or URI.
*
* @param string $controller A controller name to execute (a string like BlogBundle:Post:index), or a relative URI
* @param array $attributes An array of request attributes
* @param array $options An array of options
*
* @see Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver::render()
*/
public function renderAction($controller, array $attributes = array(), array $options = array())
{
$options['attributes'] = $attributes;
if (isset($options['query']))
{
$options['query'] = $options['query'];
}
return $this->container->get('controller_resolver')->render($controller, $options);
}
/**
* Returns the token parser instance to add to the existing list.
*

View File

@ -0,0 +1,99 @@
<?php
namespace Symfony\Bundle\TwigBundle\Loader;
use Symfony\Component\Templating\TemplateNameParserInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
/*
* 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.
*/
/**
* FilesystemLoader extends the default Twig filesystem loader
* to work with the Symfony2 paths.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class FilesystemLoader extends \Twig_Loader_Filesystem
{
protected $nameParser;
protected $logger;
/**
* Constructor.
*
* @param TemplateNameParserInterface $nameParser A TemplateNameParserInterface instance
*/
public function __construct(TemplateNameParserInterface $nameParser, array $paths = array(), LoggerInterface $logger = null)
{
parent::__construct($paths);
$this->nameParser = $nameParser;
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public function setPaths($paths)
{
// invalidate the cache
$this->cache = array();
// we don't check if the directory exists here as we have path patterns, not paths
$this->paths = is_array($paths) ? $paths : array($paths);
}
protected function findTemplate($name)
{
list($tpl, $options) = $this->nameParser->parse($name);
// normalize name
$tpl = preg_replace('#/{2,}#', '/', strtr($tpl, '\\', '/'));
if (isset($this->cache[$tpl])) {
return $this->cache[$tpl];
}
$this->validateName($tpl);
$this->validateName($options['bundle']);
$this->validateName($options['controller']);
$this->validateName($options['format']);
$options['name'] = $tpl;
$replacements = array();
foreach ($options as $key => $value) {
$replacements['%'.$key.'%'] = $value;
}
$logs = array();
foreach ($this->paths as $path) {
if (is_file($file = strtr($path, $replacements))) {
if (null !== $this->logger) {
$this->logger->info(sprintf('Loaded template file "%s"', $file));
}
return $file;
}
if (null !== $this->logger) {
$logs[] = sprintf('Failed loading template file "%s"', $file);
}
}
if (null !== $this->logger) {
foreach ($logs as $log) {
$this->logger->debug($log);
}
}
throw new \Twig_Error_Loader(sprintf('Unable to find template "%s".', $name));
}
}

View File

@ -1,96 +0,0 @@
<?php
namespace Symfony\Bundle\TwigBundle\Loader;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateNameConverter;
use Symfony\Component\Templating\Loader\LoaderInterface;
/*
* 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.
*/
/**
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Loader implements \Twig_LoaderInterface
{
protected $converter;
protected $loader;
public function __construct(TemplateNameConverter $converter, LoaderInterface $loader)
{
$this->converter = $converter;
$this->loader = $loader;
}
/**
* 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->converter->fromShortNotation($name);
$template = $this->loader->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->converter->fromShortNotation($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->converter->fromShortNotation($name);
return $this->loader->isFresh($name, $options, $time);
}
}

View File

@ -32,7 +32,7 @@ class RenderNode extends \Twig_Node
{
$compiler
->addDebugInfo($this)
->write("echo \$this->env->getExtension('templating')->getContainer()->get('templating.helper.actions')->render(")
->write("echo \$this->env->getExtension('templating')->renderAction(")
->subcompile($this->getNode('expr'))
->raw(', ')
->subcompile($this->getNode('attributes'))

View File

@ -1,46 +0,0 @@
<?php
namespace Symfony\Bundle\TwigBundle\Renderer;
use Symfony\Component\Templating\Renderer\Renderer as BaseRenderer;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\Engine;
use Symfony\Bundle\TwigBundle\GlobalVariables;
/*
* 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.
*/
/**
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Renderer extends BaseRenderer
{
protected $environment;
public function __construct(\Twig_Environment $environment, GlobalVariables $globals)
{
$this->environment = $environment;
$environment->addGlobal('app', $globals);
}
/**
* 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())
{
return $this->environment->loadTemplate($template)->render($parameters);
}
}

View File

@ -11,12 +11,12 @@
<parameter key="debug">%kernel.debug%</parameter>
<parameter key="cache">%kernel.cache_dir%/twig</parameter>
</parameter>
<parameter key="twig.loader.class">Symfony\Bundle\TwigBundle\Loader\Loader</parameter>
<parameter key="twig.renderer.class">Symfony\Bundle\TwigBundle\Renderer\Renderer</parameter>
<parameter key="twig.loader.class">Symfony\Bundle\TwigBundle\Loader\FilesystemLoader</parameter>
<parameter key="twig.globals.class">Symfony\Bundle\TwigBundle\GlobalVariables</parameter>
<parameter key="twig.form.resources" type="collection">
<parameter>TwigBundle::form.twig</parameter>
</parameter>
<parameter key="templating.engine.twig.class">Symfony\Bundle\TwigBundle\TwigEngine</parameter>
</parameters>
<services>
@ -26,20 +26,22 @@
</service>
<service id="twig.loader" class="%twig.loader.class%">
<argument type="service" id="templating.name_converter" />
<argument type="service" id="templating.loader" />
<argument type="service" id="templating.name_parser" />
<argument>%templating.loader.filesystem.path%</argument>
<argument type="service" id="logger" on-invalid="ignore" />
</service>
<service id="templating.engine.twig" class="%templating.engine.twig.class%">
<tag name="templating.engine" priority="255" />
<argument type="service" id="service_container" />
<argument type="service" id="twig" />
<argument type="service" id="twig.globals" />
</service>
<service id="twig.globals" class="%twig.globals.class%">
<argument type="service" id="service_container" />
</service>
<service id="twig.renderer" class="%twig.renderer.class%">
<tag name="templating.renderer" alias="twig" />
<argument type="service" id="twig" />
<argument type="service" id="twig.globals" />
</service>
<service id="twig.extension.trans" class="Symfony\Bundle\TwigBundle\Extension\TransExtension" public="false">
<tag name="twig.extension" />
<argument type="service" id="translator" />

View File

@ -2,35 +2,28 @@
namespace Symfony\Bundle\TwigBundle\Tests;
use Symfony\Bundle\FrameworkBundle\Templating\Engine;
use Symfony\Bundle\TwigBundle\Renderer\Renderer;
use Symfony\Bundle\TwigBundle\Tests\TestCase;
use Symfony\Bundle\TwigBundle\TwigEngine;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session;
use Symfony\Component\HttpFoundation\SessionStorage\ArraySessionStorage;
use Symfony\Bundle\TwigBundle\GlobalVariables;
class RendererTest extends TestCase
class TwigEngineTest extends TestCase
{
public function testEvalutateAddsAppGlobal()
{
$environment = $this->getTwigEnvironment();
$container = $this->getContainer();
$renderer = new Renderer($environment, $app = new GlobalVariables($container));
$engine = new TwigEngine($container, $environment, $app = new GlobalVariables($container));
$engine = new Engine($container, $this->getMock('Symfony\Component\Templating\Loader\LoaderInterface'), array());
$storage = $this->getStorage();
$template = $this->getMock('\Twig_TemplateInterface');
$environment->expects($this->once())
->method('loadTemplate')
->with($storage)
->will($this->returnValue($template));
$renderer->setEngine($engine);
$renderer->evaluate($storage);
$engine->render('name');
$request = $container->get('request');
$globals = $environment->getGlobals();
@ -41,21 +34,17 @@ class RendererTest extends TestCase
{
$environment = $this->getTwigEnvironment();
$container = new Container();
$renderer = new Renderer($environment, new GlobalVariables($container));
$engine = new TwigEngine($container, $environment, new GlobalVariables($container));
$engine = new Engine($container, $this->getMock('Symfony\Component\Templating\Loader\LoaderInterface'), array());
$storage = $this->getStorage();
$template = $this->getMock('\Twig_TemplateInterface');
$environment->expects($this->once())
->method('loadTemplate')
->with($storage)
->will($this->returnValue($template));
$container->set('request', null);
$renderer->setEngine($engine);
$renderer->evaluate($storage);
$engine->render('name');
$globals = $environment->getGlobals();
$this->assertEmpty($globals['app']->getRequest());
@ -78,19 +67,6 @@ class RendererTest extends TestCase
return $container;
}
/**
* Creates a mock Storage object.
*
* @return Storage
*/
protected function getStorage()
{
return $this
->getMockBuilder('Symfony\Component\Templating\Storage\Storage')
->disableOriginalConstructor()
->getMock();
}
/**
* Creates a mock Twig_Environment object.
*

View File

@ -0,0 +1,122 @@
<?php
namespace Symfony\Bundle\TwigBundle;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\DependencyInjection\ContainerInterface;
/*
* 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.
*/
/**
* This engine knows how to render Twig templates.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class TwigEngine implements EngineInterface
{
protected $environment;
protected $container;
/**
* Constructor.
*
* @param ContainerInterface $container The DI container
* @param \Twig_Environment $environment A \Twig_Environment instance
* @param GlobalVariables $globals A GlobalVariables instance
*/
public function __construct(ContainerInterface $container, \Twig_Environment $environment, GlobalVariables $globals)
{
$this->container = $container;
$this->environment = $environment;
$environment->addGlobal('app', $globals);
}
/**
* Renders a template.
*
* @param string $name A template name
* @param array $parameters An array of parameters to pass to the template
*
* @return string The evaluated template as a string
*
* @throws \InvalidArgumentException if the template does not exist
* @throws \RuntimeException if the template cannot be rendered
*/
public function render($name, array $parameters = array())
{
return $this->load($name)->render($parameters);
}
/**
* Returns true if the template exists.
*
* @param string $name A template name
*
* @return Boolean true if the template exists, false otherwise
*/
public function exists($name)
{
try {
$this->load($name);
} catch (\Twig_Error_Loader $e) {
return false;
}
return true;
}
/**
* Loads the given template.
*
* @param string $name A template name
*
* @return \Twig_TemplateInterface A \Twig_TemplateInterface instance
*
* @throws \Twig_Error_Loader if the template cannot be found
*/
public function load($name)
{
return $this->environment->loadTemplate($name);
}
/**
* Returns true if this class is able to render the given template.
*
* @param string $name A template name
*
* @return boolean True if this class supports the given resource, false otherwise
*/
public function supports($name)
{
return false !== strpos($name, '.twig');
}
/**
* Renders a view and returns a Response.
*
* @param string $view The view name
* @param array $parameters An array of parameters to pass to the view
* @param Response $response A Response instance
*
* @return Response A Response instance
*/
public function renderResponse($view, array $parameters = array(), Response $response = null)
{
if (null === $response) {
$response = $this->container->get('response');
}
$response->setContent($this->render($view, $parameters));
return $response;
}
}

View File

@ -0,0 +1,116 @@
<?php
namespace Symfony\Component\Templating;
/*
* 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.
*/
/**
* DelegatingEngine selects an engine for a given template.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class DelegatingEngine implements EngineInterface
{
protected $engines;
/**
* Constructor.
*
* @param array $engines An array of EngineInterface instances to add
*/
public function __construct(array $engines = array())
{
$this->engines = array();
foreach ($engines as $engine) {
$this->addEngine($engine);
}
}
/**
* Renders a template.
*
* @param string $name A template name
* @param array $parameters An array of parameters to pass to the template
*
* @return string The evaluated template as a string
*
* @throws \InvalidArgumentException if the template does not exist
* @throws \RuntimeException if the template cannot be rendered
*/
public function render($name, array $parameters = array())
{
return $this->getEngine($name)->render($name, $parameters);
}
/**
* Returns true if the template exists.
*
* @param string $name A template name
*
* @return Boolean true if the template exists, false otherwise
*/
public function exists($name)
{
return $this->getEngine($name)->exists($name);
}
/**
* Loads the given template.
*
* @param string $name A template name
*
* @return \Twig_TemplateInterface A \Twig_TemplateInterface instance
*
* @throws \Twig_Error_Loader if the template cannot be found
*/
public function load($name)
{
return $this->getEngine($name)->load($name);
}
/**
* Adds an engine.
*
* @param EngineInterface $engine An EngineInterface instance
*/
public function addEngine(EngineInterface $engine)
{
$this->engines[] = $engine;
}
/**
* Returns true if this class is able to render the given template.
*
* @param string $name A template name
*
* @return boolean True if this class supports the given template, false otherwise
*/
public function supports($name)
{
foreach ($this->engines as $engine) {
if ($engine->supports($name)) {
return true;
}
}
return false;
}
protected function getEngine($name)
{
foreach ($this->engines as $engine) {
if ($engine->supports($name)) {
return $engine;
}
}
throw new \RuntimeException(sprintf('No engine is able to work with the "%s" template.', $name));
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace Symfony\Component\Templating;
/*
* 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.
*/
/**
* EngineInterface is the interface each engine must implement.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
interface EngineInterface
{
/**
* Renders a template.
*
* @param string $name A template name
* @param array $parameters An array of parameters to pass to the template
*
* @return string The evaluated template as a string
*
* @throws \RuntimeException if the template cannot be rendered
*/
function render($name, array $parameters = array());
/**
* Returns true if the template exists.
*
* @param string $name A template name
*
* @return Boolean true if the template exists, false otherwise
*/
function exists($name);
/**
* Loads the given template.
*
* @param string $name A template name
*
* @return mixed A renderable template
*
* @throws \Exception if the template cannot be found
*/
function load($name);
/**
* Returns true if this class is able to render the given template.
*
* @param string $name A template name
*
* @return boolean True if this class supports the given template, false otherwise
*/
function supports($name);
}

View File

@ -36,23 +36,22 @@ class CacheLoader extends Loader
*/
public function __construct(Loader $loader, $dir)
{
parent::__construct($loader->getTemplateNameParser());
$this->loader = $loader;
$this->dir = $dir;
parent::__construct();
}
/**
* Loads a template.
*
* @param string $template The logical template name
* @param array $options An array of options
*
* @return Storage|Boolean false if the template cannot be loaded, a Storage instance otherwise
*/
public function load($template, array $options = array())
public function load($template)
{
$options = $this->mergeDefaultOptions($options);
list($template, $options) = $this->nameParser->parse($template);
$tmp = md5($template.serialize($options)).'.tpl';
$dir = $this->dir.DIRECTORY_SEPARATOR.substr($tmp, 0, 2);
@ -64,7 +63,7 @@ class CacheLoader extends Loader
$this->debugger->log(sprintf('Fetching template "%s" from cache', $template));
}
return new FileStorage($path, $options['renderer']);
return new FileStorage($path);
}
if (false === $storage = $this->loader->load($template, $options)) {
@ -83,18 +82,17 @@ class CacheLoader extends Loader
$this->debugger->log(sprintf('Storing template "%s" in cache', $template));
}
return new FileStorage($path, $options['renderer']);
return new FileStorage($path);
}
/**
* Returns true if the template is still fresh.
*
* @param string $template The template name
* @param array $options An array of options
* @param timestamp $time The last modification time of the cached template
*/
public function isFresh($template, array $options = array(), $time)
public function isFresh($template, $time)
{
return $this->loader->isFresh($template, $options);
return $this->loader->isFresh($template, $time);
}
}

View File

@ -33,8 +33,6 @@ class ChainLoader extends Loader
foreach ($loaders as $loader) {
$this->addLoader($loader);
}
parent::__construct();
}
/**
@ -51,14 +49,13 @@ class ChainLoader extends Loader
* Loads a template.
*
* @param string $template The logical template name
* @param array $options An array of options
*
* @return Storage|Boolean false if the template cannot be loaded, a Storage instance otherwise
*/
public function load($template, array $options = array())
public function load($template)
{
foreach ($this->loaders as $loader) {
if (false !== $ret = $loader->load($template, $options)) {
if (false !== $ret = $loader->load($template)) {
return $ret;
}
}
@ -70,15 +67,12 @@ class ChainLoader extends Loader
* Returns true if the template is still fresh.
*
* @param string $template The template name
* @param array $options An array of options
* @param timestamp $time The last modification time of the cached template
*/
public function isFresh($template, array $options = array(), $time)
public function isFresh($template, $time)
{
foreach ($this->loaders as $loader) {
if (false !== $ret = $loader->load($template, $options)) {
return $loader->isFresh($template, $options);
}
return $loader->isFresh($template);
}
return false;

View File

@ -4,6 +4,7 @@ namespace Symfony\Component\Templating\Loader;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\TemplateNameParserInterface;
/*
* This file is part of the Symfony package.
@ -26,34 +27,35 @@ class FilesystemLoader extends Loader
/**
* Constructor.
*
* @param array $templatePathPatterns An array of path patterns to look for templates
* @param TemplateNameParserInterface $nameParser A TemplateNameParserInterface instance
* @param array $templatePathPatterns An array of path patterns to look for templates
*/
public function __construct($templatePathPatterns)
public function __construct(TemplateNameParserInterface $nameParser, $templatePathPatterns)
{
parent::__construct($nameParser);
if (!is_array($templatePathPatterns)) {
$templatePathPatterns = array($templatePathPatterns);
}
$this->templatePathPatterns = $templatePathPatterns;
parent::__construct();
}
/**
* Loads a template.
*
* @param string $template The logical template name
* @param array $options An array of options
*
* @return Storage|Boolean false if the template cannot be loaded, a Storage instance otherwise
*/
public function load($template, array $options = array())
public function load($template)
{
list($template, $options) = $this->nameParser->parse($template);
if (self::isAbsolutePath($template) && file_exists($template)) {
return new FileStorage($template, $options['renderer']);
return new FileStorage($template);
}
$options = $this->mergeDefaultOptions($options);
$options['name'] = $template;
$replacements = array();
@ -65,14 +67,14 @@ class FilesystemLoader extends Loader
foreach ($this->templatePathPatterns as $templatePathPattern) {
if (is_file($file = strtr($templatePathPattern, $replacements))) {
if (null !== $this->debugger) {
$this->debugger->log(sprintf('Loaded template file "%s" (renderer: %s)', $file, $options['renderer']));
$this->debugger->log(sprintf('Loaded template file "%s"', $file));
}
return new FileStorage($file, $options['renderer']);
return new FileStorage($file);
}
if (null !== $this->debugger) {
$logs[] = sprintf('Failed loading template file "%s" (renderer: %s)', $file, $options['renderer']);
$logs[] = sprintf('Failed loading template file "%s"', $file);
}
}
@ -92,9 +94,9 @@ class FilesystemLoader extends Loader
* @param array $options An array of options
* @param timestamp $time The last modification time of the cached template
*/
public function isFresh($template, array $options = array(), $time)
public function isFresh($template, $time)
{
if (false === $template = $this->load($template, $options))
if (false === $template = $this->load($template))
{
return false;
}

View File

@ -3,6 +3,7 @@
namespace Symfony\Component\Templating\Loader;
use Symfony\Component\Templating\DebuggerInterface;
use Symfony\Component\Templating\TemplateNameParserInterface;
/*
* This file is part of the Symfony package.
@ -21,11 +22,26 @@ use Symfony\Component\Templating\DebuggerInterface;
abstract class Loader implements LoaderInterface
{
protected $debugger;
protected $defaultOptions;
protected $nameParser;
public function __construct()
/**
* Constructor.
*
* @param TemplateNameParserInterface $nameParser A TemplateNameParserInterface instance
*/
public function __construct(TemplateNameParserInterface $nameParser)
{
$this->defaultOptions = array('renderer' => 'php');
$this->nameParser = $nameParser;
}
/**
* Gets the template name parser.
*
* @return TemplateNameParserInterface A TemplateNameParserInterface instance
*/
public function getTemplateNameParser()
{
return $this->nameParser;
}
/**
@ -37,27 +53,4 @@ abstract class Loader implements LoaderInterface
{
$this->debugger = $debugger;
}
/**
* Sets a default option.
*
* @param string $name The option name
* @param mixed $value The option value
*/
public function setDefaultOption($name, $value)
{
$this->defaultOptions[$name] = $value;
}
/**
* Merges the default options with the given set of options.
*
* @param array $options An array of options
*
* @return array The merged set of options
*/
protected function mergeDefaultOptions(array $options)
{
return array_merge($this->defaultOptions, $options);
}
}

View File

@ -22,18 +22,16 @@ interface LoaderInterface
* Loads a template.
*
* @param string $template The logical template name
* @param array $options An array of options
*
* @return Storage|Boolean false if the template cannot be loaded, a Storage instance otherwise
*/
function load($template, array $options = array());
function load($template);
/**
* Returns true if the template is still fresh.
*
* @param string $template The template name
* @param array $options An array of options
* @param timestamp $time The last modification time of the cached template
*/
function isFresh($template, array $options = array(), $time);
function isFresh($template, $time);
}

View File

@ -2,9 +2,11 @@
namespace Symfony\Component\Templating;
use Symfony\Component\Templating\Loader\LoaderInterface;
use Symfony\Component\Templating\Renderer\RendererInterface;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\Storage\StringStorage;
use Symfony\Component\Templating\Helper\HelperInterface;
use Symfony\Component\Templating\Loader\LoaderInterface;
/*
* This file is part of the Symfony package.
@ -16,14 +18,13 @@ use Symfony\Component\Templating\Helper\HelperInterface;
*/
/**
* Engine is the main class of the templating component.
* PhpEngine is an engine able to render PHP templates.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class Engine implements \ArrayAccess
class PhpEngine implements EngineInterface, \ArrayAccess
{
protected $loader;
protected $renderers;
protected $current;
protected $helpers;
protected $parents;
@ -41,16 +42,14 @@ class Engine implements \ArrayAccess
*/
public function __construct(LoaderInterface $loader, array $helpers = array())
{
$this->loader = $loader;
$this->helpers = array();
$this->parents = array();
$this->stack = array();
$this->charset = 'UTF-8';
$this->cache = array();
$this->globals = array();
$this->renderers = array();
$this->loader = $loader;
$this->parents = array();
$this->stack = array();
$this->charset = 'UTF-8';
$this->cache = array();
$this->globals = array();
$this->addHelpers($helpers);
$this->setHelpers($helpers);
$this->initializeEscapers();
foreach ($this->escapers as $context => $escaper) {
@ -58,51 +57,30 @@ class Engine implements \ArrayAccess
}
}
public function setRenderers(array $renderers = array())
{
$this->renderers = array();
foreach ($renderers as $name => $renderer) {
$this->setRenderer($name, $renderer);
}
}
/**
* Renders a template.
*
* The template name is composed of segments separated by a colon (:).
* By default, this engine knows how to parse templates with one or two segments:
*
* * index: The template logical name is index and the renderer is php
* * index:twig: The template logical name is index and the renderer is twig
*
* @param string $name A template name
* @param array $parameters An array of parameters to pass to the template
*
* @return string The evaluated template as a string
*
* @throws \InvalidArgumentException if the renderer does not exist or if the template does not exist
* @throws \RuntimeException if the template cannot be rendered
* @throws \InvalidArgumentException if the template does not exist
* @throws \RuntimeException if the template cannot be rendered
*/
public function render($name, array $parameters = array())
{
$template = $this->load($name);
// renderer
$renderer = $template->getRenderer();
if (!isset($this->renderers[$renderer])) {
throw new \InvalidArgumentException(sprintf('The renderer "%s" is not registered.', $renderer));
}
$this->current = $name;
$this->parents[$name] = null;
// Attach the global variables
// attach the global variables
$parameters = array_replace($this->getGlobals(), $parameters);
// render
if (false === $content = $this->renderers[$renderer]->evaluate($template, $parameters)) {
throw new \RuntimeException(sprintf('The template "%s" cannot be rendered (renderer: %s).', $name, $renderer));
if (false === $content = $this->evaluate($template, $parameters)) {
throw new \RuntimeException(sprintf('The template "%s" cannot be rendered.', $name));
}
// decorator
@ -111,11 +89,6 @@ class Engine implements \ArrayAccess
$this->stack[] = $slots->get('_content');
$slots->set('_content', $content);
// a decorator must use the same renderer as its children
$parent = $this->load($this->parents[$name]);
if ($renderer !== $parentRenderer = ($parent->getRenderer() ? $parent->getRenderer() : $renderer)) {
throw new \LogicException(sprintf('Template "%s" extends "%s" but a "%s" template cannot extend a "%s" one.', $name, $this->parents[$name], $renderer, $parentRenderer));
}
$content = $this->render($this->parents[$name], $parameters);
$slots->set('_content', array_pop($this->stack));
@ -157,13 +130,11 @@ class Engine implements \ArrayAccess
return $this->cache[$name];
}
list($tpl, $options) = $this->splitTemplateName($name);
// load
$template = $this->loader->load($tpl, $options);
$template = $this->loader->load($name);
if (false === $template) {
throw new \InvalidArgumentException(sprintf('The template "%s" does not exist (renderer: %s).', $name, $options['renderer']));
throw new \InvalidArgumentException(sprintf('The template "%s" does not exist.', $name));
}
$this->cache[$name] = $template;
@ -171,6 +142,48 @@ class Engine implements \ArrayAccess
return $template;
}
/**
* Returns true if this class is able to render the given template.
*
* @param string $name A template name
*
* @return boolean True if this class supports the given resource, false otherwise
*/
public function supports($name)
{
return false !== strpos($name, '.php');
}
/**
* 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 engine is unable to render the template
*/
protected function evaluate(Storage $template, array $parameters = array())
{
$__template__ = $template;
if ($__template__ instanceof FileStorage) {
extract($parameters);
$view = $this;
ob_start();
require $__template__;
return ob_get_clean();
} elseif ($__template__ instanceof StringStorage) {
extract($parameters);
$view = $this;
ob_start();
eval('; ?>'.$__template__.'<?php ;');
return ob_get_clean();
}
return false;
}
/**
* Gets a helper value.
*
@ -228,6 +241,12 @@ class Engine implements \ArrayAccess
}
}
public function setHelpers(array $helpers)
{
$this->helpers = array();
$this->addHelpers($helpers);
}
/**
* Sets a helper.
*
@ -316,48 +335,6 @@ class Engine implements \ArrayAccess
return $this->charset;
}
/**
* Gets the loader associated with this engine.
*
* @return LoaderInterface A LoaderInterface instance
*/
public function getLoader()
{
return $this->loader;
}
/**
* Sets a template renderer.
*
* @param string $name The renderer name
* @param RendererInterface $renderer A RendererInterface instance
*/
public function setRenderer($name, RendererInterface $renderer)
{
$this->renderers[$name] = $renderer;
$renderer->setEngine($this);
}
/**
* Converts a short template notation to a template name and an array of options.
*
* @param string $name A short template template
* @param array $defaults An array of default options
*
* @return array An array composed of the template name and an array of options
*/
public function splitTemplateName($name)
{
if (false !== $pos = strpos($name, ':')) {
$renderer = substr($name, $pos + 1);
$name = substr($name, 0, $pos);
} else {
$renderer = 'php';
}
return array($name, array('renderer' => $renderer));
}
/**
* Adds an escaper for the given context.
*
@ -493,4 +470,14 @@ class Engine implements \ArrayAccess
throw new \RuntimeException('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
}
}
/**
* Gets the loader associated with this engine.
*
* @return LoaderInterface A LoaderInterface instance
*/
public function getLoader()
{
return $this->loader;
}
}

View File

@ -1,54 +0,0 @@
<?php
namespace Symfony\Component\Templating\Renderer;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\Storage\StringStorage;
/*
* 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.
*/
/**
* PhpRenderer is a renderer for PHP templates.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class PhpRenderer extends Renderer
{
/**
* 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())
{
$__template__ = $template;
if ($__template__ instanceof FileStorage) {
extract($parameters);
$view = $this->engine;
ob_start();
require $__template__;
return ob_get_clean();
} elseif ($__template__ instanceof StringStorage) {
extract($parameters);
$view = $this->engine;
ob_start();
eval('; ?>'.$__template__.'<?php ;');
return ob_get_clean();
}
return false;
}
}

View File

@ -1,34 +0,0 @@
<?php
namespace Symfony\Component\Templating\Renderer;
use Symfony\Component\Templating\Engine;
/*
* 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.
*/
/**
* Renderer is the base class for all template renderer.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
abstract class Renderer implements RendererInterface
{
protected $engine;
/**
* Sets the template engine associated with this renderer.
*
* @param Engine $engine A Engine instance
*/
public function setEngine(Engine $engine)
{
$this->engine = $engine;
}
}

View File

@ -1,40 +0,0 @@
<?php
namespace Symfony\Component\Templating\Renderer;
use Symfony\Component\Templating\Engine;
use Symfony\Component\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.
*/
/**
* RendererInterface is the interface all renderer classes must implement.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
interface RendererInterface
{
/**
* 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
*/
function evaluate(Storage $template, array $parameters = array());
/**
* Sets the template engine associated with this renderer.
*
* @param Engine $engine A Engine instance
*/
function setEngine(Engine $engine);
}

View File

@ -18,19 +18,16 @@ namespace Symfony\Component\Templating\Storage;
*/
abstract class Storage
{
protected $renderer;
protected $template;
/**
* Constructor.
*
* @param string $template The template name
* @param string $renderer The renderer name
*/
public function __construct($template, $renderer)
public function __construct($template)
{
$this->template = $template;
$this->renderer = $renderer;
}
/**
@ -49,14 +46,4 @@ abstract class Storage
* @return string The template content
*/
abstract public function getContent();
/**
* Gets the renderer.
*
* @return string|null The renderer name or null if no renderer is stored for this template
*/
public function getRenderer()
{
return $this->renderer;
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace Symfony\Component\Templating;
/*
* 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.
*/
/**
* TemplateNameParser is the default implementation of TemplateNameParserInterface.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class TemplateNameParser implements TemplateNameParserInterface
{
protected $defaultOptions = array();
/**
* Parses a template to a template name and an array of options.
*
* @param string $name A template name
* @param array $defaults An array of default options
*
* @return array An array composed of the template name and an array of options
*/
public function parse($name, array $defaults = array())
{
return array($name, array_merge($this->defaultOptions, $defaults));
}
/**
* Sets a default option.
*
* @param string $name The option name
* @param mixed $value The option value
*/
public function setDefaultOption($name, $value)
{
$this->defaultOptions[$name] = $value;
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace Symfony\Component\Templating;
/*
* 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.
*/
/**
* TemplateNameParserInterface parses template name to a template name and an array of options.
*
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
interface TemplateNameParserInterface
{
/**
* Parses a template to a template name and an array of options.
*
* @param string $name A template name
* @param array $defaults An array of default options
*
* @return array An array composed of the template name and an array of options
*/
function parse($name, array $defaults = array());
}

View File

@ -16,12 +16,13 @@ require_once __DIR__.'/../Fixtures/ProjectTemplateDebugger.php';
use Symfony\Component\Templating\Loader\Loader;
use Symfony\Component\Templating\Loader\CacheLoader;
use Symfony\Component\Templating\Storage\StringStorage;
use Symfony\Component\Templating\TemplateNameParser;
class CacheLoaderTest extends \PHPUnit_Framework_TestCase
{
public function testConstructor()
{
$loader = new ProjectTemplateLoader($varLoader = new ProjectTemplateLoaderVar(), sys_get_temp_dir());
$loader = new ProjectTemplateLoader($varLoader = new ProjectTemplateLoaderVar(new TemplateNameParser()), sys_get_temp_dir());
$this->assertTrue($loader->getLoader() === $varLoader, '__construct() takes a template loader as its first argument');
$this->assertEquals(sys_get_temp_dir(), $loader->getDir(), '__construct() takes a directory where to store the cache as its second argument');
}
@ -31,7 +32,7 @@ class CacheLoaderTest extends \PHPUnit_Framework_TestCase
$dir = sys_get_temp_dir().DIRECTORY_SEPARATOR.rand(111111, 999999);
mkdir($dir, 0777, true);
$loader = new ProjectTemplateLoader($varLoader = new ProjectTemplateLoaderVar(), $dir);
$loader = new ProjectTemplateLoader($varLoader = new ProjectTemplateLoaderVar(new TemplateNameParser()), $dir);
$loader->setDebugger($debugger = new \ProjectTemplateDebugger());
$this->assertFalse($loader->load('foo'), '->load() returns false if the embed loader is not able to load the template');
$loader->load('index');
@ -66,16 +67,16 @@ class ProjectTemplateLoaderVar extends Loader
return 'Hello {{ name }}';
}
public function load($template, array $options = array())
public function load($template)
{
if (method_exists($this, $method = 'get'.ucfirst($template).'Template')) {
return new StringStorage($this->$method(), '.php');
return new StringStorage($this->$method());
}
return false;
}
public function isFresh($template, array $options = array(), $time)
public function isFresh($template, $time)
{
return false;
}

View File

@ -16,37 +16,39 @@ require_once __DIR__.'/../Fixtures/ProjectTemplateDebugger.php';
use Symfony\Component\Templating\Loader\ChainLoader;
use Symfony\Component\Templating\Loader\FilesystemLoader;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\TemplateNameParser;
class ChainLoaderTest extends \PHPUnit_Framework_TestCase
{
static protected $loader1, $loader2;
protected $loader1;
protected $loader2;
static public function setUpBeforeClass()
public function setUp()
{
$fixturesPath = realpath(__DIR__.'/../Fixtures/');
self::$loader1 = new FilesystemLoader($fixturesPath.'/null/%name%');
self::$loader2 = new FilesystemLoader($fixturesPath.'/templates/%name%.%renderer%');
$this->loader1 = new FilesystemLoader(new TemplateNameParser(), $fixturesPath.'/null/%name%');
$this->loader2 = new FilesystemLoader(new TemplateNameParser(), $fixturesPath.'/templates/%name%');
}
public function testConstructor()
{
$loader = new ProjectTemplateLoader1(array(self::$loader1, self::$loader2));
$this->assertEquals(array(self::$loader1, self::$loader2), $loader->getLoaders(), '__construct() takes an array of template loaders as its second argument');
$loader = new ProjectTemplateLoader1(array($this->loader1, $this->loader2));
$this->assertEquals(array($this->loader1, $this->loader2), $loader->getLoaders(), '__construct() takes an array of template loaders as its second argument');
}
public function testAddLoader()
{
$loader = new ProjectTemplateLoader1(array(self::$loader1));
$loader->addLoader(self::$loader2);
$this->assertEquals(array(self::$loader1, self::$loader2), $loader->getLoaders(), '->addLoader() adds a template loader at the end of the loaders');
$loader = new ProjectTemplateLoader1(array($this->loader1));
$loader->addLoader($this->loader2);
$this->assertEquals(array($this->loader1, $this->loader2), $loader->getLoaders(), '->addLoader() adds a template loader at the end of the loaders');
}
public function testLoad()
{
$loader = new ProjectTemplateLoader1(array(self::$loader1, self::$loader2));
$loader = new ProjectTemplateLoader1(array($this->loader1, $this->loader2));
$this->assertFalse($loader->load('bar'), '->load() returns false if the template is not found');
$this->assertFalse($loader->load('foo', array('renderer' => 'xml')), '->load() returns false if the template does not exists for the given renderer');
$this->assertInstanceOf('Symfony\Component\Templating\Storage\FileStorage', $loader->load('foo'), '->load() returns a FileStorage if the template exists');
$this->assertFalse($loader->load('foo'), '->load() returns false if the template does not exists for the given renderer');
$this->assertInstanceOf('Symfony\Component\Templating\Storage\FileStorage', $loader->load('foo.php'), '->load() returns a FileStorage if the template exists');
}
}

View File

@ -15,6 +15,7 @@ require_once __DIR__.'/../Fixtures/ProjectTemplateDebugger.php';
use Symfony\Component\Templating\Loader\FilesystemLoader;
use Symfony\Component\Templating\Storage\FileStorage;
use Symfony\Component\Templating\TemplateNameParser;
class FilesystemLoaderTest extends \PHPUnit_Framework_TestCase
{
@ -29,9 +30,9 @@ class FilesystemLoaderTest extends \PHPUnit_Framework_TestCase
{
$pathPattern = self::$fixturesPath.'/templates/%name%.%renderer%';
$path = self::$fixturesPath.'/templates';
$loader = new ProjectTemplateLoader2($pathPattern);
$loader = new ProjectTemplateLoader2(new TemplateNameParser(), $pathPattern);
$this->assertEquals(array($pathPattern), $loader->getTemplatePathPatterns(), '__construct() takes a path as its second argument');
$loader = new ProjectTemplateLoader2(array($pathPattern));
$loader = new ProjectTemplateLoader2(new TemplateNameParser(), array($pathPattern));
$this->assertEquals(array($pathPattern), $loader->getTemplatePathPatterns(), '__construct() takes an array of paths as its second argument');
}
@ -45,27 +46,27 @@ class FilesystemLoaderTest extends \PHPUnit_Framework_TestCase
public function testLoad()
{
$pathPattern = self::$fixturesPath.'/templates/%name%.%renderer%';
$pathPattern = self::$fixturesPath.'/templates/%name%';
$path = self::$fixturesPath.'/templates';
$loader = new ProjectTemplateLoader2($pathPattern);
$storage = $loader->load($path.'/foo.php', array('renderer' => 'php'));
$loader = new ProjectTemplateLoader2(new TemplateNameParser(), $pathPattern);
$storage = $loader->load($path.'/foo.php');
$this->assertInstanceOf('Symfony\Component\Templating\Storage\FileStorage', $storage, '->load() returns a FileStorage if you pass an absolute path');
$this->assertEquals($path.'/foo.php', (string) $storage, '->load() returns a FileStorage pointing to the passed absolute path');
$this->assertFalse($loader->load('bar'), '->load() returns false if the template is not found');
$storage = $loader->load('foo');
$storage = $loader->load('foo.php');
$this->assertInstanceOf('Symfony\Component\Templating\Storage\FileStorage', $storage, '->load() returns a FileStorage if you pass a relative template that exists');
$this->assertEquals($path.'/foo.php', (string) $storage, '->load() returns a FileStorage pointing to the absolute path of the template');
$loader = new ProjectTemplateLoader2($pathPattern);
$loader = new ProjectTemplateLoader2(new TemplateNameParser(), $pathPattern);
$loader->setDebugger($debugger = new \ProjectTemplateDebugger());
$this->assertFalse($loader->load('foo', array('renderer' => 'xml')), '->load() returns false if the template does not exists for the given renderer');
$this->assertFalse($loader->load('foo.xml'), '->load() returns false if the template does not exists for the given renderer');
$this->assertTrue($debugger->hasMessage('Failed loading template'), '->load() logs a "Failed loading template" message if the template is not found');
$loader = new ProjectTemplateLoader2(array(self::$fixturesPath.'/null/%name%', $pathPattern));
$loader = new ProjectTemplateLoader2(new TemplateNameParser(), array(self::$fixturesPath.'/null/%name%', $pathPattern));
$loader->setDebugger($debugger = new \ProjectTemplateDebugger());
$loader->load('foo');
$loader->load('foo.php');
$this->assertTrue($debugger->hasMessage('Loaded template file'), '->load() logs a "Loaded template file" message if the template is found');
}
}

View File

@ -14,12 +14,13 @@ namespace Symfony\Tests\Component\Templating\Loader;
require_once __DIR__.'/../Fixtures/ProjectTemplateDebugger.php';
use Symfony\Component\Templating\Loader\Loader;
use Symfony\Component\Templating\TemplateNameParser;
class LoaderTest extends \PHPUnit_Framework_TestCase
{
public function testGetSetDebugger()
{
$loader = new ProjectTemplateLoader4();
$loader = new ProjectTemplateLoader4(new TemplateNameParser());
$loader->setDebugger($debugger = new \ProjectTemplateDebugger());
$this->assertTrue($loader->getDebugger() === $debugger, '->setDebugger() sets the debugger instance');
}
@ -27,7 +28,7 @@ class LoaderTest extends \PHPUnit_Framework_TestCase
class ProjectTemplateLoader4 extends Loader
{
public function load($template, array $options = array())
public function load($template)
{
}
@ -36,7 +37,7 @@ class ProjectTemplateLoader4 extends Loader
return $this->debugger;
}
public function isFresh($template, array $options = array(), $time)
public function isFresh($template, $time)
{
return false;
}

View File

@ -13,41 +13,31 @@ namespace Symfony\Tests\Component\Templating;
require_once __DIR__.'/Fixtures/SimpleHelper.php';
use Symfony\Component\Templating\Engine;
use Symfony\Component\Templating\PhpEngine;
use Symfony\Component\Templating\Loader\Loader;
use Symfony\Component\Templating\Renderer\Renderer;
use Symfony\Component\Templating\Renderer\PhpRenderer;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\Storage\StringStorage;
use Symfony\Component\Templating\Helper\SlotsHelper;
use Symfony\Component\Templating\TemplateNameParser;
class EngineTest extends \PHPUnit_Framework_TestCase
class PhpEngineTest extends \PHPUnit_Framework_TestCase
{
static protected $loader, $renderer;
protected $loader;
static public function setUpBeforeClass()
public function setUp()
{
self::$loader = new ProjectTemplateLoader();
self::$renderer = new ProjectTemplateRenderer();
$this->loader = new ProjectTemplateLoader(new TemplateNameParser());
}
public function testConstructor()
{
$engine = new ProjectTemplateEngine(self::$loader);
$this->assertEquals(self::$loader, $engine->getLoader(), '__construct() takes a loader instance as its second first argument');
}
public function testSetRenderers()
{
$engine = new ProjectTemplateEngine(self::$loader);
$engine->setRenderers(array('foo' => self::$renderer));
$this->assertEquals(array('foo'), array_keys($engine->getRenderers()));
$this->assertTrue(self::$renderer->getEngine() === $engine, 'setRenderers() registers itself on all renderers');
$engine = new ProjectTemplateEngine($this->loader);
$this->assertEquals($this->loader, $engine->getLoader(), '__construct() takes a loader instance as its second first argument');
}
public function testOffsetGet()
{
$engine = new ProjectTemplateEngine(self::$loader);
$engine = new ProjectTemplateEngine($this->loader);
$engine->set($helper = new \SimpleHelper('bar'), 'foo');
$this->assertEquals($helper, $engine['foo'], '->offsetGet() returns the value of a helper');
@ -62,7 +52,7 @@ class EngineTest extends \PHPUnit_Framework_TestCase
public function testGetSetHas()
{
$engine = new ProjectTemplateEngine(self::$loader);
$engine = new ProjectTemplateEngine($this->loader);
$foo = new \SimpleHelper('foo');
$engine->set($foo);
$this->assertEquals($foo, $engine->get('foo'), '->set() sets a helper');
@ -84,43 +74,32 @@ class EngineTest extends \PHPUnit_Framework_TestCase
public function testExtendRender()
{
$engine = new ProjectTemplateEngine(self::$loader, array(), array(new SlotsHelper()));
$engine = new ProjectTemplateEngine($this->loader, array(), array(new SlotsHelper()));
try {
$engine->render('name');
$this->fail('->render() throws an InvalidArgumentException if the template does not exist');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->render() throws an InvalidArgumentException if the template does not exist');
$this->assertEquals('The template "name" does not exist (renderer: php).', $e->getMessage(), '->render() throws an InvalidArgumentException if the template does not exist');
$this->assertEquals('The template "name" does not exist.', $e->getMessage(), '->render() throws an InvalidArgumentException if the template does not exist');
}
try {
self::$loader->setTemplate('name.foo', 'foo');
$engine->render('foo:name');
$this->fail('->render() throws an InvalidArgumentException if no renderer is registered for the given renderer');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->render() throws an InvalidArgumentException if no renderer is registered for the given renderer');
$this->assertEquals('The template "foo:name" does not exist (renderer: name).', $e->getMessage(), '->render() throws an InvalidArgumentException if no renderer is registered for the given renderer');
}
$engine = new ProjectTemplateEngine(self::$loader, array(new SlotsHelper()));
$engine->setRenderer('php', new PhpRenderer());
$engine = new ProjectTemplateEngine($this->loader, array(new SlotsHelper()));
$engine->set(new \SimpleHelper('bar'));
self::$loader->setTemplate('foo.php', '<?php $view->extend("layout"); echo $view[\'foo\'].$foo ?>');
self::$loader->setTemplate('layout.php', '-<?php echo $view[\'slots\']->get("_content") ?>-');
$this->assertEquals('-barfoo-', $engine->render('foo', array('foo' => 'foo')), '->render() uses the decorator to decorate the template');
$this->loader->setTemplate('foo.php', '<?php $view->extend("layout.php"); echo $view[\'foo\'].$foo ?>');
$this->loader->setTemplate('layout.php', '-<?php echo $view[\'slots\']->get("_content") ?>-');
$this->assertEquals('-barfoo-', $engine->render('foo.php', array('foo' => 'foo')), '->render() uses the decorator to decorate the template');
$engine = new ProjectTemplateEngine(self::$loader, array(new SlotsHelper()));
$engine->setRenderer('php', new PhpRenderer());
$engine = new ProjectTemplateEngine($this->loader, array(new SlotsHelper()));
$engine->set(new \SimpleHelper('bar'));
self::$loader->setTemplate('bar.php', 'bar');
self::$loader->setTemplate('foo.php', '<?php $view->extend("layout"); echo $foo ?>');
self::$loader->setTemplate('layout.php', '<?php echo $view->render("bar") ?>-<?php echo $view[\'slots\']->get("_content") ?>-');
$this->assertEquals('bar-foo-', $engine->render('foo', array('foo' => 'foo', 'bar' => 'bar')), '->render() supports render() calls in templates');
$this->loader->setTemplate('bar.php', 'bar');
$this->loader->setTemplate('foo.php', '<?php $view->extend("layout.php"); echo $foo ?>');
$this->loader->setTemplate('layout.php', '<?php echo $view->render("bar.php") ?>-<?php echo $view[\'slots\']->get("_content") ?>-');
$this->assertEquals('bar-foo-', $engine->render('foo.php', array('foo' => 'foo', 'bar' => 'bar')), '->render() supports render() calls in templates');
}
public function testEscape()
{
$engine = new ProjectTemplateEngine(self::$loader);
$engine = new ProjectTemplateEngine($this->loader);
$this->assertEquals('&lt;br /&gt;', $engine->escape('<br />'), '->escape() escapes strings');
$foo = new \stdClass();
$this->assertEquals($foo, $engine->escape($foo), '->escape() does nothing on non strings');
@ -128,7 +107,7 @@ class EngineTest extends \PHPUnit_Framework_TestCase
public function testGetSetCharset()
{
$engine = new ProjectTemplateEngine(self::$loader);
$engine = new ProjectTemplateEngine($this->loader);
$this->assertEquals('UTF-8', $engine->getCharset(), '->getCharset() returns UTF-8 by default');
$engine->setCharset('ISO-8859-1');
$this->assertEquals('ISO-8859-1', $engine->getCharset(), '->setCharset() changes the default charset to use');
@ -136,7 +115,7 @@ class EngineTest extends \PHPUnit_Framework_TestCase
public function testGlobalVariables()
{
$engine = new ProjectTemplateEngine(self::$loader);
$engine = new ProjectTemplateEngine($this->loader);
$engine->addGlobal('global_variable', 'lorem ipsum');
$this->assertEquals(array(
@ -146,37 +125,23 @@ class EngineTest extends \PHPUnit_Framework_TestCase
public function testGlobalsGetPassedToTemplate()
{
$engine = new ProjectTemplateEngine(self::$loader);
$engine->setRenderer('php', new PhpRenderer());
$engine = new ProjectTemplateEngine($this->loader);
$engine->addGlobal('global', 'global variable');
self::$loader->setTemplate('global.php', '<?php echo $global; ?>');
$this->loader->setTemplate('global.php', '<?php echo $global; ?>');
$this->assertEquals($engine->render('global:php'), 'global variable');
$this->assertEquals($engine->render('global.php'), 'global variable');
$this->assertEquals($engine->render('global:php', array('global' => 'overwritten')), 'overwritten');
$this->assertEquals($engine->render('global.php', array('global' => 'overwritten')), 'overwritten');
}
}
class ProjectTemplateEngine extends Engine
class ProjectTemplateEngine extends PhpEngine
{
public function getLoader()
{
return $this->loader;
}
public function getRenderers()
{
return $this->renderers;
}
}
class ProjectTemplateRenderer extends PhpRenderer
{
public function getEngine()
{
return $this->engine;
}
}
class ProjectTemplateLoader extends Loader
@ -188,25 +153,17 @@ class ProjectTemplateLoader extends Loader
$this->templates[$name] = $template;
}
public function load($template, array $options = array())
public function load($template)
{
if (isset($this->templates[$template.'.'.$options['renderer']])) {
return new StringStorage($this->templates[$template.'.'.$options['renderer']], $options['renderer']);
if (isset($this->templates[$template])) {
return new StringStorage($this->templates[$template]);
}
return false;
}
public function isFresh($template, array $options = array(), $time)
public function isFresh($template, $time)
{
return false;
}
}
class FooTemplateRenderer extends Renderer
{
public function evaluate(Storage $template, array $parameters = array())
{
return 'foo';
}
}

View File

@ -1,31 +0,0 @@
<?php
/*
* 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.
*/
namespace Symfony\Tests\Component\Templating\Renderer;
use Symfony\Component\Templating\Renderer\PhpRenderer;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\Storage\StringStorage;
use Symfony\Component\Templating\Storage\FileStorage;
class PhpRendererTest extends \PHPUnit_Framework_TestCase
{
public function testEvaluate()
{
$renderer = new PhpRenderer();
$template = new StringStorage('<?php echo $foo ?>', 'php');
$this->assertEquals('bar', $renderer->evaluate($template, array('foo' => 'bar')), '->evaluate() renders templates that are instances of StringStorage');
$template = new FileStorage(__DIR__.'/../Fixtures/templates/foo.php', 'php');
$this->assertEquals('bar', $renderer->evaluate($template, array('foo' => 'bar')), '->evaluate() renders templates that are instances of FileStorage');
}
}

View File

@ -1,42 +0,0 @@
<?php
/*
* 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.
*/
namespace Symfony\Tests\Component\Templating\Renderer;
require_once __DIR__.'/../Fixtures/SimpleHelper.php';
use Symfony\Component\Templating\Engine;
use Symfony\Component\Templating\Renderer\Renderer;
use Symfony\Component\Templating\Storage\Storage;
use Symfony\Component\Templating\Loader\FilesystemLoader;
class RendererTest extends \PHPUnit_Framework_TestCase
{
public function testSetEngine()
{
$loader = new FilesystemLoader(array(__DIR__.'/../Fixtures/templates/%name%.%renderer%'));
$engine = new Engine($loader);
$renderer = new ProjectTemplateRenderer();
$renderer->setEngine($engine);
$this->assertTrue($renderer->getEngine() === $engine, '->setEngine() sets the engine instance tied to this renderer');
}
}
class ProjectTemplateRenderer extends Renderer
{
public function getEngine()
{
return $this->engine;
}
public function evaluate(Storage $template, array $parameters = array())
{
}
}

View File

@ -18,9 +18,9 @@ class FileStorageTest extends \PHPUnit_Framework_TestCase
{
public function testGetContent()
{
$storage = new FileStorage('foo', 'php');
$storage = new FileStorage('foo');
$this->assertInstanceOf('Symfony\Component\Templating\Storage\Storage', $storage, 'FileStorage is an instance of Storage');
$storage = new FileStorage(__DIR__.'/../Fixtures/templates/foo.php', 'php');
$storage = new FileStorage(__DIR__.'/../Fixtures/templates/foo.php');
$this->assertEquals('<?php echo $foo ?>', $storage->getContent(), '->getContent() returns the content of the template');
}
}

View File

@ -18,15 +18,9 @@ class StorageTest extends \PHPUnit_Framework_TestCase
{
public function testMagicToString()
{
$storage = new TestStorage('foo', 'php');
$storage = new TestStorage('foo');
$this->assertEquals('foo', (string) $storage, '__toString() returns the template name');
}
public function testGetRenderer()
{
$storage = new TestStorage('foo', 'php');
$this->assertEquals('php', $storage->getRenderer(), '->getRenderer() returns the renderer');
}
}
class TestStorage extends Storage

View File

@ -18,9 +18,9 @@ class StringStorageTest extends \PHPUnit_Framework_TestCase
{
public function testGetContent()
{
$storage = new StringStorage('foo', 'php');
$storage = new StringStorage('foo');
$this->assertInstanceOf('Symfony\Component\Templating\Storage\Storage', $storage, 'StringStorage is an instance of Storage');
$storage = new StringStorage('foo', 'php');
$storage = new StringStorage('foo');
$this->assertEquals('foo', $storage->getContent(), '->getContent() returns the content of the template');
}
}