Actualización

This commit is contained in:
Xes
2025-04-10 12:24:57 +02:00
parent 8969cc929d
commit 45420b6f0d
39760 changed files with 4303286 additions and 0 deletions

3
vendor/symfony/twig-bundle/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
vendor/
composer.lock
phpunit.xml

36
vendor/symfony/twig-bundle/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,36 @@
CHANGELOG
=========
2.7.0
-----
* made it possible to configure the default formats for both the `date` and the `number_format` filter
* added support for the new Asset component (from Twig bridge)
* deprecated the assets extension (use the one from the Twig bridge instead)
2.6.0
-----
* [BC BREAK] changed exception.json.twig to match same structure as error.json.twig making clients independent of runtime environment.
2.3.0
-----
* added option to configure a custom template escaping guesser (via `autoescape_service` and `autoescape_service_method`)
2.2.0
-----
* moved the exception controller to be a service (`twig.controller.exception:showAction` vs `Symfony\\Bundle\\TwigBundle\\Controller\\ExceptionController::showAction`)
* added support for multiple loaders via the "twig.loader" tag.
* added automatic registration of namespaced paths for registered bundles
* added support for namespaced paths
2.1.0
-----
* added a new setting ("paths") to configure more paths for the Twig filesystem loader
* added contextual escaping based on the template file name (disabled if you explicitly pass an autoescape option)
* added a command that extracts translation messages from templates
* added the real template name when an error occurs in a Twig template
* added the twig:lint command that will validate a Twig template syntax.

View File

@@ -0,0 +1,123 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\CacheWarmer;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\CacheWarmer\TemplateFinderInterface;
use Symfony\Component\Templating\TemplateReference;
use Twig\Error\Error;
/**
* Generates the Twig cache for all templates.
*
* This warmer must be registered after TemplatePathsCacheWarmer,
* as the Twig loader will need the cache generated by it.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TemplateCacheCacheWarmer implements CacheWarmerInterface
{
protected $container;
protected $finder;
private $paths;
/**
* Constructor.
*
* @param ContainerInterface $container The dependency injection container
* @param TemplateFinderInterface $finder The template paths cache warmer
* @param array $paths Additional twig paths to warm
*/
public function __construct(ContainerInterface $container, TemplateFinderInterface $finder = null, array $paths = array())
{
// We don't inject the Twig environment directly as it depends on the
// template locator (via the loader) which might be a cached one.
// The cached template locator is available once the TemplatePathsCacheWarmer
// has been warmed up.
// But it can also be null if templating has been disabled.
$this->container = $container;
$this->finder = $finder;
$this->paths = $paths;
}
/**
* Warms up the cache.
*
* @param string $cacheDir The cache directory
*/
public function warmUp($cacheDir)
{
if (null === $this->finder) {
return;
}
$twig = $this->container->get('twig');
$templates = $this->finder->findAllTemplates();
foreach ($this->paths as $path => $namespace) {
$templates = array_merge($templates, $this->findTemplatesInFolder($namespace, $path));
}
foreach ($templates as $template) {
if ('twig' !== $template->get('engine')) {
continue;
}
try {
$twig->loadTemplate($template);
} catch (Error $e) {
// problem during compilation, give up
}
}
}
/**
* Checks whether this warmer is optional or not.
*
* @return bool always true
*/
public function isOptional()
{
return true;
}
/**
* Find templates in the given directory.
*
* @param string $namespace The namespace for these templates
* @param string $dir The folder where to look for templates
*
* @return array An array of templates of type TemplateReferenceInterface
*/
private function findTemplatesInFolder($namespace, $dir)
{
if (!is_dir($dir)) {
return array();
}
$templates = array();
$finder = new Finder();
foreach ($finder->files()->followLinks()->in($dir) as $file) {
$name = $file->getRelativePathname();
$templates[] = new TemplateReference(
$namespace ? sprintf('@%s/%s', $namespace, $name) : $name,
'twig'
);
}
return $templates;
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\CacheWarmer;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
use Twig\Environment;
use Twig\Error\Error;
/**
* Generates the Twig cache for all templates.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TemplateCacheWarmer implements CacheWarmerInterface
{
private $container;
private $twig;
private $iterator;
/**
* TemplateCacheWarmer constructor.
*
* @param ContainerInterface|Environment $container
* @param \Traversable $iterator
*/
public function __construct($container, \Traversable $iterator)
{
// As this cache warmer is optional, dependencies should be lazy-loaded, that's why a container should be injected.
if ($container instanceof ContainerInterface) {
$this->container = $container;
} elseif ($container instanceof Environment) {
$this->twig = $container;
} else {
throw new \InvalidArgumentException(sprintf('%s only accepts instance of Symfony\Component\DependencyInjection\ContainerInterface or Environment as first argument.', __CLASS__));
}
$this->iterator = $iterator;
}
/**
* {@inheritdoc}
*/
public function warmUp($cacheDir)
{
if (null === $this->twig) {
$this->twig = $this->container->get('twig');
}
foreach ($this->iterator as $template) {
try {
$this->twig->loadTemplate($template);
} catch (Error $e) {
// problem during compilation, give up
// might be a syntax error or a non-Twig template
}
}
}
/**
* {@inheritdoc}
*/
public function isOptional()
{
return true;
}
}

View File

@@ -0,0 +1,34 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Command;
use Symfony\Bridge\Twig\Command\DebugCommand as BaseDebugCommand;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
/**
* Lists twig functions, filters, globals and tests present in the current project.
*
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
final class DebugCommand extends BaseDebugCommand implements ContainerAwareInterface
{
use ContainerAwareTrait;
/**
* {@inheritdoc}
*/
protected function getTwigEnvironment()
{
return $this->container->get('twig');
}
}

View File

@@ -0,0 +1,67 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Command;
use Symfony\Bridge\Twig\Command\LintCommand as BaseLintCommand;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\Finder\Finder;
/**
* Command that will validate your template syntax and output encountered errors.
*
* @author Marc Weistroff <marc.weistroff@sensiolabs.com>
* @author Jérôme Tamarelle <jerome@tamarelle.net>
*/
final class LintCommand extends BaseLintCommand implements ContainerAwareInterface
{
use ContainerAwareTrait;
/**
* {@inheritdoc}
*/
protected function getTwigEnvironment()
{
return $this->container->get('twig');
}
/**
* {@inheritdoc}
*/
protected function configure()
{
parent::configure();
$this
->setHelp(
$this->getHelp().<<<'EOF'
Or all template files in a bundle:
<info>php %command.full_name% @AcmeDemoBundle</info>
EOF
)
;
}
protected function findFiles($filename)
{
if (0 === strpos($filename, '@')) {
$dir = $this->getApplication()->getKernel()->locateResource($filename);
return Finder::create()->files()->in($dir)->name('*.twig');
}
return parent::findFiles($filename);
}
}

View File

@@ -0,0 +1,49 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Twig\RuntimeLoader\RuntimeLoaderInterface;
/**
* Loads Twig extension runtimes via the service container.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ContainerAwareRuntimeLoader implements RuntimeLoaderInterface
{
private $container;
private $mapping;
private $logger;
public function __construct(ContainerInterface $container, array $mapping, LoggerInterface $logger = null)
{
$this->container = $container;
$this->mapping = $mapping;
$this->logger = $logger;
}
/**
* {@inheritdoc}
*/
public function load($class)
{
if (isset($this->mapping[$class])) {
return $this->container->get($this->mapping[$class]);
}
if (null !== $this->logger) {
$this->logger->warning(sprintf('Class "%s" is not configured as a Twig runtime. Add the "twig.runtime" tag to the related service in the container.', $class));
}
}
}

View File

@@ -0,0 +1,148 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Controller;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Loader\ExistsLoaderInterface;
/**
* ExceptionController renders error or exception pages for a given
* FlattenException.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Matthias Pigulla <mp@webfactory.de>
*/
class ExceptionController
{
protected $twig;
/**
* @var bool Show error (false) or exception (true) pages by default
*/
protected $debug;
public function __construct(Environment $twig, $debug)
{
$this->twig = $twig;
$this->debug = $debug;
}
/**
* Converts an Exception to a Response.
*
* A "showException" request parameter can be used to force display of an error page (when set to false) or
* the exception page (when true). If it is not present, the "debug" value passed into the constructor will
* be used.
*
* @param Request $request The request
* @param FlattenException $exception A FlattenException instance
* @param DebugLoggerInterface $logger A DebugLoggerInterface instance
*
* @return Response
*
* @throws \InvalidArgumentException When the exception template does not exist
*/
public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
{
$currentContent = $this->getAndCleanOutputBuffering($request->headers->get('X-Php-Ob-Level', -1));
$showException = $request->attributes->get('showException', $this->debug); // As opposed to an additional parameter, this maintains BC
$code = $exception->getStatusCode();
return new Response($this->twig->render(
(string) $this->findTemplate($request, $request->getRequestFormat(), $code, $showException),
array(
'status_code' => $code,
'status_text' => isset(Response::$statusTexts[$code]) ? Response::$statusTexts[$code] : '',
'exception' => $exception,
'logger' => $logger,
'currentContent' => $currentContent,
)
), 200, array('Content-Type' => $request->getMimeType($request->getRequestFormat()) ?: 'text/html'));
}
/**
* @param int $startObLevel
*
* @return string
*/
protected function getAndCleanOutputBuffering($startObLevel)
{
if (ob_get_level() <= $startObLevel) {
return '';
}
Response::closeOutputBuffers($startObLevel + 1, true);
return ob_get_clean();
}
/**
* @param Request $request
* @param string $format
* @param int $code An HTTP response status code
* @param bool $showException
*
* @return string
*/
protected function findTemplate(Request $request, $format, $code, $showException)
{
$name = $showException ? 'exception' : 'error';
if ($showException && 'html' == $format) {
$name = 'exception_full';
}
// For error pages, try to find a template for the specific HTTP status code and format
if (!$showException) {
$template = sprintf('@Twig/Exception/%s%s.%s.twig', $name, $code, $format);
if ($this->templateExists($template)) {
return $template;
}
}
// try to find a template for the given format
$template = sprintf('@Twig/Exception/%s.%s.twig', $name, $format);
if ($this->templateExists($template)) {
return $template;
}
// default to a generic HTML exception
$request->setRequestFormat('html');
return sprintf('@Twig/Exception/%s.html.twig', $showException ? 'exception_full' : $name);
}
// to be removed when the minimum required version of Twig is >= 3.0
protected function templateExists($template)
{
$template = (string) $template;
$loader = $this->twig->getLoader();
if ($loader instanceof ExistsLoaderInterface || method_exists($loader, 'exists')) {
return $loader->exists($template);
}
try {
$loader->getSourceContext($template)->getCode();
return true;
} catch (LoaderError $e) {
}
return false;
}
}

View File

@@ -0,0 +1,56 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Controller;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* PreviewErrorController can be used to test error pages.
*
* It will create a test exception and forward it to another controller.
*
* @author Matthias Pigulla <mp@webfactory.de>
*/
class PreviewErrorController
{
protected $kernel;
protected $controller;
public function __construct(HttpKernelInterface $kernel, $controller)
{
$this->kernel = $kernel;
$this->controller = $controller;
}
public function previewErrorPageAction(Request $request, $code)
{
$exception = FlattenException::create(new \Exception('Something has intentionally gone wrong.'), $code);
/*
* This Request mimics the parameters set by
* \Symfony\Component\HttpKernel\EventListener\ExceptionListener::duplicateRequest, with
* the additional "showException" flag.
*/
$subRequest = $request->duplicate(null, null, array(
'_controller' => $this->controller,
'exception' => $exception,
'logger' => null,
'format' => $request->getRequestFormat(),
'showException' => false,
));
return $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Registers the Twig exception listener if Twig is registered as a templating engine.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class ExceptionListenerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('twig')) {
return;
}
// register the exception controller only if Twig is enabled and required dependencies do exist
if (!class_exists('Symfony\Component\Debug\Exception\FlattenException') || !interface_exists('Symfony\Component\EventDispatcher\EventSubscriberInterface')) {
$container->removeDefinition('twig.exception_listener');
} elseif ($container->hasParameter('templating.engines')) {
$engines = $container->getParameter('templating.engines');
if (!in_array('twig', $engines)) {
$container->removeDefinition('twig.exception_listener');
}
}
}
}

View File

