feature #31398 [TwigBundle] Deprecating error templates for non-html formats and using ErrorRenderer as fallback (yceruto)

This PR was merged into the 4.4 branch.

Discussion
----------

[TwigBundle] Deprecating error templates for non-html formats and using ErrorRenderer as fallback

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

In the previous [PR](https://github.com/symfony/symfony/pull/31065) we created a new mechanism to render any PHP error/exception in a formatted string, which if the FB is enabled, would return an HTTP Response according to the preferred Request format (html, json, xml, txt, etc.), but when installing the TwigBundle this rendering mechanism is replaced by the current ExceptionController.

This ExceptionController allows us to render custom error pages based on Twig in many formats, just what is already supported with the new ErrorRenderer component, so let's deprecate this in favor of the native.

Commits
-------

bf0c24a634 Deprecating error templates for non-html formats and using ErrorRenderer
This commit is contained in:
Tobias Schultze 2019-07-24 06:43:54 +02:00
commit 4045a134df
58 changed files with 404 additions and 26 deletions

View File

@ -151,6 +151,74 @@ TwigBridge
* Deprecated to pass `$rootDir` and `$fileLinkFormatter` as 5th and 6th argument respectively to the
`DebugCommand::__construct()` method, swap the variables position.
TwigBundle
----------
* Deprecated default value `twig.controller.exception::showAction` of the `twig.exception_controller` configuration option,
set it to `null` instead. This will also change the default error response format according to https://tools.ietf.org/html/rfc7807
for `json`, `xml`, `atom` and `txt` formats:
Before:
```json
{
"error": {
"code": 404,
"message": "Sorry, the page you are looking for could not be found"
}
}
```
After:
```json
{
"title": "Not Found",
"status": 404,
"detail": "Sorry, the page you are looking for could not be found"
}
```
* Deprecated the `ExceptionController` and all built-in error templates, use the error renderer mechanism of the `ErrorRenderer` component
* Deprecated loading custom error templates in non-html formats. Custom HTML error pages based on Twig keep working as before:
Before (`templates/bundles/TwigBundle/Exception/error.jsonld.twig`):
```twig
{
"@id": "https://example.com",
"@type": "error",
"@context": {
"title": "{{ status_text }}",
"code": {{ status_code }},
"message": "{{ exception.message }}"
}
}
```
After (`App\ErrorRenderer\JsonLdErrorRenderer`):
```php
class JsonLdErrorRenderer implements ErrorRendererInterface
{
public static function getFormat(): string
{
return 'jsonld';
}
public function render(FlattenException $exception): string
{
return json_encode([
'@id' => 'https://example.com',
'@type' => 'error',
'@context' => [
'title' => $exception->getTitle(),
'code' => $exception->getStatusCode(),
'message' => $exception->getMessage(),
],
]);
}
}
```
Configure your rendering service tagging it with `error_renderer.renderer`.
Validator
---------

View File

@ -467,6 +467,8 @@ TwigBundle
* The default value (`false`) of the `twig.strict_variables` configuration option has been changed to `%kernel.debug%`.
* The `transchoice` tag and filter have been removed, use the `trans` ones instead with a `%count%` parameter.
* Removed support for legacy templates directories `src/Resources/views/` and `src/Resources/<BundleName>/views/`, use `templates/` and `templates/bundles/<BundleName>/` instead.
* The default value (`twig.controller.exception::showAction`) of the `twig.exception_controller` configuration option has been changed to `null`.
* Removed `ExceptionController` class and all built-in error templates
TwigBridge
----------

View File

@ -7,3 +7,4 @@ framework:
twig:
strict_variables: '%kernel.debug%'
exception_controller: ~

View File

@ -52,7 +52,7 @@
"symfony/stopwatch": "^3.4|^4.0|^5.0",
"symfony/translation": "^4.3|^5.0",
"symfony/templating": "^3.4|^4.0|^5.0",
"symfony/twig-bundle": "^3.4|^4.0|^5.0",
"symfony/twig-bundle": "^4.4|^5.0",
"symfony/validator": "^4.1|^5.0",
"symfony/var-dumper": "^4.3|^5.0",
"symfony/workflow": "^4.3|^5.0",
@ -80,6 +80,7 @@
"symfony/stopwatch": "<3.4",
"symfony/translation": "<4.3",
"symfony/twig-bridge": "<4.1.1",
"symfony/twig-bundle": "<4.4",
"symfony/validator": "<4.1",
"symfony/workflow": "<4.3"
},