@@ -0,0 +1,139 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\Config\Resource\ClassExistenceResource;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\Workflow\Workflow;
use Symfony\Component\Yaml\Parser as YamlParser;
/**
* @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com>
*/
class ExtensionPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!class_exists('Symfony\Component\Asset\Packages')) {
$container->removeDefinition('twig.extension.assets');
}
if (!class_exists('Symfony\Component\ExpressionLanguage\Expression')) {
$container->removeDefinition('twig.extension.expression');
}
if (!interface_exists('Symfony\Component\Routing\Generator\UrlGeneratorInterface')) {
$container->removeDefinition('twig.extension.routing');
}
if (!interface_exists('Symfony\Component\Translation\TranslatorInterface')) {
$container->removeDefinition('twig.extension.trans');
}
if (!class_exists('Symfony\Component\Yaml\Yaml')) {
$container->removeDefinition('twig.extension.yaml');
}
if ($container->has('form.extension')) {
$container->getDefinition('twig.extension.form')->addTag('twig.extension');
$reflClass = new \ReflectionClass('Symfony\Bridge\Twig\Extension\FormExtension');
$container->getDefinition('twig.loader.native_filesystem')->addMethodCall('addPath', array(dirname(dirname($reflClass->getFileName())).'/Resources/views/Form'));
}
if ($container->has('translator')) {
$container->getDefinition('twig.extension.trans')->addTag('twig.extension');
}
if ($container->has('router')) {
$container->getDefinition('twig.extension.routing')->addTag('twig.extension');
}
if ($container->has('fragment.handler')) {
$container->getDefinition('twig.extension.httpkernel')->addTag('twig.extension');
// inject Twig in the hinclude service if Twig is the only registered templating engine
if ((!$container->hasParameter('templating.engines') || array('twig') == $container->getParameter('templating.engines')) && $container->hasDefinition('fragment.renderer.hinclude')) {
$container->getDefinition('fragment.renderer.hinclude')
->addTag('kernel.fragment_renderer', array('alias' => 'hinclude'))
->replaceArgument(0, new Reference('twig'))
;
}
}
if ($container->has('request_stack')) {
$container->getDefinition('twig.extension.httpfoundation')->addTag('twig.extension');
}
if ($container->getParameter('kernel.debug')) {
$container->getDefinition('twig.extension.profiler')->addTag('twig.extension');
$container->getDefinition('twig.extension.debug')->addTag('twig.extension');
}
$composerRootDir = $this->getComposerRootDir($container->getParameter('kernel.root_dir'));
$twigLoader = $container->getDefinition('twig.loader.native_filesystem');
if ($container->has('templating')) {
$loader = $container->getDefinition('twig.loader.filesystem');
$loader->setMethodCalls(array_merge($twigLoader->getMethodCalls(), $loader->getMethodCalls()));
$loader->replaceArgument(2, $composerRootDir);
$twigLoader->clearTag('twig.loader');
} else {
$twigLoader->replaceArgument(1, $composerRootDir);
$container->setAlias('twig.loader.filesystem', new Alias('twig.loader.native_filesystem', false));
$container->removeDefinition('templating.engine.twig');
}
if ($container->has('assets.packages')) {
$container->getDefinition('twig.extension.assets')->addTag('twig.extension');
}
$container->addResource(new ClassExistenceResource(YamlParser::class));
if (class_exists(YamlParser::class)) {
$container->getDefinition('twig.extension.yaml')->addTag('twig.extension');
}
$container->addResource(new ClassExistenceResource(Stopwatch::class));
if (class_exists(Stopwatch::class)) {
$container->getDefinition('twig.extension.debug.stopwatch')->addTag('twig.extension');
}
$container->addResource(new ClassExistenceResource(ExpressionLanguage::class));
if (class_exists(ExpressionLanguage::class)) {
$container->getDefinition('twig.extension.expression')->addTag('twig.extension');
}
$container->addResource(new ClassExistenceResource(Workflow::class));
if (!class_exists(Workflow::class) || !$container->has('workflow.registry')) {
$container->removeDefinition('workflow.twig_extension');
} else {
$container->getDefinition('workflow.twig_extension')->addTag('twig.extension');
}
}
private function getComposerRootDir($rootDir)
{
$dir = $rootDir;
while (!file_exists($dir.'/composer.json')) {
if ($dir === dirname($dir)) {
return $rootDir;
}
$dir = dirname($dir);
}
return $dir;
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
/**
* Registers Twig runtime services.
*/
class RuntimeLoaderPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (!$container->hasDefinition('twig.runtime_loader')) {
return;
}
$definition = $container->getDefinition('twig.runtime_loader');
$mapping = array();
foreach ($container->findTaggedServiceIds('twig.runtime') as $id => $attributes) {
$def = $container->getDefinition($id);
if (!$def->isPublic()) {
throw new InvalidArgumentException(sprintf('The service "%s" must be public as it can be lazy-loaded.', $id));
}
if ($def->isAbstract()) {
throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as it can be lazy-loaded.', $id));
}
$mapping[$def->getClass()] = $id;
}
$definition->replaceArgument(1, $mapping);
}
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
/**
* Adds tagged twig.extension services to twig service.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TwigEnvironmentPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('twig')) {
return;
}
$definition = $container->getDefinition('twig');
// Extensions must always be registered before everything else.
// For instance, global variable definitions must be registered
// afterward. If not, the globals from the extensions will never
// be registered.
$calls = $definition->getMethodCalls();
$definition->setMethodCalls(array());
foreach ($container->findTaggedServiceIds('twig.extension') as $id => $attributes) {
$definition->addMethodCall('addExtension', array(new Reference($id)));
}
$definition->setMethodCalls(array_merge($definition->getMethodCalls(), $calls));
}
}

View File

@@ -0,0 +1,60 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Exception\LogicException;
/**
* Adds services tagged twig.loader as Twig loaders.
*
* @author Daniel Leech <daniel@dantleech.com>
*/
class TwigLoaderPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
if (false === $container->hasDefinition('twig')) {
return;
}
$prioritizedLoaders = array();
$found = 0;
foreach ($container->findTaggedServiceIds('twig.loader') as $id => $attributes) {
$priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
$prioritizedLoaders[$priority][] = $id;
++$found;
}
if (!$found) {
throw new LogicException('No twig loaders found. You need to tag at least one loader with "twig.loader"');
}
if (1 === $found) {
$container->setAlias('twig.loader', $id);
} else {
$chainLoader = $container->getDefinition('twig.loader.chain');
krsort($prioritizedLoaders);
foreach ($prioritizedLoaders as $loaders) {
foreach ($loaders as $loader) {
$chainLoader->addMethodCall('addLoader', array(new Reference($loader)));
}
}
$container->setAlias('twig.loader', 'twig.loader.chain');
}
}
}

View File

@@ -0,0 +1,193 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
/**
* TwigExtension configuration structure.
*
* @author Jeremy Mikola <jmikola@gmail.com>
*/
class Configuration implements ConfigurationInterface
{
/**
* Generates the configuration tree builder.
*
* @return TreeBuilder The tree builder
*/
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('twig');
$rootNode
->children()
->scalarNode('exception_controller')->defaultValue('twig.controller.exception:showAction')->end()
->end()
;
$this->addFormThemesSection($rootNode);
$this->addGlobalsSection($rootNode);
$this->addTwigOptions($rootNode);
$this->addTwigFormatOptions($rootNode);
return $treeBuilder;
}
private function addFormThemesSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->fixXmlConfig('form_theme')
->children()
->arrayNode('form_themes')
->addDefaultChildrenIfNoneSet()
->prototype('scalar')->defaultValue('form_div_layout.html.twig')->end()
->example(array('MyBundle::form.html.twig'))
->validate()
->ifTrue(function ($v) { return !in_array('form_div_layout.html.twig', $v); })
->then(function ($v) {
return array_merge(array('form_div_layout.html.twig'), $v);
})
->end()
->end()
->end()
;
}
private function addGlobalsSection(ArrayNodeDefinition $rootNode)
{
$rootNode
->fixXmlConfig('global')
->children()
->arrayNode('globals')
->normalizeKeys(false)
->useAttributeAsKey('key')
->example(array('foo' => '"@bar"', 'pi' => 3.14))
->prototype('array')
->beforeNormalization()
->ifTrue(function ($v) { return is_string($v) && 0 === strpos($v, '@'); })
->then(function ($v) {
if (0 === strpos($v, '@@')) {
return substr($v, 1);
}
return array('id' => substr($v, 1), 'type' => 'service');
})
->end()
->beforeNormalization()
->ifTrue(function ($v) {
if (is_array($v)) {
$keys = array_keys($v);
sort($keys);
return $keys !== array('id', 'type') && $keys !== array('value');
}
return true;
})
->then(function ($v) { return array('value' => $v); })
->end()
->children()
->scalarNode('id')->end()
->scalarNode('type')
->validate()
->ifNotInArray(array('service'))
->thenInvalid('The %s type is not supported')
->end()
->end()
->variableNode('value')->end()
->end()
->end()
->end()
->end()
;
}
private function addTwigOptions(ArrayNodeDefinition $rootNode)
{
$rootNode
->fixXmlConfig('path')
->children()
->variableNode('autoescape')->defaultValue('name')->end()
->scalarNode('autoescape_service')->defaultNull()->end()
->scalarNode('autoescape_service_method')->defaultNull()->end()
->scalarNode('base_template_class')->example('Twig\Template')->cannotBeEmpty()->end()
->scalarNode('cache')->defaultValue('%kernel.cache_dir%/twig')->end()
->scalarNode('charset')->defaultValue('%kernel.charset%')->end()
->booleanNode('debug')->defaultValue('%kernel.debug%')->end()
->booleanNode('strict_variables')->end()
->scalarNode('auto_reload')->end()
->integerNode('optimizations')->min(-1)->end()
->arrayNode('paths')
->normalizeKeys(false)
->useAttributeAsKey('paths')
->beforeNormalization()
->always()
->then(function ($paths) {
$normalized = array();
foreach ($paths as $path => $namespace) {
if (is_array($namespace)) {
// xml
$path = $namespace['value'];
$namespace = $namespace['namespace'];
}
// path within the default namespace
if (ctype_digit((string) $path)) {
$path = $namespace;
$namespace = null;
}
$normalized[$path] = $namespace;
}
return $normalized;
})
->end()
->prototype('variable')->end()
->end()
->end()
;
}
private function addTwigFormatOptions(ArrayNodeDefinition $rootNode)
{
$rootNode
->children()
->arrayNode('date')
->info('The default format options used by the date filter')
->addDefaultsIfNotSet()
->children()
->scalarNode('format')->defaultValue('F j, Y H:i')->end()
->scalarNode('interval_format')->defaultValue('%d days')->end()
->scalarNode('timezone')
->info('The timezone used when formatting dates, when set to null, the timezone returned by date_default_timezone_get() is used')
->defaultNull()
->end()
->end()
->end()
->arrayNode('number_format')
->info('The default format options for the number_format filter')
->addDefaultsIfNotSet()
->children()
->integerNode('decimals')->defaultValue(0)->end()
->scalarNode('decimal_point')->defaultValue('.')->end()
->scalarNode('thousands_separator')->defaultValue(',')->end()
->end()
->end()
->end()
;
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Configurator;
use Twig\Environment;
// BC/FC with namespaced Twig
class_exists('Twig\Environment');
/**
* Twig environment configurator.
*
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
*/
class EnvironmentConfigurator
{
private $dateFormat;
private $intervalFormat;
private $timezone;
private $decimals;
private $decimalPoint;
private $thousandsSeparator;
public function __construct($dateFormat, $intervalFormat, $timezone, $decimals, $decimalPoint, $thousandsSeparator)
{
$this->dateFormat = $dateFormat;
$this->intervalFormat = $intervalFormat;
$this->timezone = $timezone;
$this->decimals = $decimals;
$this->decimalPoint = $decimalPoint;
$this->thousandsSeparator = $thousandsSeparator;
}
public function configure(Environment $environment)
{
$environment->getExtension('Twig\Extension\CoreExtension')->setDateFormat($this->dateFormat, $this->intervalFormat);
if (null !== $this->timezone) {
$environment->getExtension('Twig\Extension\CoreExtension')->setTimezone($this->timezone);
}
$environment->getExtension('Twig\Extension\CoreExtension')->setNumberFormat($this->decimals, $this->decimalPoint, $this->thousandsSeparator);
}
}

View File

@@ -0,0 +1,227 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\DependencyInjection;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Resource\FileExistenceResource;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
/**
* TwigExtension.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jeremy Mikola <jmikola@gmail.com>
*/
class TwigExtension extends Extension
{
/**
* Responds to the twig configuration parameter.
*
* @param array $configs
* @param ContainerBuilder $container
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('twig.xml');
if (class_exists('Symfony\Component\Form\Form')) {
$loader->load('form.xml');
}
if (interface_exists('Symfony\Component\Templating\EngineInterface')) {
$loader->load('templating.xml');
}
if (!interface_exists('Symfony\Component\Translation\TranslatorInterface')) {
$container->removeDefinition('twig.translation.extractor');
}
foreach ($configs as $key => $config) {
if (isset($config['globals'])) {
foreach ($config['globals'] as $name => $value) {
if (is_array($value) && isset($value['key'])) {
$configs[$key]['globals'][$name] = array(
'key' => $name,
'value' => $value,
);
}
}
}
}
$configuration = $this->getConfiguration($configs, $container);
$config = $this->processConfiguration($configuration, $configs);
$container->setParameter('twig.exception_listener.controller', $config['exception_controller']);
$container->setParameter('twig.form.resources', $config['form_themes']);
$envConfiguratorDefinition = $container->getDefinition('twig.configurator.environment');
$envConfiguratorDefinition->replaceArgument(0, $config['date']['format']);
$envConfiguratorDefinition->replaceArgument(1, $config['date']['interval_format']);
$envConfiguratorDefinition->replaceArgument(2, $config['date']['timezone']);
$envConfiguratorDefinition->replaceArgument(3, $config['number_format']['decimals']);
$envConfiguratorDefinition->replaceArgument(4, $config['number_format']['decimal_point']);
$envConfiguratorDefinition->replaceArgument(5, $config['number_format']['thousands_separator']);
$twigFilesystemLoaderDefinition = $container->getDefinition('twig.loader.native_filesystem');
// register user-configured paths
foreach ($config['paths'] as $path => $namespace) {
if (!$namespace) {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path));
} else {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace));
}
}
$container->getDefinition('twig.cache_warmer')->replaceArgument(2, $config['paths']);
$container->getDefinition('twig.template_iterator')->replaceArgument(2, $config['paths']);
$bundleHierarchy = $this->getBundleHierarchy($container);
foreach ($bundleHierarchy as $name => $bundle) {
$namespace = $this->normalizeBundleName($name);
foreach ($bundle['children'] as $child) {
foreach ($bundleHierarchy[$child]['paths'] as $path) {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace));
}
}
foreach ($bundle['paths'] as $path) {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($path, $namespace));
}
}
if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/views')) {
$twigFilesystemLoaderDefinition->addMethodCall('addPath', array($dir));
}
$container->addResource(new FileExistenceResource($dir));
if (!empty($config['globals'])) {
$def = $container->getDefinition('twig');
foreach ($config['globals'] as $key => $global) {
if (isset($global['type']) && 'service' === $global['type']) {
$def->addMethodCall('addGlobal', array($key, new Reference($global['id'])));
} else {
$def->addMethodCall('addGlobal', array($key, $global['value']));
}
}
}
unset(
$config['form'],
$config['globals'],
$config['extensions']
);
if (isset($config['autoescape_service']) && isset($config['autoescape_service_method'])) {
$config['autoescape'] = array(new Reference($config['autoescape_service']), $config['autoescape_service_method']);
}
unset($config['autoescape_service'], $config['autoescape_service_method']);
$container->getDefinition('twig')->replaceArgument(1, $config);
$this->addClassesToCompile(array(
'Twig_Environment',
'Twig_Extension',
'Twig_Extension_Core',
'Twig_Extension_Escaper',
'Twig_Extension_Optimizer',
'Twig_LoaderInterface',
'Twig_Markup',
'Twig_Template',
));
}
private function getBundleHierarchy(ContainerBuilder $container)
{
$bundleHierarchy = array();
foreach ($container->getParameter('kernel.bundles_metadata') as $name => $bundle) {
if (!array_key_exists($name, $bundleHierarchy)) {
$bundleHierarchy[$name] = array(
'paths' => array(),
'parents' => array(),
'children' => array(),
);
}
if (is_dir($dir = $container->getParameter('kernel.root_dir').'/Resources/'.$name.'/views')) {
$bundleHierarchy[$name]['paths'][] = $dir;
}
$container->addResource(new FileExistenceResource($dir));
if (is_dir($dir = $bundle['path'].'/Resources/views')) {
$bundleHierarchy[$name]['paths'][] = $dir;
}
$container->addResource(new FileExistenceResource($dir));
if (null === $bundle['parent']) {
continue;
}
$bundleHierarchy[$name]['parents'][] = $bundle['parent'];
if (!array_key_exists($bundle['parent'], $bundleHierarchy)) {
$bundleHierarchy[$bundle['parent']] = array(
'paths' => array(),
'parents' => array(),
'children' => array(),
);
}
$bundleHierarchy[$bundle['parent']]['children'] = array_merge($bundleHierarchy[$name]['children'], array($name), $bundleHierarchy[$bundle['parent']]['children']);
foreach ($bundleHierarchy[$bundle['parent']]['parents'] as $parent) {
$bundleHierarchy[$name]['parents'][] = $parent;
$bundleHierarchy[$parent]['children'] = array_merge($bundleHierarchy[$name]['children'], array($name), $bundleHierarchy[$parent]['children']);
}
foreach ($bundleHierarchy[$name]['children'] as $child) {
$bundleHierarchy[$child]['parents'] = array_merge($bundleHierarchy[$child]['parents'], $bundleHierarchy[$name]['parents']);
}
}
return $bundleHierarchy;
}
private function normalizeBundleName($name)
{
if ('Bundle' === substr($name, -6)) {
$name = substr($name, 0, -6);
}
return $name;
}
/**
* Returns the base path for the XSD files.
*
* @return string The XSD base path
*/
public function getXsdValidationBasePath()
{
return __DIR__.'/../Resources/config/schema';
}
public function getNamespace()
{
return 'http://symfony.com/schema/dic/twig';
}
}

19
vendor/symfony/twig-bundle/LICENSE vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2004-2017 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,102 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Loader;
use Symfony\Component\Config\FileLocatorInterface;
use Symfony\Component\Templating\TemplateNameParserInterface;
use Symfony\Component\Templating\TemplateReferenceInterface;
use Twig\Error\LoaderError;
use Twig\Loader\FilesystemLoader as BaseFilesystemLoader;
/**
* FilesystemLoader extends the default Twig filesystem loader
* to work with the Symfony paths and template references.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FilesystemLoader extends BaseFilesystemLoader
{
protected $locator;
protected $parser;
/**
* Constructor.
*
* @param FileLocatorInterface $locator A FileLocatorInterface instance
* @param TemplateNameParserInterface $parser A TemplateNameParserInterface instance
* @param string|null $rootPath The root path common to all relative paths (null for getcwd())
*/
public function __construct(FileLocatorInterface $locator, TemplateNameParserInterface $parser, $rootPath = null)
{
parent::__construct(array(), $rootPath);
$this->locator = $locator;
$this->parser = $parser;
}
/**
* {@inheritdoc}
*
* The name parameter might also be a TemplateReferenceInterface.
*/
public function exists($name)
{
return parent::exists((string) $name);
}
/**
* Returns the path to the template file.
*
* The file locator is used to locate the template when the naming convention
* is the symfony one (i.e. the name can be parsed).
* Otherwise the template is located using the locator from the twig library.
*
* @param string|TemplateReferenceInterface $template The template
* @param bool $throw When true, a LoaderError exception will be thrown if a template could not be found
*
* @return string The path to the template file
*
* @throws LoaderError if the template could not be found
*/
protected function findTemplate($template, $throw = true)
{
$logicalName = (string) $template;
if (isset($this->cache[$logicalName])) {
return $this->cache[$logicalName];
}
$file = null;
try {
$file = parent::findTemplate($logicalName);
} catch (LoaderError $e) {
$twigLoaderException = $e;
// for BC
try {
$template = $this->parser->parse($template);
$file = $this->locator->locate($template);
} catch (\Exception $e) {
}
}
if (false === $file || null === $file) {
if ($throw) {
throw $twigLoaderException;
}
return false;
}
return $this->cache[$logicalName] = $file;
}
}

10
vendor/symfony/twig-bundle/README.md vendored Normal file
View File

@@ -0,0 +1,10 @@
TwigBundle
==========
Resources
---------
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
* [Report issues](https://github.com/symfony/symfony/issues) and
[send Pull Requests](https://github.com/symfony/symfony/pulls)
in the [main Symfony repository](https://github.com/symfony/symfony)

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="twig.extension.form" class="Symfony\Bridge\Twig\Extension\FormExtension" public="false">
<argument type="collection">
<argument type="service" id="service_container" />
<argument>twig.form.renderer</argument>
</argument>
</service>
<service id="twig.form.engine" class="Symfony\Bridge\Twig\Form\TwigRendererEngine" public="false">
<argument>%twig.form.resources%</argument>
<argument type="service" id="twig" />
</service>
<service id="twig.form.renderer" class="Symfony\Bridge\Twig\Form\TwigRenderer">
<argument type="service" id="twig.form.engine" />
<argument type="service" id="security.csrf.token_manager" on-invalid="null" />
<tag name="twig.runtime" />
</service>
</services>
</container>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<routes xmlns="http://symfony.com/schema/routing"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
<route id="_twig_error_test" path="/{code}.{_format}">
<default key="_controller">twig.controller.preview_error:previewErrorPageAction</default>
<default key="_format">html</default>
<requirement key="code">\d+</requirement>
</route>
</routes>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xsd:schema xmlns="http://symfony.com/schema/dic/twig"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://symfony.com/schema/dic/twig"
elementFormDefault="qualified">
<xsd:element name="config" type="config" />
<xsd:complexType name="config">
<xsd:sequence>
<xsd:element name="date" type="date" minOccurs="0" maxOccurs="1" />
<xsd:element name="number-format" type="number_format" minOccurs="0" maxOccurs="1" />
<xsd:element name="form-theme" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="global" type="global" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="path" type="path" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="auto-reload" type="xsd:string" />
<xsd:attribute name="autoescape" type="xsd:string" />
<xsd:attribute name="autoescape-service" type="xsd:string" />
<xsd:attribute name="autoescape-service-method" type="xsd:string" />
<xsd:attribute name="base-template-class" type="xsd:string" />
<xsd:attribute name="cache" type="xsd:string" />
<xsd:attribute name="charset" type="xsd:string" />
<xsd:attribute name="debug" type="xsd:string" />
<xsd:attribute name="strict-variables" type="xsd:string" />
<xsd:attribute name="exception-controller" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="date">
<xsd:attribute name="format" type="xsd:string" />
<xsd:attribute name="interval-format" type="xsd:string" />
<xsd:attribute name="timezone" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="number_format">
<xsd:attribute name="decimals" type="xsd:integer" />
<xsd:attribute name="decimal-point" type="xsd:string" />
<xsd:attribute name="thousands-separator" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="path" mixed="true">
<xsd:attribute name="namespace" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="global" mixed="true">
<xsd:attribute name="key" type="xsd:string" use="required" />
<xsd:attribute name="type" type="global_type" />
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:simpleType name="global_type">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="service" />
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="twig.loader.filesystem" class="Symfony\Bundle\TwigBundle\Loader\FilesystemLoader" public="false">
<argument type="service" id="templating.locator" />
<argument type="service" id="templating.name_parser" />
<argument /> <!-- project's root dir -->
<tag name="twig.loader"/>
</service>
<service id="templating.engine.twig" class="Symfony\Bundle\TwigBundle\TwigEngine" public="false">
<argument type="service" id="twig" />
<argument type="service" id="templating.name_parser" />
<argument type="service" id="templating.locator" />
</service>
</services>
</container>

View File

@@ -0,0 +1,150 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="twig" class="Twig\Environment">
<argument type="service" id="twig.loader" />
<argument /> <!-- Twig options -->
<call method="addGlobal">
<argument>app</argument>
<argument type="service" id="twig.app_variable" />
</call>
<call method="addRuntimeLoader">
<argument type="service" id="twig.runtime_loader" />
</call>
<configurator service="twig.configurator.environment" method="configure" />
</service>
<service id="twig.app_variable" class="Symfony\Bridge\Twig\AppVariable" public="false">
<call method="setEnvironment"><argument>%kernel.environment%</argument></call>
<call method="setDebug"><argument>%kernel.debug%</argument></call>
<call method="setTokenStorage"><argument type="service" id="security.token_storage" on-invalid="ignore" /></call>
<call method="setRequestStack"><argument type="service" id="request_stack" on-invalid="ignore" /></call>
</service>
<service id="twig.cache_warmer" class="Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheCacheWarmer" public="false">
<tag name="kernel.cache_warmer" />
<argument type="service" id="service_container" />
<argument type="service" id="templating.finder" on-invalid="ignore" />
<argument type="collection" /> <!-- Twig paths -->
</service>
<service id="twig.template_iterator" class="Symfony\Bundle\TwigBundle\TemplateIterator" public="false">
<argument type="service" id="kernel" />
<argument>%kernel.root_dir%</argument>
<argument type="collection" /> <!-- Twig paths -->
</service>
<service id="twig.template_cache_warmer" class="Symfony\Bundle\TwigBundle\CacheWarmer\TemplateCacheWarmer" public="false">
<tag name="kernel.cache_warmer" />
<argument type="service" id="service_container" />
<argument type="service" id="twig.template_iterator" />
</service>
<service id="twig.loader.native_filesystem" class="Twig\Loader\FilesystemLoader" public="false">
<argument type="collection" /> <!-- paths -->
<argument /> <!-- project's root dir -->
<tag name="twig.loader"/>
</service>
<service id="twig.loader.chain" class="Twig\Loader\ChainLoader" public="false"/>
<service id="twig.extension.profiler" class="Symfony\Bridge\Twig\Extension\ProfilerExtension" public="false">
<argument type="service" id="twig.profile" />
<argument type="service" id="debug.stopwatch" on-invalid="null" />
</service>
<service id="twig.profile" class="Twig\Profiler\Profile" />
<service id="data_collector.twig" class="Symfony\Bridge\Twig\DataCollector\TwigDataCollector" public="false">
<tag name="data_collector" template="@WebProfiler/Collector/twig.html.twig" id="twig" priority="257" />
<argument type="service" id="twig.profile" />
</service>
<service id="twig.extension.trans" class="Symfony\Bridge\Twig\Extension\TranslationExtension" public="false">
<argument type="service" id="translator" />
</service>
<service id="twig.extension.assets" class="Symfony\Bridge\Twig\Extension\AssetExtension" public="false">
<argument type="service" id="assets.packages" />
</service>
<service id="twig.extension.code" class="Symfony\Bridge\Twig\Extension\CodeExtension" public="false">
<tag name="twig.extension" />
<argument type="service" id="debug.file_link_formatter" on-invalid="ignore" />
<argument>%kernel.root_dir%</argument>
<argument>%kernel.charset%</argument>
</service>
<service id="twig.extension.routing" class="Symfony\Bridge\Twig\Extension\RoutingExtension" public="false">
<argument type="service" id="router" />
</service>
<service id="twig.extension.yaml" class="Symfony\Bridge\Twig\Extension\YamlExtension" public="false" />
<service id="twig.extension.debug.stopwatch" class="Symfony\Bridge\Twig\Extension\StopwatchExtension" public="false">
<argument type="service" id="debug.stopwatch" on-invalid="ignore" />
<argument>%kernel.debug%</argument>
</service>
<service id="twig.extension.expression" class="Symfony\Bridge\Twig\Extension\ExpressionExtension" public="false" />
<service id="twig.extension.httpkernel" class="Symfony\Bridge\Twig\Extension\HttpKernelExtension" public="false" />
<service id="twig.runtime.httpkernel" class="Symfony\Bridge\Twig\Extension\HttpKernelRuntime">
<argument type="service" id="fragment.handler" />
<tag name="twig.runtime" />
</service>
<service id="twig.extension.httpfoundation" class="Symfony\Bridge\Twig\Extension\HttpFoundationExtension" public="false">
<argument type="service" id="request_stack" />
<argument type="service" id="router.request_context" on-invalid="ignore" />
</service>
<service id="twig.extension.debug" class="Twig\Extension\DebugExtension" public="false" />
<service id="workflow.twig_extension" class="Symfony\Bridge\Twig\Extension\WorkflowExtension">
<argument type="service" id="workflow.registry" />
</service>
<service id="twig.translation.extractor" class="Symfony\Bridge\Twig\Translation\TwigExtractor">
<argument type="service" id="twig" />
<tag name="translation.extractor" alias="twig" />
</service>
<service id="twig.exception_listener" class="Symfony\Component\HttpKernel\EventListener\ExceptionListener">
<tag name="kernel.event_subscriber" />
<tag name="monolog.logger" channel="request" />
<argument>%twig.exception_listener.controller%</argument>
<argument type="service" id="logger" on-invalid="null" />
</service>
<service id="twig.controller.exception" class="Symfony\Bundle\TwigBundle\Controller\ExceptionController">
<argument type="service" id="twig" />
<argument>%kernel.debug%</argument>
</service>
<service id="twig.controller.preview_error" class="Symfony\Bundle\TwigBundle\Controller\PreviewErrorController">
<argument type="service" id="http_kernel" />
<argument>%twig.exception_listener.controller%</argument>
</service>
<service id="twig.configurator.environment" class="Symfony\Bundle\TwigBundle\DependencyInjection\Configurator\EnvironmentConfigurator" public="false">
<argument /> <!-- date format, set in TwigExtension -->
<argument /> <!-- interval format, set in TwigExtension -->
<argument /> <!-- timezone, set in TwigExtension -->
<argument /> <!-- decimals, set in TwigExtension -->
<argument /> <!-- decimal point, set in TwigExtension -->
<argument /> <!-- thousands separator, set in TwigExtension -->
</service>
<service id="twig.runtime_loader" class="Symfony\Bundle\TwigBundle\ContainerAwareRuntimeLoader" public="false">
<argument type="service" id="service_container" />
<argument type="collection" /> <!-- the mapping between class names and service names -->
<argument type="service" id="logger" on-invalid="null" />
</service>
</services>
</container>

View File

@@ -0,0 +1 @@
{% include '@Twig/Exception/error.xml.twig' %}

View File

@@ -0,0 +1,4 @@
/*
{{ status_code }} {{ status_text }}
*/

View File

@@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="{{ _charset }}" />
<title>An Error Occurred: {{ status_text }}</title>
</head>
<body>
<h1>Oops! An Error Occurred</h1>
<h2>The server returned a "{{ status_code }} {{ status_text }}".</h2>
<div>
Something is broken. Please let us know what you were doing when this error occurred.
We will fix it as soon as possible. Sorry for any inconvenience caused.
</div>
</body>
</html>

View File

@@ -0,0 +1,4 @@
/*
{{ status_code }} {{ status_text }}
*/

View File

@@ -0,0 +1 @@
{{ { 'error': { 'code': status_code, 'message': status_text } }|json_encode|raw }}

View File

@@ -0,0 +1 @@
{% include '@Twig/Exception/error.xml.twig' %}

View File

@@ -0,0 +1,7 @@
Oops! An Error Occurred
=======================
The server returned a "{{ status_code }} {{ status_text }}".
Something is broken. Please let us know what you were doing when this error occurred.
We will fix it as soon as possible. Sorry for any inconvenience caused.

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="{{ _charset }}" ?>
<error code="{{ status_code }}" message="{{ status_text }}" />

View File

@@ -0,0 +1 @@
{% include '@Twig/Exception/exception.xml.twig' with { 'exception': exception } %}

View File

@@ -0,0 +1,3 @@
/*
{% include '@Twig/Exception/exception.txt.twig' with { 'exception': exception } %}
*/

View File

@@ -0,0 +1,120 @@
<div class="block-exception">
<div class="block-exception-detected clear-fix">
<div class="support">
<a href="http://symfony.com/support">Need support?</a>
</div>
<div class="illustration-exception">
{{ include('@Twig/Exception/exception.svg') }}
</div>
<div class="text-exception">
<div class="open-quote">“</div>
<h1>{{ exception.message|nl2br|format_file_from_text }}</h1>
<div>
<strong>{{ status_code }}</strong> {{ status_text }} - {{ exception.class|abbr_class }}
</div>
{% set previous_count = exception.allPrevious|length %}
{% if previous_count %}
<div class="linked"><span><strong>{{ previous_count }}</strong> linked Exception{{ previous_count > 1 ? 's' : '' }}:</span>
<ul>
{% for i, previous in exception.allPrevious %}
<li>
{{ previous.class|abbr_class }} <a href="#traces-link-{{ i + 1 }}" onclick="toggle('traces-{{ i + 1 }}', 'traces'); switchIcons('icon-traces-{{ i + 1 }}-open', 'icon-traces-{{ i + 1 }}-close');">&#187;</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
<div class="close-quote">”</div>
</div>
</div>
</div>
{% for position, e in exception.toarray %}
{% include '@Twig/Exception/traces.html.twig' with { 'exception': e, 'position': position, 'count': previous_count } only %}
{% endfor %}
{% if logger %}
<div class="block">
<div class="logs clear-fix">
{% spaceless %}
<h2>
Logs&nbsp;
<a href="#" onclick="toggle('logs'); switchIcons('icon-logs-open', 'icon-logs-close'); return false;">
<img class="toggle" id="icon-logs-open" alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: none" />
<img class="toggle" id="icon-logs-close" alt="-" src="data:image/gif;base64,R0lGODlhEgASAMQSANft94TG57Hb8GS44ez1+mC24IvK6ePx+Wa44dXs92+942e54o3L6W2844/M6dnu+P/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABIALAAAAAASABIAQAVCoCQBTBOd6Kk4gJhGBCTPxysJb44K0qD/ER/wlxjmisZkMqBEBW5NHrMZmVKvv9hMVsO+hE0EoNAstEYGxG9heIhCADs=" style="display: inline" />
</a>
</h2>
{% endspaceless %}
{% if logger.counterrors %}
<div class="error-count">
<span>
{{ logger.counterrors }} error{{ logger.counterrors > 1 ? 's' : ''}}
</span>
</div>
{% endif %}
</div>
<div id="logs">
{% include '@Twig/Exception/logs.html.twig' with { 'logs': logger.logs } only %}
</div>
</div>
{% endif %}
{% if currentContent %}
<div class="block">
{% spaceless %}
<h2>
Content of the Output&nbsp;
<a href="#" onclick="toggle('output-content'); switchIcons('icon-content-open', 'icon-content-close'); return false;">
<img class="toggle" id="icon-content-close" alt="-" src="data:image/gif;base64,R0lGODlhEgASAMQSANft94TG57Hb8GS44ez1+mC24IvK6ePx+Wa44dXs92+942e54o3L6W2844/M6dnu+P/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABIALAAAAAASABIAQAVCoCQBTBOd6Kk4gJhGBCTPxysJb44K0qD/ER/wlxjmisZkMqBEBW5NHrMZmVKvv9hMVsO+hE0EoNAstEYGxG9heIhCADs=" style="display: none" />
<img class="toggle" id="icon-content-open" alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: inline" />
</a>
</h2>
{% endspaceless %}
<div id="output-content" style="display: none">
{{ currentContent }}
</div>
<div style="clear: both"></div>
</div>
{% endif %}
{% include '@Twig/Exception/traces_text.html.twig' with { 'exception': exception } only %}
<script type="text/javascript">//<![CDATA[
function toggle(id, clazz) {
var el = document.getElementById(id),
current = el.style.display,
i;
if (clazz) {
var tags = document.getElementsByTagName('*');
for (i = tags.length - 1; i >= 0; i--) {
if (tags[i].className === clazz) {
tags[i].style.display = 'none';
}
}
}
el.style.display = current === 'none' ? 'block' : 'none';
}
function switchIcons(id1, id2) {
var icon1, icon2, display1, display2;
icon1 = document.getElementById(id1);
icon2 = document.getElementById(id2);
display1 = icon1.style.display;
display2 = icon2.style.display;
icon1.style.display = display2;
icon2.style.display = display1;
}
//]]></script>

View File

@@ -0,0 +1,3 @@
/*
{% include '@Twig/Exception/exception.txt.twig' with { 'exception': exception } %}
*/

View File

@@ -0,0 +1 @@
{{ { 'error': { 'code': status_code, 'message': status_text, 'exception': exception.toarray } }|json_encode|raw }}

View File

@@ -0,0 +1 @@
{% include '@Twig/Exception/exception.xml.twig' with { 'exception': exception } %}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -0,0 +1,7 @@
[exception] {{ status_code ~ ' | ' ~ status_text ~ ' | ' ~ exception.class }}
[message] {{ exception.message }}
{% for i, e in exception.toarray %}
[{{ i + 1 }}] {{ e.class }}: {{ e.message }}
{% include '@Twig/Exception/traces.txt.twig' with { 'exception': e } only %}
{% endfor %}

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="{{ _charset }}" ?>
<error code="{{ status_code }}" message="{{ status_text }}">
{% for e in exception.toarray %}
<exception class="{{ e.class }}" message="{{ e.message }}">
{% include '@Twig/Exception/traces.xml.twig' with { 'exception': e } only %}
</exception>
{% endfor %}
</error>

View File

@@ -0,0 +1,142 @@
{% extends '@Twig/layout.html.twig' %}
{% block head %}
<style>
.sf-reset .traces {
padding-bottom: 14px;
}
.sf-reset .traces li {
font-size: 12px;
color: #868686;
padding: 5px 4px;
list-style-type: decimal;
margin-left: 20px;
}
.sf-reset #logs .traces li.error {
font-style: normal;
color: #AA3333;
background: #f9ecec;
}
.sf-reset #logs .traces li.warning {
font-style: normal;
background: #ffcc00;
}
/* fix for Opera not liking empty <li> */
.sf-reset .traces li:after {
content: "\00A0";
}
.sf-reset .trace {
border: 1px solid #D3D3D3;
padding: 10px;
overflow: auto;
margin: 10px 0 20px;
}
.sf-reset .block-exception {
-moz-border-radius: 16px;
-webkit-border-radius: 16px;
border-radius: 16px;
margin-bottom: 20px;
background-color: #f6f6f6;
border: 1px solid #dfdfdf;
padding: 30px 28px;
word-wrap: break-word;
overflow: hidden;
}
.sf-reset .block-exception div {
color: #313131;
font-size: 10px;
}
.sf-reset .block-exception-detected .illustration-exception,
.sf-reset .block-exception-detected .text-exception {
float: left;
}
.sf-reset .block-exception-detected .illustration-exception {
width: 152px;
}
.sf-reset .block-exception-detected .text-exception {
width: 670px;
padding: 30px 44px 24px 46px;
position: relative;
}
.sf-reset .text-exception .open-quote,
.sf-reset .text-exception .close-quote {
font-family: Arial, Helvetica, sans-serif;
position: absolute;
color: #C9C9C9;
font-size: 8em;
}
.sf-reset .open-quote {
top: 0;
left: 0;
}
.sf-reset .close-quote {
bottom: -0.5em;
right: 50px;
}
.sf-reset .block-exception p {
font-family: Arial, Helvetica, sans-serif;
}
.sf-reset .block-exception p a,
.sf-reset .block-exception p a:hover {
color: #565656;
}
.sf-reset .logs h2 {
float: left;
width: 654px;
}
.sf-reset .error-count, .sf-reset .support {
float: right;
width: 170px;
text-align: right;
}
.sf-reset .error-count span {
display: inline-block;
background-color: #aacd4e;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
border-radius: 6px;
padding: 4px;
color: white;
margin-right: 2px;
font-size: 11px;
font-weight: bold;
}
.sf-reset .support a {
display: inline-block;
-moz-border-radius: 6px;
-webkit-border-radius: 6px;
border-radius: 6px;
padding: 4px;
color: #000000;
margin-right: 2px;
font-size: 11px;
font-weight: bold;
}
.sf-reset .toggle {
vertical-align: middle;
}
.sf-reset .linked ul,
.sf-reset .linked li {
display: inline;
}
.sf-reset #output-content {
color: #000;
font-size: 12px;
}
.sf-reset #traces-text pre {
white-space: pre;
font-size: 12px;
font-family: monospace;
}
</style>
{% endblock %}
{% block title %}
{{ exception.message }} ({{ status_code }} {{ status_text }})
{% endblock %}
{% block body %}
{% include '@Twig/Exception/exception.html.twig' %}
{% endblock %}