View File

@ -70,6 +70,6 @@ class JsonLoginTest extends AbstractWebTestCase
$this->assertSame(400, $response->getStatusCode());
$this->assertSame('application/json', $response->headers->get('Content-Type'));
$this->assertArraySubset(['error' => ['code' => 400, 'message' => 'Bad Request']], json_decode($response->getContent(), true));
$this->assertArraySubset(['title' => 'Bad Request', 'status' => 400, 'detail' => 'Invalid JSON.'], json_decode($response->getContent(), true));
}
}

View File

@ -0,0 +1,37 @@
<?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\SecurityBundle\Tests\Functional\app;
use Symfony\Component\ErrorRenderer\ErrorRenderer;
use Symfony\Component\ErrorRenderer\ErrorRenderer\HtmlErrorRenderer;
use Symfony\Component\ErrorRenderer\ErrorRenderer\JsonErrorRenderer;
use Symfony\Component\ErrorRenderer\Exception\FlattenException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class ExceptionController
{
private $errorRenderer;
public function __construct()
{
$this->errorRenderer = new ErrorRenderer([
new HtmlErrorRenderer(),
new JsonErrorRenderer(),
]);
}
public function __invoke(Request $request, FlattenException $exception)
{
return new Response($this->errorRenderer->render($exception, $request->getPreferredFormat()), $exception->getStatusCode());
}
}

View File

@ -12,5 +12,4 @@
return [
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
];

View File

@ -1,5 +1,5 @@
imports:
- { resource: ./../config/default.yml }
- { resource: ./../config/framework.yml }
services:
Symfony\Component\Ldap\Ldap:
arguments: ['@Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']

View File

@ -11,10 +11,8 @@
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Bundle\SecurityBundle\SecurityBundle;
use Symfony\Bundle\TwigBundle\TwigBundle;
return [
new FrameworkBundle(),
new SecurityBundle(),
new TwigBundle(),
];

View File

@ -1,5 +1,5 @@
imports:
- { resource: ./../config/default.yml }
- { resource: ./../config/framework.yml }
services:
# alias the service so we can access it in the tests

View File

@ -2,3 +2,4 @@
twig:
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
exception_controller: Symfony\Bundle\SecurityBundle\Tests\Functional\app\ExceptionController

View File

@ -20,7 +20,7 @@
"ext-xml": "*",
"symfony/config": "^4.2|^5.0",
"symfony/dependency-injection": "^4.2|^5.0",
"symfony/http-kernel": "^4.3",
"symfony/http-kernel": "^4.4",
"symfony/security-core": "^4.3",
"symfony/security-csrf": "^4.2|^5.0",
"symfony/security-guard": "^4.2|^5.0",
@ -33,10 +33,10 @@
"symfony/css-selector": "^3.4|^4.0|^5.0",
"symfony/dom-crawler": "^3.4|^4.0|^5.0",
"symfony/form": "^3.4|^4.0|^5.0",
"symfony/framework-bundle": "^4.2|^5.0",
"symfony/framework-bundle": "^4.4|^5.0",
"symfony/http-foundation": "^3.4|^4.0|^5.0",
"symfony/translation": "^3.4|^4.0|^5.0",
"symfony/twig-bundle": "^4.2|^5.0",
"symfony/twig-bundle": "^4.4|^5.0",
"symfony/twig-bridge": "^3.4|^4.0|^5.0",
"symfony/process": "^3.4|^4.0|^5.0",
"symfony/validator": "^3.4|^4.0|^5.0",
@ -48,9 +48,9 @@
},
"conflict": {
"symfony/browser-kit": "<4.2",
"symfony/twig-bundle": "<4.2",
"symfony/twig-bundle": "<4.4",
"symfony/var-dumper": "<3.4",
"symfony/framework-bundle": "<4.2",
"symfony/framework-bundle": "<4.4",
"symfony/console": "<3.4"
},
"autoload": {

View File

@ -6,6 +6,9 @@ CHANGELOG
* marked the `TemplateIterator` as `internal`
* added HTML comment to beginning and end of `exception_full.html.twig`
* added a new `TwigHtmlErrorRenderer` for `html` format, integrated with the `ErrorRenderer` component
* deprecated `ExceptionController` class and all built-in error templates in favor of the new error renderer mechanism
* deprecated default value `twig.controller.exception::showAction` of `twig.exception_controller` configuration option, set it to `null` instead
4.2.0
-----

View File

@ -19,12 +19,16 @@ use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Loader\ExistsLoaderInterface;
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.4, use the ErrorRenderer component instead.', ExceptionController::class), E_USER_DEPRECATED);
/**
* ExceptionController renders error or exception pages for a given
* FlattenException.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Matthias Pigulla <mp@webfactory.de>
*
* @deprecated since Symfony 4.4, use the ErrorRenderer component instead.
*/
class ExceptionController
{

View File

@ -11,8 +11,10 @@
namespace Symfony\Bundle\TwigBundle\Controller;
use Symfony\Component\ErrorRenderer\ErrorRenderer;
use Symfony\Component\ErrorRenderer\Exception\FlattenException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
@ -26,16 +28,22 @@ class PreviewErrorController
{
protected $kernel;
protected $controller;
private $errorRenderer;
public function __construct(HttpKernelInterface $kernel, $controller)
public function __construct(HttpKernelInterface $kernel, $controller, ErrorRenderer $errorRenderer = null)
{
$this->kernel = $kernel;
$this->controller = $controller;
$this->errorRenderer = $errorRenderer;
}
public function previewErrorPageAction(Request $request, $code)
{
$exception = FlattenException::createFromThrowable(new \Exception('Something has intentionally gone wrong.'), $code);
$exception = FlattenException::createFromThrowable(new \Exception('Something has intentionally gone wrong.'), $code, ['X-Debug' => false]);
if (null === $this->controller && null !== $this->errorRenderer) {
return new Response($this->errorRenderer->render($exception, $request->getPreferredFormat()), $code);
}
/*
* This Request mimics the parameters set by

View File

@ -34,7 +34,13 @@ class Configuration implements ConfigurationInterface
$rootNode
->children()
->scalarNode('exception_controller')->defaultValue('twig.controller.exception::showAction')->end()
->scalarNode('exception_controller')
->defaultValue(static function () {
@trigger_error('Relying on the default value ("twig.controller.exception::showAction") of the "twig.exception_controller" configuration option is deprecated since Symfony 4.4, set it to "null" explicitly instead, which will be the new default in 5.0.', E_USER_DEPRECATED);
return 'twig.controller.exception::showAction';
})
->end()
->end()
;

View File

@ -0,0 +1,111 @@
<?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\ErrorRenderer;
use Symfony\Component\ErrorRenderer\ErrorRenderer\ErrorRendererInterface;
use Symfony\Component\ErrorRenderer\ErrorRenderer\HtmlErrorRenderer;
use Symfony\Component\ErrorRenderer\Exception\FlattenException;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Loader\ExistsLoaderInterface;
/**
* Provides the ability to render custom Twig-based HTML error pages
* in non-debug mode, otherwise falls back to HtmlErrorRenderer.
*
* @author Yonel Ceruto <yonelceruto@gmail.com>
*/
class TwigHtmlErrorRenderer implements ErrorRendererInterface
{
private $twig;
private $htmlErrorRenderer;
private $debug;
public function __construct(Environment $twig, HtmlErrorRenderer $htmlErrorRenderer, bool $debug = false)
{
$this->twig = $twig;
$this->htmlErrorRenderer = $htmlErrorRenderer;
$this->debug = $debug;
}
/**
* {@inheritdoc}
*/
public static function getFormat(): string
{
return 'html';
}
/**
* {@inheritdoc}
*/
public function render(FlattenException $exception): string
{
$debug = $this->debug && ($exception->getHeaders()['X-Debug'] ?? true);
if ($debug) {
return $this->htmlErrorRenderer->render($exception);
}
$template = $this->findTemplate($exception->getStatusCode());
if (null === $template) {
return $this->htmlErrorRenderer->render($exception);
}
return $this->twig->render($template, [
'legacy' => false, // to be removed in 5.0
'exception' => $exception,
'status_code' => $exception->getStatusCode(),
'status_text' => $exception->getTitle(),
]);
}
private function findTemplate(int $statusCode): ?string
{
$template = sprintf('@Twig/Exception/error%s.html.twig', $statusCode);
if ($this->templateExists($template)) {
return $template;
}
$template = '@Twig/Exception/error.html.twig';
if ($this->templateExists($template)) {
return $template;
}
return null;
}
/**
* To be removed in 5.0.
*
* Use instead:
*
* $this->twig->getLoader()->exists($template)
*/
private function templateExists(string $template): bool
{
$loader = $this->twig->getLoader();
if ($loader instanceof ExistsLoaderInterface || method_exists($loader, 'exists')) {
return $loader->exists($template);
}
try {
$loader->getSourceContext($template);
return true;
} catch (LoaderError $e) {
}
return false;
}
}

View File

@ -139,11 +139,13 @@
<service id="twig.controller.exception" class="Symfony\Bundle\TwigBundle\Controller\ExceptionController" public="true">
<argument type="service" id="twig" />
<argument>%kernel.debug%</argument>
<deprecated>The "%service_id%" service is deprecated since Symfony 4.4.</deprecated>
</service>
<service id="twig.controller.preview_error" class="Symfony\Bundle\TwigBundle\Controller\PreviewErrorController" public="true">
<argument type="service" id="http_kernel" />
<argument>%twig.exception_listener.controller%</argument>
<argument type="service" id="error_renderer" on-invalid="null" />
</service>
<service id="twig.configurator.environment" class="Symfony\Bundle\TwigBundle\DependencyInjection\Configurator\EnvironmentConfigurator">
@ -158,5 +160,12 @@
<service id="twig.runtime_loader" class="Twig\RuntimeLoader\ContainerRuntimeLoader">
<argument /> <!-- runtime locator -->
</service>
<service id="twig.error_renderer.html" class="Symfony\Bundle\TwigBundle\ErrorRenderer\TwigHtmlErrorRenderer">
<tag name="error_renderer.renderer" priority="1" />
<argument type="service" id="twig" />
<argument type="service" id="error_renderer.renderer.html" />
<argument>%kernel.debug%</argument>
</service>
</services>
</container>

View File

@ -1 +1,2 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
{{ include('@Twig/Exception/error.xml.twig') }}

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
/*
{{ status_code }} {{ status_text }}

View File

@ -1,3 +1,6 @@
{% if legacy is not defined or legacy %}
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
{% endif %}
<!DOCTYPE html>
<html>
<head>

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
/*
{{ status_code }} {{ status_text }}

View File

@ -1 +1,2 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
{{ { 'error': { 'code': status_code, 'message': status_text } }|json_encode|raw }}

View File

@ -1 +1,2 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
{{ include('@Twig/Exception/error.xml.twig') }}

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
Oops! An Error Occurred
=======================

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
<?xml version="1.0" encoding="{{ _charset }}" ?>
<error code="{{ status_code }}" message="{{ status_text }}" />

View File

@ -1 +1,2 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
{{ include('@Twig/Exception/exception.xml.twig', { exception: exception }) }}

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
/*
{{ include('@Twig/Exception/exception.txt.twig', { exception: exception }) }}
*/

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
/*
{{ include('@Twig/Exception/exception.txt.twig', { exception: exception }) }}
*/

View File

@ -1 +1,2 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
{{ { 'error': { 'code': status_code, 'message': status_text, 'exception': exception.toarray } }|json_encode|raw }}

View File

@ -1 +1,2 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
{{ include('@Twig/Exception/exception.xml.twig', { exception: exception }) }}

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
[exception] {{ status_code ~ ' | ' ~ status_text ~ ' | ' ~ exception.class }}
[message] {{ exception.message }}
{% for i, e in exception.toarray %}

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
<?xml version="1.0" encoding="{{ _charset }}" ?>
<error code="{{ status_code }}" message="{{ status_text }}">

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
<traces>
{% for trace in exception.trace %}
<trace>

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
{# This file is based on WebProfilerBundle/Resources/views/Profiler/base_js.html.twig.
If you make any change in this file, verify the same change is needed in the other file. #}
<script{% if csp_script_nonce is defined and csp_script_nonce %} nonce="{{ csp_script_nonce }}"{% endif %}>/*<![CDATA[*/

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
{# This file is based on WebProfilerBundle/Resources/views/Profiler/profiler.css.twig.
If you make any change in this file, verify the same change is needed in the other file. #}
:root {

View File

@ -1,3 +1,4 @@
{% deprecated 'The template "' ~ _self ~'" is deprecated since Symfony 4.4, will be removed in 5.0.' %}
{% block before_html %}{% endblock %}
<!DOCTYPE html>
<html>

View File

@ -18,6 +18,9 @@ use Symfony\Component\HttpFoundation\Request;
use Twig\Environment;
use Twig\Loader\ArrayLoader;
/**
* @group legacy
*/
class ExceptionControllerTest extends TestCase
{
public function testShowActionCanBeForcedToShowErrorPage()

View File

@ -21,6 +21,7 @@ class ConfigurationTest extends TestCase
{
$input = [
'strict_variables' => false, // to be removed in 5.0 relying on default
'exception_controller' => null, // to be removed in 5.0 relying on default
'form_themes' => ['form_div_layout.html.twig'],
];
@ -42,10 +43,23 @@ class ConfigurationTest extends TestCase
$this->assertFalse($config['strict_variables']);
}
/**
* @group legacy
* @expectedDeprecation Relying on the default value ("twig.controller.exception::showAction") of the "twig.exception_controller" configuration option is deprecated since Symfony 4.4, set it to "null" explicitly instead, which will be the new default in 5.0.
*/
public function testGetExceptionControllerDefault()
{
$processor = new Processor();
$config = $processor->processConfiguration(new Configuration(), [[]]);
$this->assertSame('twig.controller.exception::showAction', $config['exception_controller']);
}
public function testGlobalsAreNotNormalized()
{
$input = [
'strict_variables' => false, // to be removed in 5.0 relying on default
'exception_controller' => null, // to be removed in 5.0 relying on default
'globals' => ['some-global' => true],
];
@ -59,6 +73,7 @@ class ConfigurationTest extends TestCase
{
$input = [
'strict_variables' => false, // to be removed in 5.0 relying on default
'exception_controller' => null, // to be removed in 5.0 relying on default
'globals' => ['global' => ['some-key' => 'some-value']],
];

View File

@ -4,4 +4,5 @@ $container->loadFromExtension('twig', [
'autoescape_service' => 'my_project.some_bundle.template_escaping_guesser',
'autoescape_service_method' => 'guess',
'strict_variables' => false, // to be removed in 5.0 relying on default
'exception_controller' => null, // to be removed in 5.0 relying on default
]);

View File

@ -2,4 +2,5 @@
$container->loadFromExtension('twig', [
'strict_variables' => false, // to be removed in 5.0 relying on default
'exception_controller' => null, // to be removed in 5.0 relying on default
]);

View File

@ -12,4 +12,5 @@ $container->loadFromExtension('twig', [
'thousands_separator' => '.',
],
'strict_variables' => false, // to be removed in 5.0 relying on default
'exception_controller' => null, // to be removed in 5.0 relying on default
]);

View File

@ -17,6 +17,7 @@ $container->loadFromExtension('twig', [
'charset' => 'ISO-8859-1',
'debug' => true,
'strict_variables' => true,
'exception_controller' => null,
'default_path' => '%kernel.project_dir%/Fixtures/templates',
'paths' => [
'path1',

View File

@ -6,5 +6,5 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig https://symfony.com/schema/dic/twig/twig-1.0.xsd">
<twig:config autoescape-service="my_project.some_bundle.template_escaping_guesser" autoescape-service-method="guess" strict-variables="false" />
<twig:config autoescape-service="my_project.some_bundle.template_escaping_guesser" autoescape-service-method="guess" strict-variables="false" exception-controller="null" />
</container>

View File

@ -6,5 +6,5 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig https://symfony.com/schema/dic/twig/twig-1.0.xsd">
<twig:config strict-variables="false" />
<twig:config strict-variables="false" exception-controller="null" />
</container>

View File

@ -6,7 +6,7 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig https://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:config auto-reload="true" autoescape="true" base-template-class="stdClass" cache="/tmp" charset="ISO-8859-1" debug="true" strict-variables="true" exception-controller="null">
<twig:path namespace="namespace3">namespaced_path3</twig:path>
</twig:config>
</container>

View File

@ -5,7 +5,7 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig https://symfony.com/schema/dic/twig/twig-1.0.xsd">
<twig:config strict-variables="false">
<twig:config strict-variables="false" exception-controller="null">
<twig:date format="Y-m-d" interval-format="%d" timezone="Europe/Berlin" />
<twig:number-format decimals="2" decimal-point="," thousands-separator="." />
</twig:config>

View File

@ -6,7 +6,7 @@
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig https://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" default-path="%kernel.project_dir%/Fixtures/templates">
<twig:config auto-reload="true" autoescape="true" base-template-class="stdClass" cache="/tmp" charset="ISO-8859-1" debug="true" strict-variables="true" default-path="%kernel.project_dir%/Fixtures/templates" exception-controller="null">
<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>

View File

@ -2,3 +2,4 @@ twig:
autoescape_service: my_project.some_bundle.template_escaping_guesser
autoescape_service_method: guess
strict_variables: false # to be removed in 5.0 relying on default
exception_controller: ~ # to be removed in 5.0 relying on default

View File

@ -1,2 +1,3 @@
twig:
strict_variables: false # to be removed in 5.0 relying on default
exception_controller: ~ # to be removed in 5.0 relying on default

View File

@ -1,4 +1,5 @@
twig:
strict_variables: false # to be removed in 5.0 relying on default
exception_controller: ~ # to be removed in 5.0 relying on default
paths:
namespaced_path3: namespace3

View File

@ -1,5 +1,6 @@
twig:
strict_variables: false # to be removed in 5.0 relying on default
exception_controller: ~ # to be removed in 5.0 relying on default
date:
format: Y-m-d
interval_format: '%d'

View File

@ -14,6 +14,7 @@ twig:
debug: true
strict_variables: true
default_path: '%kernel.project_dir%/Fixtures/templates'
exception_controller: ~ # to be removed in 5.0 relying on default
paths:
path1: ''
path2: ''

View File

@ -31,6 +31,7 @@ class TwigExtensionTest extends TestCase
$container->registerExtension(new TwigExtension());
$container->loadFromExtension('twig', [
'strict_variables' => false, // to be removed in 5.0 relying on default
'exception_controller' => null, // to be removed in 5.0 relying on default
]);
$this->compileContainer($container);
@ -156,6 +157,7 @@ class TwigExtensionTest extends TestCase
$container->loadFromExtension('twig', [
'globals' => $globals,
'strict_variables' => false, // // to be removed in 5.0 relying on default
'exception_controller' => null, // to be removed in 5.0 relying on default
]);
$this->compileContainer($container);
@ -259,6 +261,7 @@ class TwigExtensionTest extends TestCase
$container->registerExtension(new TwigExtension());
$container->loadFromExtension('twig', [
'strict_variables' => false, // to be removed in 5.0 relying on default
'exception_controller' => null, // to be removed in 5.0 relying on default
]);
$container->setAlias('test.twig.extension.debug.stopwatch', 'twig.extension.debug.stopwatch')->setPublic(true);
$this->compileContainer($container);
@ -289,6 +292,7 @@ class TwigExtensionTest extends TestCase
$container->registerExtension(new TwigExtension());
$container->loadFromExtension('twig', [
'strict_variables' => false, // to be removed in 5.0 relying on default
'exception_controller' => null, // to be removed in 5.0 relying on default
]);
$container->setParameter('kernel.environment', 'test');
$container->setParameter('debug.file_link_format', 'test');

View File

@ -0,0 +1,73 @@
<?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\ErrorRenderer;
use PHPUnit\Framework\TestCase;
use Symfony\Bundle\TwigBundle\ErrorRenderer\TwigHtmlErrorRenderer;
use Symfony\Component\ErrorRenderer\ErrorRenderer\HtmlErrorRenderer;
use Symfony\Component\ErrorRenderer\Exception\FlattenException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Twig\Environment;
use Twig\Loader\ArrayLoader;
class TwigHtmlErrorRendererTest extends TestCase
{
public function testFallbackToNativeRendererIfDebugOn()
{
$exception = FlattenException::createFromThrowable(new \Exception());
$twig = $this->createMock(Environment::class);
$nativeRenderer = $this->createMock(HtmlErrorRenderer::class);
$nativeRenderer
->expects($this->once())
->method('render')
->with($exception)
;
(new TwigHtmlErrorRenderer($twig, $nativeRenderer, true))->render($exception);
}
public function testFallbackToNativeRendererIfCustomTemplateNotFound()
{
$exception = FlattenException::createFromThrowable(new NotFoundHttpException());
$twig = new Environment(new ArrayLoader([]));
$nativeRenderer = $this->createMock(HtmlErrorRenderer::class);
$nativeRenderer
->expects($this->once())
->method('render')
->with($exception)
;
(new TwigHtmlErrorRenderer($twig, $nativeRenderer, false))->render($exception);
}
public function testRenderCustomErrorTemplate()
{
$exception = FlattenException::createFromThrowable(new NotFoundHttpException());
$twig = new Environment(new ArrayLoader([
'@Twig/Exception/error404.html.twig' => '<h1>Page Not Found</h1>',
]));
$nativeRenderer = $this->createMock(HtmlErrorRenderer::class);
$nativeRenderer
->expects($this->never())
->method('render')
;
$content = (new TwigHtmlErrorRenderer($twig, $nativeRenderer, false))->render($exception);
$this->assertSame('<h1>Page Not Found</h1>', $content);
}
}

View File

@ -99,6 +99,7 @@ class CacheWarmingKernel extends Kernel
])
->loadFromExtension('twig', [ // to be removed in 5.0 relying on default
'strict_variables' => false,
'exception_controller' => null,
])
;
});

View File

@ -14,6 +14,8 @@ namespace Symfony\Bundle\TwigBundle\Tests\Functional;
use Symfony\Bundle\TwigBundle\Tests\TestCase;
use Symfony\Bundle\TwigBundle\TwigBundle;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\ErrorRenderer\ErrorRenderer;
use Symfony\Component\HttpKernel\Kernel;
class EmptyAppTest extends TestCase
@ -37,12 +39,13 @@ class EmptyAppKernel extends Kernel
public function registerContainerConfiguration(LoaderInterface $loader)
{
$loader->load(function ($container) {
$container
->loadFromExtension('twig', [ // to be removed in 5.0 relying on default
'strict_variables' => false,
])
;
$loader->load(static function (ContainerBuilder $container) {
$container->loadFromExtension('twig', [ // to be removed in 5.0 relying on default
'strict_variables' => false,
'exception_controller' => null,
]);
$container->register('error_renderer', ErrorRenderer::class);
$container->setParameter('debug.file_link_format', null);
});
}

View File

@ -68,6 +68,7 @@ class NoTemplatingEntryKernel extends Kernel
])
->loadFromExtension('twig', [
'strict_variables' => false, // to be removed in 5.0 relying on default
'exception_controller' => null, // to be removed in 5.0 relying on default
'default_path' => __DIR__.'/templates',
])
;