View File

@@ -0,0 +1,7 @@
<ol class="traces logs">
{% for log in logs %}
<li{% if log.priority >= 400 %} class="error"{% elseif log.priority >= 300 %} class="warning"{% endif %}>
{{ log.priorityName }} - {{ log.message|format_log_message(log.context) }}
</li>
{% endfor %}
</ol>

View File

@@ -0,0 +1,22 @@
{% if trace.function %}
at
<strong>
<abbr title="{{ trace.class }}">{{ trace.short_class }}</abbr>
{{ trace.type ~ trace.function }}
</strong>
({{ trace.args|format_args }})
{% endif %}
{% if trace.file is defined and trace.file and trace.line is defined and trace.line %}
{{ trace.function ? '<br />' : '' }}
in {{ trace.file|format_file(trace.line) }}&nbsp;
{% spaceless %}
<a href="#" onclick="toggle('trace-{{ prefix ~ '-' ~ i }}'); switchIcons('icon-{{ prefix ~ '-' ~ i }}-open', 'icon-{{ prefix ~ '-' ~ i }}-close'); return false;">
<img class="toggle" id="icon-{{ prefix ~ '-' ~ i }}-close" alt="-" src="data:image/gif;base64,R0lGODlhEgASAMQSANft94TG57Hb8GS44ez1+mC24IvK6ePx+Wa44dXs92+942e54o3L6W2844/M6dnu+P/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABIALAAAAAASABIAQAVCoCQBTBOd6Kk4gJhGBCTPxysJb44K0qD/ER/wlxjmisZkMqBEBW5NHrMZmVKvv9hMVsO+hE0EoNAstEYGxG9heIhCADs=" style="display: {{ 0 == i ? 'inline' : 'none' }}" />
<img class="toggle" id="icon-{{ prefix ~ '-' ~ i }}-open" alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: {{ 0 == i ? 'none' : 'inline' }}" />
</a>
{% endspaceless %}
<div id="trace-{{ prefix ~ '-' ~ i }}" style="display: {{ 0 == i ? 'block' : 'none' }}" class="trace">
{{ trace.file|file_excerpt(trace.line) }}
</div>
{% endif %}

View File

@@ -0,0 +1,8 @@
{% if trace.function %}
at {{ trace.class ~ trace.type ~ trace.function }}({{ trace.args|format_args_as_text }})
{% else %}
at n/a
{% endif %}
{% if trace.file is defined and trace.line is defined %}
in {{ trace.file }} line {{ trace.line }}
{% endif %}

View File

@@ -0,0 +1,25 @@
<div class="block">
{% if count > 0 %}
<h2>
<span><small>[{{ count - position + 1 }}/{{ count + 1 }}]</small></span>
{{ exception.class|abbr_class }}: {{ exception.message|nl2br|format_file_from_text }}&nbsp;
{% spaceless %}
<a href="#" onclick="toggle('traces-{{ position }}', 'traces'); switchIcons('icon-traces-{{ position }}-open', 'icon-traces-{{ position }}-close'); return false;">
<img class="toggle" id="icon-traces-{{ position }}-close" alt="-" src="data:image/gif;base64,R0lGODlhEgASAMQSANft94TG57Hb8GS44ez1+mC24IvK6ePx+Wa44dXs92+942e54o3L6W2844/M6dnu+P/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABIALAAAAAASABIAQAVCoCQBTBOd6Kk4gJhGBCTPxysJb44K0qD/ER/wlxjmisZkMqBEBW5NHrMZmVKvv9hMVsO+hE0EoNAstEYGxG9heIhCADs=" style="display: {{ 0 == count ? 'inline' : 'none' }}" />
<img class="toggle" id="icon-traces-{{ position }}-open" alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: {{ 0 == count ? 'none' : 'inline' }}" />
</a>
{% endspaceless %}
</h2>
{% else %}
<h2>Stack Trace</h2>
{% endif %}
<a id="traces-link-{{ position }}"></a>
<ol class="traces list-exception" id="traces-{{ position }}" style="display: {{ 0 == count ? 'block' : 'none' }}">
{% for i, trace in exception.trace %}
<li>
{% include '@Twig/Exception/trace.html.twig' with { 'prefix': position, 'i': i, 'trace': trace } only %}
</li>
{% endfor %}
</ol>
</div>

View File

@@ -0,0 +1,6 @@
{% if exception.trace|length %}
{% for trace in exception.trace %}
{% include '@Twig/Exception/trace.txt.twig' with { 'trace': trace } only %}
{% endfor %}
{% endif %}

View File

@@ -0,0 +1,8 @@
<traces>
{% for trace in exception.trace %}
<trace>
{% include '@Twig/Exception/trace.txt.twig' with { 'trace': trace } only %}
</trace>
{% endfor %}
</traces>

View File

@@ -0,0 +1,18 @@
<div class="block">
<h2>
Stack Trace (Plain Text)&nbsp;
{% spaceless %}
<a href="#" onclick="toggle('traces-text'); switchIcons('icon-traces-text-open', 'icon-traces-text-close'); return false;">
<img class="toggle" id="icon-traces-text-close" alt="-" src="data:image/gif;base64,R0lGODlhEgASAMQSANft94TG57Hb8GS44ez1+mC24IvK6ePx+Wa44dXs92+942e54o3L6W2844/M6dnu+P/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABIALAAAAAASABIAQAVCoCQBTBOd6Kk4gJhGBCTPxysJb44K0qD/ER/wlxjmisZkMqBEBW5NHrMZmVKvv9hMVsO+hE0EoNAstEYGxG9heIhCADs=" style="display: none" />
<img class="toggle" id="icon-traces-text-open" alt="+" src="data:image/gif;base64,R0lGODlhEgASAMQTANft99/v+Ga44bHb8ITG52S44dXs9+z1+uPx+YvK6WC24G+944/M6W28443L6dnu+Ge54v/+/l614P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAABMALAAAAAASABIAQAVS4DQBTiOd6LkwgJgeUSzHSDoNaZ4PU6FLgYBA5/vFID/DbylRGiNIZu74I0h1hNsVxbNuUV4d9SsZM2EzWe1qThVzwWFOAFCQFa1RQq6DJB4iIQA7" style="display: inline" />
</a>
{% endspaceless %}
</h2>
<div id="traces-text" class="trace" style="display: none;">
<pre>{% for i, e in exception.toarray %}
[{{ i + 1 }}] {{ e.class }}: {{ e.message }}
{% include '@Twig/Exception/traces.txt.twig' with { 'exception': e } only %}
{% endfor %}</pre>
</div>
</div>

View File

@@ -0,0 +1,251 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="{{ _charset }}" />
<meta name="robots" content="noindex,nofollow" />
<title>{% block title %}{% endblock %}</title>
<style>
html {
background: #eee;
}
body {
font: 11px Verdana, Arial, sans-serif;
color: #333;
}
.sf-reset, .sf-reset .block, .sf-reset #message {
margin: auto;
}
img {
border: 0;
}
.clear {
clear: both;
height: 0;
font-size: 0;
line-height: 0;
}
.clear-fix:after {
content: "\0020";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.clear-fix {
display: inline-block;
}
* html .clear-fix {
height: 1%;
}
.clear-fix {
display: block;
}
.header {
padding: 30px 30px 20px 30px;
}
.header-logo {
float: left;
}
.search {
float: right;
padding-top: 20px;
}
.search label {
line-height: 28px;
vertical-align: middle;
}
.search input {
width: 195px;
font-size: 12px;
border: 1px solid #dadada;
background: #fff url(data:image/gif;base64,R0lGODlhAQAFAKIAAPX19e/v7/39/fr6+urq6gAAAAAAAAAAACH5BAAAAAAALAAAAAABAAUAAAMESAEjCQA7) repeat-x left top;
padding: 5px 6px;
color: #565656;
}
.search input[type="search"] {
-webkit-appearance: textfield;
}
#content {
width: 970px;
margin: 0 auto;
}
#content pre {
white-space: normal;
font-family: Arial, Helvetica, sans-serif;
}
/*
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
Code licensed under the BSD License:
http://developer.yahoo.com/yui/license.html
version: 3.1.2
build: 56
*/
.sf-reset div,.sf-reset dl,.sf-reset dt,.sf-reset dd,.sf-reset ul,.sf-reset ol,.sf-reset li,.sf-reset h1,.sf-reset h2,.sf-reset h3,.sf-reset h4,.sf-reset h5,.sf-reset h6,.sf-reset pre,.sf-reset code,.sf-reset form,.sf-reset fieldset,.sf-reset legend,.sf-reset input,.sf-reset textarea,.sf-reset p,.sf-reset blockquote,.sf-reset th,.sf-reset td{margin:0;padding:0;}.sf-reset table{border-collapse:collapse;border-spacing:0;}.sf-reset fieldset,.sf-reset img{border:0;}.sf-reset address,.sf-reset caption,.sf-reset cite,.sf-reset code,.sf-reset dfn,.sf-reset em,.sf-reset strong,.sf-reset th,.sf-reset var{font-style:normal;font-weight:normal;}.sf-reset li{list-style:none;}.sf-reset caption,.sf-reset th{text-align:left;}.sf-reset h1,.sf-reset h2,.sf-reset h3,.sf-reset h4,.sf-reset h5,.sf-reset h6{font-size:100%;font-weight:normal;}.sf-reset q:before,.sf-reset q:after{content:'';}.sf-reset abbr,.sf-reset acronym{border:0;font-variant:normal;}.sf-reset sup{vertical-align:text-top;}.sf-reset sub{vertical-align:text-bottom;}.sf-reset input,.sf-reset textarea,.sf-reset select{font-family:inherit;font-size:inherit;font-weight:inherit;}.sf-reset input,.sf-reset textarea,.sf-reset select{font-size:100%;}.sf-reset legend{color:#000;}
.sf-reset abbr {
border-bottom: 1px dotted #000;
cursor: help;
}
.sf-reset p {
font-size: 14px;
line-height: 20px;
padding-bottom: 20px;
}
.sf-reset strong {
color: #313131;
font-weight: bold;
}
.sf-reset a {
color: #6c6159;
}
.sf-reset a img {
border: none;
}
.sf-reset a:hover {
text-decoration: underline;
}
.sf-reset em {
font-style: italic;
}
.sf-reset h2,
.sf-reset h3 {
font-weight: bold;
}
.sf-reset h1 {
font-family: Georgia, "Times New Roman", Times, serif;
font-size: 20px;
color: #313131;
word-wrap: break-word;
}
.sf-reset li {
padding-bottom: 10px;
}
.sf-reset .block {
-moz-border-radius: 16px;
-webkit-border-radius: 16px;
border-radius: 16px;
margin-bottom: 20px;
background-color: #FFFFFF;
border: 1px solid #dfdfdf;
padding: 40px 50px;
word-break: break-all;
}
.sf-reset h2 {
font-size: 16px;
font-family: Arial, Helvetica, sans-serif;
}
.sf-reset li a {
background: none;
color: #868686;
text-decoration: none;
}
.sf-reset li a:hover {
background: none;
color: #313131;
text-decoration: underline;
}
.sf-reset ol {
padding: 10px 0;
}
.sf-reset ol li {
list-style: decimal;
margin-left: 20px;
padding: 2px;
padding-bottom: 20px;
}
.sf-reset ol ol li {
list-style-position: inside;
margin-left: 0;
white-space: nowrap;
font-size: 12px;
padding-bottom: 0;
}
.sf-reset li .selected {
background-color: #ffd;
}
.sf-button {
display: -moz-inline-box;
display: inline-block;
text-align: center;
vertical-align: middle;
border: 0;
background: transparent none;
text-transform: uppercase;
cursor: pointer;
font: bold 11px Arial, Helvetica, sans-serif;
}
.sf-button span {
text-decoration: none;
display: block;
height: 28px;
float: left;
}
.sf-button .border-l {
text-decoration: none;
display: block;
height: 28px;
float: left;
padding: 0 0 0 7px;
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAcCAYAAACtQ6WLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQtJREFUeNpiPHnyJAMakARiByDWYEGT8ADiYGVlZStubm5xlv///4MEQYoKZGRkQkRERLRYWVl5wYJQyXBZWdkwCQkJUxAHKgaWlAHSLqKiosb//v1DsYMFKGCvoqJiDmQzwXTAJYECulxcXNLoumCSoszMzDzoumDGghQwYZUECWIzkrAkSIIGOmlkLI10AiX//P379x8jIyMTNmPf/v79+ysLCwsvuiQoNi5//fr1Kch4dAyS3P/gwYMTQBP+wxwHw0xA4gkQ73v9+vUZdJ2w1Lf82bNn4iCHCQoKasHsZw4ODgbRIL8c+/Lly5M3b978Y2dn5wC6npkFLXnsAOKLjx49AmUHLYAAAwBoQubG016R5wAAAABJRU5ErkJggg==) no-repeat top left;
}
.sf-button .border-r {
padding: 0 7px 0 0;
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAcCAYAAACtQ6WLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAR1JREFUeNpiPHnyZCMDA8MNID5gZmb2nAEJMH7//v3N169fX969e/cYkL8WqGAHXPLv37//QYzfv39/fvPmzbUnT56sAXInmJub/2H5/x8sx8DCwsIrISFhDmQyPX78+CmQXs70798/BmQsKipqBNTgdvz4cWkmkE5kDATMioqKZkCFdiwg1eiAi4tLGqhQF24nMmBmZuYEigth1QkEbEBxTlySYPvJkwSJ00AnjYylgU6gxB8g/oFVEphkvgLF32KNMmCCewYUv4qhEyj47+HDhyeBzIMYOoEp8CxQw56wsLAncJ1//vz5/P79+2svX74EJc2V4BT58+fPd8CE/QKYHMGJOiIiAp6oWW7evDkNSF8DZYfIyEiU7AAQYACJ2vxVdJW4eQAAAABJRU5ErkJggg==) right top no-repeat;
}
.sf-button .btn-bg {
padding: 0 14px;
color: #636363;
line-height: 28px;
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcCAYAAACgXdXMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAClJREFUeNpiPnny5EKGf//+/Wf6//8/A4QAcrGzKCZwGc9sa2urBBBgAIbDUoYVp9lmAAAAAElFTkSuQmCC) repeat-x top left;
}
.sf-button:hover .border-l,
.sf-button-selected .border-l {
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAcCAYAAACtQ6WLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAR9JREFUeNpi/P//PwMyOHfunDqQSgNiexZkibNnzxYBqZa3HOs5v7PcYQBLnjlzhg1IbfzIdsTjA/t+ht9Mr8GKwZL//v3r+sB+0OMN+zqIEf8gFMvJkyd1gXTOa9YNDP//otrPAtSV/Jp9HfPff78Z0AEL0LUeXxivMfxD0wXTqfjj/2ugkf+wSrL9/YtpJEyS4S8WI5Ek/+GR/POPFjr//cenE6/kP9q4Fo/kr39/mdj+M/zFkGQCSj5i+ccPjLJ/GBgkuYOHQR1sNDpmAkb2LBmWwL///zKCIxwZM0VHR18G6p4uxeLLAA4tJMwEshiou1iMxXaHLGswA+t/YbhORuQUv2DBAnCifvxzI+enP3dQJUFg/vz5sOzgBBBgAPxX9j0YnH4JAAAAAElFTkSuQmCC) no-repeat top left;
}
.sf-button:hover .border-r,
.sf-button-selected .border-r {
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAcCAYAAACtQ6WLAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAT5JREFUeNpiPHv27BkGBoaDQDzLyMjoJgMSYHrM3WX8hn1d0f///88DFRYhSzIuv2X5H8Rg/SfKIPDTkYH/l80OINffxMTkF9O/f/8ZQPgnwyuGl+wrGd6x7vf49+9fO9jYf3+Bkkj4NesmBqAV+SdPntQC6vzHgIz//gOawbqOGchOxtAJwp8Zr4F0e7D8/fuPAR38/P8eZIo0yz8skv8YvoIk+YE6/zNgAyD7sRqLkPzzjxY6/+HS+R+fTkZ8djLh08lCUCcuSWawJGbwMTGwg7zyBatX2Bj5QZKPsBrLzaICktzN8g/NWEYGZgYZjoC/wMiei5FMpFh8QPSU6Ojoy3Cd7EwiDBJsDgxiLNY7gLrKQGIsHAxSDHxAO2TZ/b8D+TVxcXF9MCtYtLiKLgDpfUDVsxITE1GyA0CAAQA2E/N8VuHyAAAAAABJRU5ErkJggg==) right top no-repeat;
}
.sf-button:hover .btn-bg,
.sf-button-selected .btn-bg {
color: #FFFFFF;
text-shadow:0 1px 1px #6b9311;
background: transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAcCAIAAAAvP0KbAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAEFJREFUeNpiPnv2LNMdvlymf///M/37B8R/QfQ/MP33L4j+B6Qh7L9//sHpf2h8MA1V+w/KRjYLaDaLCU8vQIABAFO3TxZriO4yAAAAAElFTkSuQmCC) repeat-x top left;
}
</style>
{% block head %}{% endblock %}
</head>
<body>
<div id="content">
<div class="header clear-fix">
<div class="header-logo">
{{ include('@Twig/symfony.svg') }}
</div>
<div class="search">
<form method="get" action="https://symfony.com/search" target="_blank">
<div class="form-row">
<label for="search-id">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAABUElEQVQoz2NgAIJ29iBdD0d7X2cPb+tY2f9MDMjgP2O2hKu7vS8CBlisZUNSMJ3fxRMkXO61wm2ue6I3iB1q8Z8ZriDZFCS03fm/wX+1/xp/TBo8QPxeqf+MUAW+QIFKj/+q/wX/c/3n/i/6Qd/bx943z/Q/K1SBI1D9fKv/AhCn/Wf5L5EHdFGKw39OqAIXoPpOMziX4T9/DFBBnuN/HqhAEtCKCNf/XDA/rZRyAmrpsvrPDVUw3wrkqCiLaewg6TohX1d7X0ffs5r/OaAKfinmgt3t4ulr4+Xg4ANip3j+l/zPArNT4LNOD0pAgWCSOUIBy3+h/+pXbBa5tni0eMx23+/mB1YSYnENroT5Pw/QSOX/mkCo+l/jgo0v2KJA643s8PgAmsMBDCbu/5xALHPB2husxN9uCzsDOgAq5kAoaZVnYMCh5Ky1r88Eh/+iABM8jUk7ClYIAAAAAElFTkSuQmCC" alt="Search on Symfony website" />
</label>
<input name="q" id="search-id" type="search" placeholder="Search on Symfony website" />
<button type="submit" class="sf-button">
<span class="border-l">
<span class="border-r">
<span class="btn-bg">OK</span>
</span>
</span>
</button>
</div>
</form>
</div>
</div>
<div class="sf-reset">
{% block body %}{% endblock %}
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="182" height="62" viewBox="45 19 200 80">
<circle cx="68.72" cy="59.18" r="39.19"/>
<path fill="#FFF" d="M85.85 34.5c-3.98.14-7.45 2.33-10.04 5.36-2.85 3.33-4.76 7.28-6.13 11.3-2.46-2-4.35-4.6-8.3-5.74-3.04-.88-6.23-.52-9.18 1.67-1.4 1.04-2.36 2.6-2.82 4.1-1.18 3.83 1.25 7.25 2.35 8.48l2.4 2.58c.5.5 1.7 1.82 1.12 3.72-.63 2.06-3.12 3.4-5.66 2.6-1.15-.35-2.78-1.2-2.4-2.4.14-.47.5-.84.68-1.26.16-.36.25-.63.3-.8.47-1.5-.17-3.48-1.8-3.98-1.5-.47-3.06-.1-3.66 1.85-.7 2.2.38 6.24 6.06 8 6.66 2.03 12.3-1.6 13.1-6.32.5-2.96-.84-5.17-3.3-8l-2-2.2c-1.2-1.2-1.6-3.27-.36-4.86 1.06-1.33 2.56-1.9 5.03-1.23 3.6.96 5.2 3.46 7.87 5.47-1.1 3.63-1.82 7.26-2.48 10.52l-.4 2.42c-1.9 10.03-3.37 15.54-7.16 18.7-.77.55-1.86 1.36-3.5 1.42-.87.03-1.15-.57-1.16-.83-.02-.6.5-.87.83-1.14.5-.28 1.27-.74 1.22-2.2-.06-1.74-1.5-3.25-3.57-3.18-1.56.05-3.93 1.52-3.84 4.2.1 2.77 2.67 4.85 6.57 4.72 2.08-.07 6.73-.92 11.3-6.37 5.34-6.24 6.83-13.4 7.95-18.63l1.25-6.92c.7.1 1.44.15 2.25.16 6.64.14 9.96-3.3 10-5.8.05-1.5-.98-3-2.42-2.97-1.03.03-2.32.72-2.63 2.14-.3 1.4 2.1 2.66.23 3.9-1.34.86-3.75 1.47-7.14.97l.62-3.4c1.25-6.46 2.8-14.4 8.7-14.6.42 0 1.98.03 2.02 1.07 0 .34-.08.43-.48 1.22-.4.62-.57 1.15-.55 1.75.06 1.66 1.3 2.74 3.12 2.68 2.43-.08 3.13-2.44 3.1-3.66-.1-2.85-3.12-4.65-7.1-4.52z"/>
<g>
<path d="M209.92 51.2c5.65 0 9.43 4.08 9.43 9.72 0 5.3-3.86 9.7-9.43 9.7-5.6 0-9.46-4.4-9.46-9.7 0-5.64 3.8-9.72 9.46-9.72zm0 16.67c4 0 5.8-3.64 5.8-6.95 0-3.53-2.16-6.95-5.8-6.95-3.67 0-5.82 3.42-5.82 6.95 0 3.3 1.78 6.95 5.82 6.95zM200 52.93v-1.3h-4.92v-1.76c0-2.5.37-4.4 3.3-4.4H198.6c.8.06 1.5-.6 1.54-1.43l.06-1.12c-.7-.1-1.42-.22-2.3-.22-5.05 0-6.22 2.95-6.22 7.46v1.48h-4.36v1.44c.1.75.75 1.32 1.53 1.32h2.83v15.8h1.9c.77 0 1.42-.6 1.52-1.35V54.4h3.42c.8-.02 1.46-.67 1.5-1.47zM154.44 51.64h-.02c-.72 0-1.42.5-1.73 1.17L148 67h-.08l-4.6-14.2c-.3-.66-1-1.16-1.74-1.16h-2.4l6.3 17.32c.2.62.65 1.56.65 1.97 0 .36-1.02 4.57-4.07 4.57h-.23c-.78-.05-1.37.5-1.5 1.34l-.1 1.18c.62.1 1.25.25 2.34.25 4.5 0 5.85-4.1 7.13-7.67l6.84-18.96h-2.1zM132.7 55.4c-2.83-1.44-5.92-2.42-5.98-5.33 0-3.1 2.85-3.9 5.04-3.9h.02c.95 0 1.7.1 2.45.26h.02c.78.05 1.44-.57 1.54-1.34l.05-1.13c-1.44-.35-2.94-.54-4.24-.54-4.74.03-8.28 2.43-8.3 7 .02 3.98 2.7 5.53 5.6 6.93 2.84 1.36 5.95 2.5 5.98 5.78-.02 3.44-3.34 4.7-5.62 4.72-1.34 0-2.8-.34-4-.73-.78-.13-1.4.55-1.5 1.45l-.1 1c1.67.54 3.4 1 5.13 1 5.34-.03 9.47-2.15 9.5-7.67-.02-4.24-2.73-6.03-5.6-7.5zM184.43 70.2c.74 0 1.35-.53 1.5-1.22V58.3c0-4.04-1.7-7.1-6.43-7.1-1.68 0-4.48.95-5.75 3.6-.98-2.5-3.13-3.6-5.27-3.6-2.73 0-4.6.98-5.9 3.13h-.07v-1.15c0-.85-.7-1.54-1.54-1.54h-1.66V70.2h1.86c.86 0 1.55-.7 1.55-1.56 0-.02.02-.03.03-.05v-7.75c0-3.46 1.38-6.88 4.87-6.88 2.77 0 3.3 2.87 3.3 5.16V70.2h1.9c.8 0 1.46-.63 1.53-1.42v-7.93c0-3.46 1.4-6.88 4.88-6.88 2.77 0 3.32 2.87 3.32 5.16V70.2h1.9zM237.28 70.2c.84 0 1.5-.67 1.55-1.5v-9.53c0-4.98-2.15-7.97-6.96-7.97-2.57 0-5.05 1.28-6.07 3.4h-.07v-1.4c0-.86-.7-1.56-1.55-1.56h-1.77V70.2h1.9c.8 0 1.47-.64 1.54-1.44V61.6c0-4.5 1.75-7.63 5.64-7.63 2.98.18 3.93 2.3 3.93 6.62v9.6h1.88zM255.7 51.64h-.02c-.73 0-1.43.5-1.73 1.17l-4.7 14.2h-.08l-4.6-14.2c-.3-.66-1-1.16-1.73-1.16h-2.4l6.3 17.32c.2.62.65 1.56.65 1.97 0 .36-1.03 4.57-4.1 4.57h-.22c-.77-.05-1.36.5-1.5 1.34l-.1 1.18c.63.1 1.25.25 2.34.25 4.52 0 5.86-4.1 7.13-7.67l6.84-18.96h-2.1z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,92 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Finder\Finder;
/**
* Iterator for all templates in bundles and in the application Resources directory.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TemplateIterator implements \IteratorAggregate
{
private $kernel;
private $rootDir;
private $templates;
private $paths;
/**
* @param KernelInterface $kernel A KernelInterface instance
* @param string $rootDir The directory where global templates can be stored
* @param array $paths Additional Twig paths to warm
*/
public function __construct(KernelInterface $kernel, $rootDir, array $paths = array())
{
$this->kernel = $kernel;
$this->rootDir = $rootDir;
$this->paths = $paths;
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
if (null !== $this->templates) {
return $this->templates;
}
$this->templates = $this->findTemplatesInDirectory($this->rootDir.'/Resources/views');
foreach ($this->kernel->getBundles() as $bundle) {
$name = $bundle->getName();
if ('Bundle' === substr($name, -6)) {
$name = substr($name, 0, -6);
}
$this->templates = array_merge(
$this->templates,
$this->findTemplatesInDirectory($bundle->getPath().'/Resources/views', $name),
$this->findTemplatesInDirectory($this->rootDir.'/'.$bundle->getName().'/views', $name)
);
}
foreach ($this->paths as $dir => $namespace) {
$this->templates = array_merge($this->templates, $this->findTemplatesInDirectory($dir, $namespace));
}
return $this->templates = new \ArrayIterator(array_unique($this->templates));
}
/**
* Find templates in the given directory.
*
* @param string $dir The directory where to look for templates
* @param string|null $namespace The template namespace
*
* @return array
*/
private function findTemplatesInDirectory($dir, $namespace = null)
{
if (!is_dir($dir)) {
return array();
}
$templates = array();
foreach (Finder::create()->files()->followLinks()->in($dir) as $file) {
$templates[] = (null !== $namespace ? '@'.$namespace.'/' : '').str_replace('\\', '/', $file->getRelativePathname());
}
return $templates;
}
}

View File

@@ -0,0 +1,38 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Bundle\TwigBundle\ContainerAwareRuntimeLoader;
class ContainerAwareRuntimeLoaderTest extends TestCase
{
public function testLoad()
{
$container = $this->getMockBuilder(ContainerInterface::class)->getMock();
$container->expects($this->once())->method('get')->with('foo');
$loader = new ContainerAwareRuntimeLoader($container, array(
'FooClass' => 'foo',
));
$loader->load('FooClass');
}
public function testLoadWithoutAMatch()
{
$logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
$logger->expects($this->once())->method('warning')->with('Class "BarClass" is not configured as a Twig runtime. Add the "twig.runtime" tag to the related service in the container.');
$loader = new ContainerAwareRuntimeLoader($this->getMockBuilder(ContainerInterface::class)->getMock(), array(), $logger);
$this->assertNull($loader->load('BarClass'));
}
}

View File

@@ -0,0 +1,92 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests\Controller;
use Symfony\Bundle\TwigBundle\Tests\TestCase;
use Symfony\Bundle\TwigBundle\Controller\ExceptionController;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpFoundation\Request;
use Twig\Environment;
use Twig\Loader\ArrayLoader;
class ExceptionControllerTest extends TestCase
{
public function testShowActionCanBeForcedToShowErrorPage()
{
$twig = $this->createTwigEnv(array('@Twig/Exception/error404.html.twig' => '<html>not found</html>'));
$request = $this->createRequest('html');
$request->attributes->set('showException', false);
$exception = FlattenException::create(new \Exception(), 404);
$controller = new ExceptionController($twig, /* "showException" defaults to --> */ true);
$response = $controller->showAction($request, $exception, null);
$this->assertEquals(200, $response->getStatusCode()); // successful request
$this->assertEquals('<html>not found</html>', $response->getContent());
}
public function testFallbackToHtmlIfNoTemplateForRequestedFormat()
{
$twig = $this->createTwigEnv(array('@Twig/Exception/error.html.twig' => '<html></html>'));
$request = $this->createRequest('txt');
$exception = FlattenException::create(new \Exception());
$controller = new ExceptionController($twig, false);
$controller->showAction($request, $exception);
$this->assertEquals('html', $request->getRequestFormat());
}
public function testFallbackToHtmlWithFullExceptionIfNoTemplateForRequestedFormatAndExceptionsShouldBeShown()
{
$twig = $this->createTwigEnv(array('@Twig/Exception/exception_full.html.twig' => '<html></html>'));
$request = $this->createRequest('txt');
$request->attributes->set('showException', true);
$exception = FlattenException::create(new \Exception());
$controller = new ExceptionController($twig, false);
$controller->showAction($request, $exception);
$this->assertEquals('html', $request->getRequestFormat());
}
public function testResponseHasRequestedMimeType()
{
$twig = $this->createTwigEnv(array('@Twig/Exception/error.json.twig' => '{}'));
$request = $this->createRequest('json');
$exception = FlattenException::create(new \Exception());
$controller = new ExceptionController($twig, false);
$response = $controller->showAction($request, $exception);
$this->assertEquals('json', $request->getRequestFormat());
$this->assertEquals($request->getMimeType('json'), $response->headers->get('Content-Type'));
}
private function createRequest($requestFormat)
{
$request = Request::create('whatever');
$request->headers->set('X-Php-Ob-Level', 1);
$request->setRequestFormat($requestFormat);
return $request;
}
private function createTwigEnv(array $templates)
{
return new Environment(new ArrayLoader($templates));
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests\Controller;
use Symfony\Bundle\TwigBundle\Controller\PreviewErrorController;
use Symfony\Bundle\TwigBundle\Tests\TestCase;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;
class PreviewErrorControllerTest extends TestCase
{
public function testForwardRequestToConfiguredController()
{
$request = Request::create('whatever');
$response = new Response('');
$code = 123;
$logicalControllerName = 'foo:bar:baz';
$kernel = $this->getMockBuilder('\Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
$kernel
->expects($this->once())
->method('handle')
->with(
$this->callback(function (Request $request) use ($logicalControllerName, $code) {
$this->assertEquals($logicalControllerName, $request->attributes->get('_controller'));
$exception = $request->attributes->get('exception');
$this->assertInstanceOf(FlattenException::class, $exception);
$this->assertEquals($code, $exception->getStatusCode());
$this->assertFalse($request->attributes->get('showException'));
return true;
}),
$this->equalTo(HttpKernelInterface::SUB_REQUEST)
)
->will($this->returnValue($response));
$controller = new PreviewErrorController($kernel, $logicalControllerName);
$this->assertSame($response, $controller->previewErrorPageAction($request, $code));
}
}

View File

@@ -0,0 +1,47 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests\DependencyInjection\Compiler;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\ExtensionPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
class ExtensionPassTest extends TestCase
{
public function testProcessDoesNotDropExistingFileLoaderMethodCalls()
{
$container = new ContainerBuilder();
$container->setParameter('kernel.debug', false);
$container->setParameter('kernel.root_dir', __DIR__);
$container->register('twig.app_variable', '\Symfony\Bridge\Twig\AppVariable');
$container->register('templating', '\Symfony\Bundle\TwigBundle\TwigEngine');
$container->register('twig.extension.yaml');
$container->register('twig.extension.debug.stopwatch');
$container->register('twig.extension.expression');
$nativeTwigLoader = new Definition('\Twig\Loader\FilesystemLoader');
$nativeTwigLoader->addMethodCall('addPath', array());
$container->setDefinition('twig.loader.native_filesystem', $nativeTwigLoader);
$filesystemLoader = new Definition('\Symfony\Bundle\TwigBundle\Loader\FilesystemLoader');
$filesystemLoader->setArguments(array(null, null, null));
$filesystemLoader->addMethodCall('addPath', array());
$container->setDefinition('twig.loader.filesystem', $filesystemLoader);
$extensionPass = new ExtensionPass();
$extensionPass->process($container);
$this->assertCount(2, $filesystemLoader->getMethodCalls());
}
}

View File

@@ -0,0 +1,151 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests\DependencyInjection\Compiler;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigLoaderPass;
class TwigLoaderPassTest extends TestCase
{
/**
* @var \PHPUnit_Framework_MockObject_MockObject
*/
private $builder;
/**
* @var Definition
*/
private $chainLoader;
/**
* @var TwigLoaderPass
*/
private $pass;
protected function setUp()
{
$this->builder = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')->setMethods(array('hasDefinition', 'findTaggedServiceIds', 'setAlias', 'getDefinition'))->getMock();
$this->chainLoader = new Definition('loader');
$this->pass = new TwigLoaderPass();
}
public function testMapperPassWithOneTaggedLoaders()
{
$serviceIds = array(
'test_loader_1' => array(
array(),
),
);
$this->builder->expects($this->once())
->method('hasDefinition')
->with('twig')
->will($this->returnValue(true));
$this->builder->expects($this->once())
->method('findTaggedServiceIds')
->with('twig.loader')
->will($this->returnValue($serviceIds));
$this->builder->expects($this->once())
->method('setAlias')
->with('twig.loader', 'test_loader_1');
$this->pass->process($this->builder);
}
public function testMapperPassWithTwoTaggedLoaders()
{
$serviceIds = array(
'test_loader_1' => array(
array(),
),
'test_loader_2' => array(
array(),
),
);
$this->builder->expects($this->once())
->method('hasDefinition')
->with('twig')
->will($this->returnValue(true));
$this->builder->expects($this->once())
->method('findTaggedServiceIds')
->with('twig.loader')
->will($this->returnValue($serviceIds));
$this->builder->expects($this->once())
->method('getDefinition')
->with('twig.loader.chain')
->will($this->returnValue($this->chainLoader));
$this->builder->expects($this->once())
->method('setAlias')
->with('twig.loader', 'twig.loader.chain');
$this->pass->process($this->builder);
$calls = $this->chainLoader->getMethodCalls();
$this->assertCount(2, $calls);
$this->assertEquals('addLoader', $calls[0][0]);
$this->assertEquals('addLoader', $calls[1][0]);
$this->assertEquals('test_loader_1', (string) $calls[0][1][0]);
$this->assertEquals('test_loader_2', (string) $calls[1][1][0]);
}
public function testMapperPassWithTwoTaggedLoadersWithPriority()
{
$serviceIds = array(
'test_loader_1' => array(
array('priority' => 100),
),
'test_loader_2' => array(
array('priority' => 200),
),
);
$this->builder->expects($this->once())
->method('hasDefinition')
->with('twig')
->will($this->returnValue(true));
$this->builder->expects($this->once())
->method('findTaggedServiceIds')
->with('twig.loader')
->will($this->returnValue($serviceIds));
$this->builder->expects($this->once())
->method('getDefinition')
->with('twig.loader.chain')
->will($this->returnValue($this->chainLoader));
$this->builder->expects($this->once())
->method('setAlias')
->with('twig.loader', 'twig.loader.chain');
$this->pass->process($this->builder);
$calls = $this->chainLoader->getMethodCalls();
$this->assertCount(2, $calls);
$this->assertEquals('addLoader', $calls[0][0]);
$this->assertEquals('addLoader', $calls[1][0]);
$this->assertEquals('test_loader_2', (string) $calls[0][1][0]);
$this->assertEquals('test_loader_1', (string) $calls[1][1][0]);
}
/**
* @expectedException \Symfony\Component\DependencyInjection\Exception\LogicException
*/
public function testMapperPassWithZeroTaggedLoaders()
{
$this->builder->expects($this->once())
->method('hasDefinition')
->with('twig')
->will($this->returnValue(true));
$this->builder->expects($this->once())
->method('findTaggedServiceIds')
->with('twig.loader')
->will($this->returnValue(array()));
$this->pass->process($this->builder);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests\DependencyInjection;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\TwigBundle\DependencyInjection\Configuration;
use Symfony\Component\Config\Definition\Processor;
class ConfigurationTest extends TestCase
{
public function testDoNoDuplicateDefaultFormResources()
{
$input = array(
'form_themes' => array('form_div_layout.html.twig'),
);
$processor = new Processor();
$config = $processor->processConfiguration(new Configuration(), array($input));
$this->assertEquals(array('form_div_layout.html.twig'), $config['form_themes']);
}
}

View File

@@ -0,0 +1 @@
This is a layout

View File

@@ -0,0 +1,6 @@
<?php
$container->loadFromExtension('twig', array(
'autoescape_service' => 'my_project.some_bundle.template_escaping_guesser',
'autoescape_service_method' => 'guess',
));

View File

@@ -0,0 +1,3 @@
<?php
$container->loadFromExtension('twig', array());

View File

@@ -0,0 +1,7 @@
<?php
$container->loadFromExtension('twig', array(
'paths' => array(
'namespaced_path3' => 'namespace3',
),
));

View File

@@ -0,0 +1,14 @@
<?php
$container->loadFromExtension('twig', array(
'date' => array(
'format' => 'Y-m-d',
'interval_format' => '%d',
'timezone' => 'Europe/Berlin',
),
'number_format' => array(
'decimals' => 2,
'decimal_point' => ',',
'thousands_separator' => '.',
),
));

View File

@@ -0,0 +1,26 @@
<?php
$container->loadFromExtension('twig', array(
'form_themes' => array(
'MyBundle::form.html.twig',
),
'globals' => array(
'foo' => '@bar',
'baz' => '@@qux',
'pi' => 3.14,
'bad' => array('key' => 'foo'),
),
'auto_reload' => true,
'autoescape' => true,
'base_template_class' => 'stdClass',
'cache' => '/tmp',
'charset' => 'ISO-8859-1',
'debug' => true,
'strict_variables' => true,
'paths' => array(
'path1',
'path2',
'namespaced_path1' => 'namespace1',
'namespaced_path2' => 'namespace2',
),
));

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:twig="http://symfony.com/schema/dic/twig"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">
<twig:config autoescape-service="my_project.some_bundle.template_escaping_guesser" autoescape-service-method="guess" />
</container>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:twig="http://symfony.com/schema/dic/twig"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">
<twig:config />
</container>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:twig="http://symfony.com/schema/dic/twig"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">
<twig:config auto-reload="true" autoescape="true" base-template-class="stdClass" cache="/tmp" charset="ISO-8859-1" debug="true" strict-variables="true">
<twig:path namespace="namespace3">namespaced_path3</twig:path>
</twig:config>
</container>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:twig="http://symfony.com/schema/dic/twig"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">
<twig:config>
<twig:date format="Y-m-d" interval-format="%d" timezone="Europe/Berlin" />
<twig:number-format decimals="2" decimal-point="," thousands-separator="." />
</twig:config>
</container>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:twig="http://symfony.com/schema/dic/twig"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">
<twig:config auto-reload="true" autoescape="true" base-template-class="stdClass" cache="/tmp" charset="ISO-8859-1" debug="true" strict-variables="true">
<twig:form-theme>MyBundle::form.html.twig</twig:form-theme>
<twig:global key="foo" id="bar" type="service" />
<twig:global key="baz">@@qux</twig:global>
<twig:global key="pi">3.14</twig:global>
<twig:path>path1</twig:path>
<twig:path>path2</twig:path>
<twig:path namespace="namespace1">namespaced_path1</twig:path>
<twig:path namespace="namespace2">namespaced_path2</twig:path>
</twig:config>
</container>

View File

@@ -0,0 +1,3 @@
twig:
autoescape_service: my_project.some_bundle.template_escaping_guesser
autoescape_service_method: guess

View File

@@ -0,0 +1 @@
twig:

View File

@@ -0,0 +1,3 @@
twig:
paths:
namespaced_path3: namespace3

View File

@@ -0,0 +1,9 @@
twig:
date:
format: Y-m-d
interval_format: '%d'
timezone: Europe/Berlin
number_format:
decimals: 2
decimal_point: ','
thousands_separator: .

View File

@@ -0,0 +1,20 @@
twig:
form_themes:
- MyBundle::form.html.twig
globals:
foo: "@bar"
baz: "@@qux"
pi: 3.14
bad: {key: foo}
auto_reload: true
autoescape: true
base_template_class: stdClass
cache: /tmp
charset: ISO-8859-1
debug: true
strict_variables: true
paths:
path1: ''
path2: ''
namespaced_path1: namespace1
namespaced_path2: namespace2

View File

@@ -0,0 +1,348 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests\DependencyInjection;
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\RuntimeLoaderPass;
use Symfony\Bundle\TwigBundle\DependencyInjection\TwigExtension;
use Symfony\Bundle\TwigBundle\Tests\TestCase;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
class TwigExtensionTest extends TestCase
{
public function testLoadEmptyConfiguration()
{
$container = $this->createContainer();
$container->registerExtension(new TwigExtension());
$container->loadFromExtension('twig', array());
$this->compileContainer($container);
$this->assertEquals('Twig\Environment', $container->getDefinition('twig')->getClass(), '->load() loads the twig.xml file');
$this->assertContains('form_div_layout.html.twig', $container->getParameter('twig.form.resources'), '->load() includes default template for form resources');
// Twig options
$options = $container->getDefinition('twig')->getArgument(1);
$this->assertEquals('%kernel.cache_dir%/twig', $options['cache'], '->load() sets default value for cache option');
$this->assertEquals('%kernel.charset%', $options['charset'], '->load() sets default value for charset option');
$this->assertEquals('%kernel.debug%', $options['debug'], '->load() sets default value for debug option');
}
/**
* @dataProvider getFormats
*/
public function testLoadFullConfiguration($format)
{
$container = $this->createContainer();
$container->registerExtension(new TwigExtension());
$this->loadFromFile($container, 'full', $format);
$this->compileContainer($container);
$this->assertEquals('Twig\Environment', $container->getDefinition('twig')->getClass(), '->load() loads the twig.xml file');
// Form resources
$resources = $container->getParameter('twig.form.resources');
$this->assertContains('form_div_layout.html.twig', $resources, '->load() includes default template for form resources');
$this->assertContains('MyBundle::form.html.twig', $resources, '->load() merges new templates into form resources');
// Globals
$calls = $container->getDefinition('twig')->getMethodCalls();
$this->assertEquals('app', $calls[0][1][0], '->load() registers services as Twig globals');
$this->assertEquals(new Reference('twig.app_variable'), $calls[0][1][1]);
$this->assertEquals('foo', $calls[2][1][0], '->load() registers services as Twig globals');
$this->assertEquals(new Reference('bar'), $calls[2][1][1], '->load() registers services as Twig globals');
$this->assertEquals('baz', $calls[3][1][0], '->load() registers variables as Twig globals');
$this->assertEquals('@qux', $calls[3][1][1], '->load() allows escaping of service identifiers');
$this->assertEquals('pi', $calls[4][1][0], '->load() registers variables as Twig globals');
$this->assertEquals(3.14, $calls[4][1][1], '->load() registers variables as Twig globals');
// Yaml and Php specific configs
if (in_array($format, array('yml', 'php'))) {
$this->assertEquals('bad', $calls[5][1][0], '->load() registers variables as Twig globals');
$this->assertEquals(array('key' => 'foo'), $calls[5][1][1], '->load() registers variables as Twig globals');
}
// Twig options
$options = $container->getDefinition('twig')->getArgument(1);
$this->assertTrue($options['auto_reload'], '->load() sets the auto_reload option');
$this->assertTrue($options['autoescape'], '->load() sets the autoescape option');
$this->assertEquals('stdClass', $options['base_template_class'], '->load() sets the base_template_class option');
$this->assertEquals('/tmp', $options['cache'], '->load() sets the cache option');
$this->assertEquals('ISO-8859-1', $options['charset'], '->load() sets the charset option');
$this->assertTrue($options['debug'], '->load() sets the debug option');
$this->assertTrue($options['strict_variables'], '->load() sets the strict_variables option');
}
/**
* @dataProvider getFormats
*/
public function testLoadCustomTemplateEscapingGuesserConfiguration($format)
{
$container = $this->createContainer();
$container->registerExtension(new TwigExtension());
$this->loadFromFile($container, 'customTemplateEscapingGuesser', $format);
$this->compileContainer($container);
$options = $container->getDefinition('twig')->getArgument(1);
$this->assertEquals(array(new Reference('my_project.some_bundle.template_escaping_guesser'), 'guess'), $options['autoescape']);
}
/**
* @dataProvider getFormats
*/
public function testLoadDefaultTemplateEscapingGuesserConfiguration($format)
{
$container = $this->createContainer();
$container->registerExtension(new TwigExtension());
$this->loadFromFile($container, 'empty', $format);
$this->compileContainer($container);
$options = $container->getDefinition('twig')->getArgument(1);
$this->assertEquals('name', $options['autoescape']);
}
/**
* @dataProvider getFormats
*/
public function testLoadCustomDateFormats($fileFormat)
{
$container = $this->createContainer();
$container->registerExtension(new TwigExtension());
$this->loadFromFile($container, 'formats', $fileFormat);
$this->compileContainer($container);
$environmentConfigurator = $container->getDefinition('twig.configurator.environment');
$this->assertSame('Y-m-d', $environmentConfigurator->getArgument(0));
$this->assertSame('%d', $environmentConfigurator->getArgument(1));
$this->assertSame('Europe/Berlin', $environmentConfigurator->getArgument(2));
$this->assertSame(2, $environmentConfigurator->getArgument(3));
$this->assertSame(',', $environmentConfigurator->getArgument(4));
$this->assertSame('.', $environmentConfigurator->getArgument(5));
}
public function testGlobalsWithDifferentTypesAndValues()
{
$globals = array(
'array' => array(),
'false' => false,
'float' => 2.0,
'integer' => 3,
'null' => null,
'object' => new \stdClass(),
'string' => 'foo',
'true' => true,
);
$container = $this->createContainer();
$container->registerExtension(new TwigExtension());
$container->loadFromExtension('twig', array('globals' => $globals));
$this->compileContainer($container);
$calls = $container->getDefinition('twig')->getMethodCalls();
foreach (array_slice($calls, 2) as $call) {
$this->assertEquals(key($globals), $call[1][0]);
$this->assertSame(current($globals), $call[1][1]);
next($globals);
}
}
/**
* @dataProvider getFormats
*/
public function testTwigLoaderPaths($format)
{
$container = $this->createContainer();
$container->registerExtension(new TwigExtension());
$this->loadFromFile($container, 'full', $format);
$this->loadFromFile($container, 'extra', $format);
$this->compileContainer($container);
$def = $container->getDefinition('twig.loader.native_filesystem');
$paths = array();
foreach ($def->getMethodCalls() as $call) {
if ('addPath' === $call[0] && false === strpos($call[1][0], 'Form')) {
$paths[] = $call[1];
}
}
$this->assertEquals(array(
array('path1'),
array('path2'),
array('namespaced_path1', 'namespace1'),
array('namespaced_path2', 'namespace2'),
array('namespaced_path3', 'namespace3'),
array(__DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views', 'ChildChildChildChildTwig'),
array(__DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views', 'ChildChildChildTwig'),
array(__DIR__.'/Fixtures/Bundle/ChildChildChildTwigBundle/Resources/views', 'ChildChildChildTwig'),
array(__DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views', 'Twig'),
array(__DIR__.'/Fixtures/Bundle/ChildChildChildTwigBundle/Resources/views', 'Twig'),
array(__DIR__.'/Fixtures/Bundle/ChildChildTwigBundle/Resources/views', 'Twig'),
array(__DIR__.'/Fixtures/Bundle/ChildTwigBundle/Resources/views', 'Twig'),
array(__DIR__.'/Fixtures/Resources/TwigBundle/views', 'Twig'),
array(realpath(__DIR__.'/../..').'/Resources/views', 'Twig'),
array(__DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views', 'ChildTwig'),
array(__DIR__.'/Fixtures/Bundle/ChildChildChildTwigBundle/Resources/views', 'ChildTwig'),
array(__DIR__.'/Fixtures/Bundle/ChildChildTwigBundle/Resources/views', 'ChildTwig'),
array(__DIR__.'/Fixtures/Bundle/ChildTwigBundle/Resources/views', 'ChildTwig'),
array(__DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle/Resources/views', 'ChildChildTwig'),
array(__DIR__.'/Fixtures/Bundle/ChildChildChildTwigBundle/Resources/views', 'ChildChildTwig'),
array(__DIR__.'/Fixtures/Bundle/ChildChildTwigBundle/Resources/views', 'ChildChildTwig'),
array(__DIR__.'/Fixtures/Resources/views'),
), $paths);
}
public function getFormats()
{
return array(
array('php'),
array('yml'),
array('xml'),
);
}
/**
* @dataProvider stopwatchExtensionAvailabilityProvider
*/
public function testStopwatchExtensionAvailability($debug, $stopwatchEnabled, $expected)
{
$container = $this->createContainer();
$container->setParameter('kernel.debug', $debug);
if ($stopwatchEnabled) {
$container->register('debug.stopwatch', 'Symfony\Component\Stopwatch\Stopwatch');
}
$container->registerExtension(new TwigExtension());
$container->loadFromExtension('twig', array());
$this->compileContainer($container);
$tokenParsers = $container->get('twig.extension.debug.stopwatch')->getTokenParsers();
$stopwatchIsAvailable = new \ReflectionProperty($tokenParsers[0], 'stopwatchIsAvailable');
$stopwatchIsAvailable->setAccessible(true);
$this->assertSame($expected, $stopwatchIsAvailable->getValue($tokenParsers[0]));
}
public function stopwatchExtensionAvailabilityProvider()
{
return array(
'debug-and-stopwatch-enabled' => array(true, true, true),
'only-stopwatch-enabled' => array(false, true, false),
'only-debug-enabled' => array(true, false, false),
'debug-and-stopwatch-disabled' => array(false, false, false),
);
}
public function testRuntimeLoader()
{
$container = $this->createContainer();
$container->registerExtension(new TwigExtension());
$container->loadFromExtension('twig', array());
$container->setParameter('kernel.environment', 'test');
$container->setParameter('debug.file_link_format', 'test');
$container->setParameter('foo', 'FooClass');
$container->register('http_kernel', 'FooClass');
$container->register('templating.locator', 'FooClass');
$container->register('templating.name_parser', 'FooClass');
$container->register('foo', '%foo%')->addTag('twig.runtime');
$container->addCompilerPass(new RuntimeLoaderPass(), PassConfig::TYPE_BEFORE_REMOVING);
$container->getCompilerPassConfig()->setRemovingPasses(array());
$container->compile();
$loader = $container->getDefinition('twig.runtime_loader');
$args = $loader->getArgument(1);
$this->assertArrayHasKey('Symfony\Bridge\Twig\Form\TwigRenderer', $args);
$this->assertArrayHasKey('FooClass', $args);
$this->assertContains('twig.form.renderer', $args);
$this->assertContains('foo', $args);
}
private function createContainer()
{
$container = new ContainerBuilder(new ParameterBag(array(
'kernel.cache_dir' => __DIR__,
'kernel.root_dir' => __DIR__.'/Fixtures',
'kernel.charset' => 'UTF-8',
'kernel.debug' => false,
'kernel.bundles' => array(
'TwigBundle' => 'Symfony\\Bundle\\TwigBundle\\TwigBundle',
'ChildTwigBundle' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildTwigBundle\\ChildTwigBundle',
'ChildChildTwigBundle' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildTwigBundle\\ChildChildTwigBundle',
'ChildChildChildTwigBundle' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildChildTwigBundle\\ChildChildChildTwigBundle',
'ChildChildChildChildTwigBundle' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildChildChildTwigBundle\\ChildChildChildChildTwigBundle',
),
'kernel.bundles_metadata' => array(
'ChildChildChildChildTwigBundle' => array(
'namespace' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildChildChildTwigBundle\\ChildChildChildChildTwigBundle',
'parent' => 'ChildChildChildTwigBundle',
'path' => __DIR__.'/Fixtures/Bundle/ChildChildChildChildTwigBundle',
),
'TwigBundle' => array(
'namespace' => 'Symfony\\Bundle\\TwigBundle',
'parent' => null,
'path' => realpath(__DIR__.'/../..'),
),
'ChildTwigBundle' => array(
'namespace' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildTwigBundle\\ChildTwigBundle',
'parent' => 'TwigBundle',
'path' => __DIR__.'/Fixtures/Bundle/ChildTwigBundle',
),
'ChildChildChildTwigBundle' => array(
'namespace' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildChildTwigBundle\\ChildChildChildTwigBundle',
'parent' => 'ChildChildTwigBundle',
'path' => __DIR__.'/Fixtures/Bundle/ChildChildChildTwigBundle',
),
'ChildChildTwigBundle' => array(
'namespace' => 'Symfony\\Bundle\\TwigBundle\\Tests\\DependencyInjection\\Fixtures\\Bundle\\ChildChildTwigBundle\\ChildChildTwigBundle',
'parent' => 'ChildTwigBundle',
'path' => __DIR__.'/Fixtures/Bundle/ChildChildTwigBundle',
),
),
)));
return $container;
}
private function compileContainer(ContainerBuilder $container)
{
$container->getCompilerPassConfig()->setOptimizationPasses(array());
$container->getCompilerPassConfig()->setRemovingPasses(array());
$container->compile();
}
private function loadFromFile(ContainerBuilder $container, $file, $format)
{
$locator = new FileLocator(__DIR__.'/Fixtures/'.$format);
switch ($format) {
case 'php':
$loader = new PhpFileLoader($container, $locator);
break;
case 'xml':
$loader = new XmlFileLoader($container, $locator);
break;
case 'yml':
$loader = new YamlFileLoader($container, $locator);
break;
default:
throw new \InvalidArgumentException(sprintf('Unsupported format: %s', $format));
}
$loader->load($file.'.'.$format);
}
}

View File

@@ -0,0 +1 @@
{# Twig template #}

View File

@@ -0,0 +1 @@
{# Twig template #}

View File

@@ -0,0 +1 @@
{# Twig template #}

View File

@@ -0,0 +1 @@
{# Twig template #}

View File

@@ -0,0 +1,118 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\TwigBundle\TwigBundle;
class CacheWarmingTest extends TestCase
{
public function testCacheIsProperlyWarmedWhenTemplatingIsAvailable()
{
$kernel = new CacheWarmingKernel(true);
$kernel->boot();
$warmer = $kernel->getContainer()->get('cache_warmer');
$warmer->enableOptionalWarmers();
$warmer->warmUp($kernel->getCacheDir());
$this->assertFileExists($kernel->getCacheDir().'/twig');
}
public function testCacheIsProperlyWarmedWhenTemplatingIsDisabled()
{
$kernel = new CacheWarmingKernel(false);
$kernel->boot();
$warmer = $kernel->getContainer()->get('cache_warmer');
$warmer->enableOptionalWarmers();
$warmer->warmUp($kernel->getCacheDir());
$this->assertFileExists($kernel->getCacheDir().'/twig');
}
protected function setUp()
{
$this->deleteTempDir();
}
protected function tearDown()
{
$this->deleteTempDir();
}
private function deleteTempDir()
{
if (!file_exists($dir = sys_get_temp_dir().'/'.Kernel::VERSION.'/CacheWarmingKernel')) {
return;
}
$fs = new Filesystem();
$fs->remove($dir);
}
}
class CacheWarmingKernel extends Kernel
{
private $withTemplating;
public function __construct($withTemplating)
{
$this->withTemplating = $withTemplating;
parent::__construct(($withTemplating ? 'with' : 'without').'_templating', true);
}
public function getName()
{
return 'CacheWarming';
}
public function registerBundles()
{
return array(new FrameworkBundle(), new TwigBundle());
}
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(function ($container) {
$container->loadFromExtension('framework', array(
'secret' => '$ecret',
'form' => array('enabled' => false),
));
});
if ($this->withTemplating) {
$loader->load(function ($container) {
$container->loadFromExtension('framework', array(
'secret' => '$ecret',
'templating' => array('engines' => array('twig')),
'router' => array('resource' => '%kernel.root_dir%/Resources/config/empty_routing.yml'),
'form' => array('enabled' => false),
));
});
}
}
public function getCacheDir()
{
return sys_get_temp_dir().'/'.Kernel::VERSION.'/CacheWarmingKernel/cache/'.$this->environment;
}
public function getLogDir()
{
return sys_get_temp_dir().'/'.Kernel::VERSION.'/CacheWarmingKernel/logs';
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\TwigBundle\TwigBundle;
class NoTemplatingEntryTest extends TestCase
{
public function test()
{
$kernel = new NoTemplatingEntryKernel('dev', true);
$kernel->boot();
$container = $kernel->getContainer();
$content = $container->get('twig')->render('index.html.twig');
$this->assertContains('{ a: b }', $content);
}
protected function setUp()
{
$this->deleteTempDir();
}
protected function tearDown()
{
$this->deleteTempDir();
}
protected function deleteTempDir()
{
if (!file_exists($dir = sys_get_temp_dir().'/'.Kernel::VERSION.'/NoTemplatingEntryKernel')) {
return;
}
$fs = new Filesystem();
$fs->remove($dir);
}
}
class NoTemplatingEntryKernel extends Kernel
{
public function registerBundles()
{
return array(new FrameworkBundle(), new TwigBundle());
}
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(function ($container) {
$container->loadFromExtension('framework', array(
'secret' => '$ecret',
'form' => array('enabled' => false),
));
});
}
public function getCacheDir()
{
return sys_get_temp_dir().'/'.Kernel::VERSION.'/NoTemplatingEntryKernel/cache/'.$this->environment;
}
public function getLogDir()
{
return sys_get_temp_dir().'/'.Kernel::VERSION.'/NoTemplatingEntryKernel/logs';
}
}

View File

@@ -0,0 +1 @@
{{ {a: 'b'}|yaml_encode }}

View File

@@ -0,0 +1,131 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests\Loader;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
use Symfony\Bundle\TwigBundle\Loader\FilesystemLoader;
use Symfony\Bundle\TwigBundle\Tests\TestCase;
class FilesystemLoaderTest extends TestCase
{
public function testGetSourceContext()
{
$parser = $this->getMockBuilder('Symfony\Component\Templating\TemplateNameParserInterface')->getMock();
$locator = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
$locator
->expects($this->once())
->method('locate')
->will($this->returnValue(__DIR__.'/../DependencyInjection/Fixtures/Resources/views/layout.html.twig'))
;
$loader = new FilesystemLoader($locator, $parser);
$loader->addPath(__DIR__.'/../DependencyInjection/Fixtures/Resources/views', 'namespace');
// Twig-style
$this->assertEquals("This is a layout\n", $loader->getSourceContext('@namespace/layout.html.twig')->getCode());
// Symfony-style
$this->assertEquals("This is a layout\n", $loader->getSourceContext('TwigBundle::layout.html.twig')->getCode());
}
public function testExists()
{
// should return true for templates that Twig does not find, but Symfony does
$parser = $this->getMockBuilder('Symfony\Component\Templating\TemplateNameParserInterface')->getMock();
$locator = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
$locator
->expects($this->once())
->method('locate')
->will($this->returnValue($template = __DIR__.'/../DependencyInjection/Fixtures/Resources/views/layout.html.twig'))
;
$loader = new FilesystemLoader($locator, $parser);
$this->assertTrue($loader->exists($template));
}
/**
* @expectedException \Twig\Error\LoaderError
*/
public function testTwigErrorIfLocatorThrowsInvalid()
{
$parser = $this->getMockBuilder('Symfony\Component\Templating\TemplateNameParserInterface')->getMock();
$parser
->expects($this->once())
->method('parse')
->with('name.format.engine')
->will($this->returnValue(new TemplateReference('', '', 'name', 'format', 'engine')))
;
$locator = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
$locator
->expects($this->once())
->method('locate')
->will($this->throwException(new \InvalidArgumentException('Unable to find template "NonExistent".')))
;
$loader = new FilesystemLoader($locator, $parser);
$loader->getCacheKey('name.format.engine');
}
/**
* @expectedException \Twig\Error\LoaderError
*/
public function testTwigErrorIfLocatorReturnsFalse()
{
$parser = $this->getMockBuilder('Symfony\Component\Templating\TemplateNameParserInterface')->getMock();
$parser
->expects($this->once())
->method('parse')
->with('name.format.engine')
->will($this->returnValue(new TemplateReference('', '', 'name', 'format', 'engine')))
;
$locator = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
$locator
->expects($this->once())
->method('locate')
->will($this->returnValue(false))
;
$loader = new FilesystemLoader($locator, $parser);
$loader->getCacheKey('name.format.engine');
}
/**
* @expectedException \Twig\Error\LoaderError
* @expectedExceptionMessageRegExp /Unable to find template "name\.format\.engine" \(looked into: .*Tests.Loader.\.\..DependencyInjection.Fixtures.Resources.views\)/
*/
public function testTwigErrorIfTemplateDoesNotExist()
{
$parser = $this->getMockBuilder('Symfony\Component\Templating\TemplateNameParserInterface')->getMock();
$locator = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
$loader = new FilesystemLoader($locator, $parser);
$loader->addPath(__DIR__.'/../DependencyInjection/Fixtures/Resources/views');
$method = new \ReflectionMethod('Symfony\Bundle\TwigBundle\Loader\FilesystemLoader', 'findTemplate');
$method->setAccessible(true);
$method->invoke($loader, 'name.format.engine');
}
public function testTwigSoftErrorIfTemplateDoesNotExist()
{
$parser = $this->getMockBuilder('Symfony\Component\Templating\TemplateNameParserInterface')->getMock();
$locator = $this->getMockBuilder('Symfony\Component\Config\FileLocatorInterface')->getMock();
$loader = new FilesystemLoader($locator, $parser);
$loader->addPath(__DIR__.'/../DependencyInjection/Fixtures/Resources/views');
$method = new \ReflectionMethod('Symfony\Bundle\TwigBundle\Loader\FilesystemLoader', 'findTemplate');
$method->setAccessible(true);
$this->assertFalse($method->invoke($loader, 'name.format.engine', false));
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests;
use Symfony\Bundle\TwigBundle\TemplateIterator;
class TemplateIteratorTest extends TestCase
{
public function testGetIterator()
{
$bundle = $this->getMockBuilder('Symfony\Component\HttpKernel\Bundle\BundleInterface')->getMock();
$bundle->expects($this->any())->method('getName')->will($this->returnValue('BarBundle'));
$bundle->expects($this->any())->method('getPath')->will($this->returnValue(__DIR__.'/Fixtures/templates/BarBundle'));
$kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\Kernel')->disableOriginalConstructor()->getMock();
$kernel->expects($this->any())->method('getBundles')->will($this->returnValue(array(
$bundle,
)));
$iterator = new TemplateIterator($kernel, __DIR__.'/Fixtures/templates', array(__DIR__.'/Fixtures/templates/Foo' => 'Foo'));
$sorted = iterator_to_array($iterator);
sort($sorted);
$this->assertEquals(
array(
'@Bar/index.html.twig',
'@Foo/index.html.twig',
'layout.html.twig',
'sub/sub.html.twig',
),
$sorted
);
}
}

View File

@@ -0,0 +1,18 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle\Tests;
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
class TestCase extends PHPUnitTestCase
{
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigEnvironmentPass;
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\TwigLoaderPass;
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\ExceptionListenerPass;
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\ExtensionPass;
use Symfony\Bundle\TwigBundle\DependencyInjection\Compiler\RuntimeLoaderPass;
/**
* Bundle.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TwigBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new ExtensionPass());
$container->addCompilerPass(new TwigEnvironmentPass());
$container->addCompilerPass(new TwigLoaderPass());
$container->addCompilerPass(new ExceptionListenerPass());
$container->addCompilerPass(new RuntimeLoaderPass(), PassConfig::TYPE_BEFORE_REMOVING);
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Bundle\TwigBundle;
use Symfony\Bridge\Twig\TwigEngine as BaseEngine;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Bundle\FrameworkBundle\Templating\TemplateReference;
use Symfony\Component\Templating\TemplateNameParserInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Config\FileLocatorInterface;
use Twig\Environment;
use Twig\Error\Error;
/**
* This engine renders Twig templates.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class TwigEngine extends BaseEngine implements EngineInterface
{
protected $locator;
public function __construct(Environment $environment, TemplateNameParserInterface $parser, FileLocatorInterface $locator)
{
parent::__construct($environment, $parser);
$this->locator = $locator;
}
/**
* {@inheritdoc}
*/
public function render($name, array $parameters = array())
{
try {
return parent::render($name, $parameters);
} catch (Error $e) {
if ($name instanceof TemplateReference && !method_exists($e, 'setSourceContext')) {
try {
// try to get the real name of the template where the error occurred
$name = $e->getTemplateName();
$path = (string) $this->locator->locate($this->parser->parse($name));
$e->setTemplateName($path);
} catch (\Exception $e2) {
}
}
throw $e;
}
}
/**
* {@inheritdoc}
*
* @throws Error if something went wrong like a thrown exception while rendering the template
*/
public function renderResponse($view, array $parameters = array(), Response $response = null)
{
if (null === $response) {
$response = new Response();
}
$response->setContent($this->render($view, $parameters));
return $response;
}
}

View File

@@ -0,0 +1,52 @@
{
"name": "symfony/twig-bundle",
"type": "symfony-bundle",
"description": "Symfony TwigBundle",
"keywords": [],
"homepage": "https://symfony.com",
"license": "MIT",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"require": {
"php": ">=5.5.9",
"symfony/config": "~3.2",
"symfony/twig-bridge": "^3.2.1",
"symfony/http-foundation": "~2.8|~3.0",
"symfony/http-kernel": "~2.8.16|~3.1.9|^3.2.2",
"twig/twig": "~1.34|~2.4"
},
"require-dev": {
"symfony/asset": "~2.8|~3.0",
"symfony/stopwatch": "~2.8|~3.0",
"symfony/dependency-injection": "~2.8|~3.0",
"symfony/expression-language": "~2.8|~3.0",
"symfony/finder": "~2.8|~3.0",
"symfony/form": "~2.8|~3.0",
"symfony/routing": "~2.8|~3.0",
"symfony/templating": "~2.8|~3.0",
"symfony/yaml": "~2.8|~3.0",
"symfony/framework-bundle": "^3.2.2",
"doctrine/annotations": "~1.0",
"doctrine/cache": "~1.0"
},
"autoload": {
"psr-4": { "Symfony\\Bundle\\TwigBundle\\": "" },
"exclude-from-classmap": [
"/Tests/"
]
},
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
}
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"
failOnRisky="true"
failOnWarning="true"
>
<php>
<ini name="error_reporting" value="-1" />
</php>
<testsuites>
<testsuite name="Symfony TwigBundle Test Suite">
<directory>./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>