Merge branch '2.8' into 3.3

* 2.8:
  [HttpFoundation] Support 0 bit netmask in IPv6 ()
  Set `width: auto` on WebProfiler toolbar's reset.
  [HttpKernel] Fix logging of post-terminate errors/exceptions
  [Debug] Fix catching fatal errors in case of nested error handlers
  Fix hidden currency element with Bootstrap 3 theme
This commit is contained in:
Fabien Potencier 2017-12-11 14:02:38 -08:00
commit 0a95168cd8
11 changed files with 176 additions and 37 deletions

View File

@ -20,16 +20,21 @@
{%- endblock %}
{% block money_widget -%}
<div class="input-group">
{% set append = money_pattern starts with '{{' %}
{% if not append %}
<span class="input-group-addon">{{ money_pattern|replace({ '{{ widget }}':''}) }}</span>
{% endif %}
{% set prepend = not (money_pattern starts with '{{') %}
{% set append = not (money_pattern ends with '}}') %}
{% if prepend or append %}
<div class="input-group">
{% if prepend %}
<span class="input-group-addon">{{ money_pattern|replace({ '{{ widget }}':''}) }}</span>
{% endif %}
{{- block('form_widget_simple') -}}
{% if append %}
<span class="input-group-addon">{{ money_pattern|replace({ '{{ widget }}':''}) }}</span>
{% endif %}
</div>
{% else %}
{{- block('form_widget_simple') -}}
{% if append %}
<span class="input-group-addon">{{ money_pattern|replace({ '{{ widget }}':''}) }}</span>
{% endif %}
</div>
{% endif %}
{%- endblock money_widget %}
{% block percent_widget -%}

View File

@ -41,6 +41,7 @@
box-sizing: content-box;
vertical-align: baseline;
letter-spacing: normal;
width: auto;
}
.sf-toolbarreset {

View File

@ -526,6 +526,7 @@ class ErrorHandler
$exception = new FatalThrowableError($exception);
}
$type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
$handlerException = null;
if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
if ($exception instanceof FatalErrorException) {
@ -560,18 +561,20 @@ class ErrorHandler
}
}
}
if (empty($this->exceptionHandler)) {
throw $exception; // Give back $exception to the native handler
}
try {
call_user_func($this->exceptionHandler, $exception);
if (null !== $this->exceptionHandler) {
return \call_user_func($this->exceptionHandler, $exception);
}
$handlerException = $handlerException ?: $exception;
} catch (\Exception $handlerException) {
} catch (\Throwable $handlerException) {
}
if (isset($handlerException)) {
$this->exceptionHandler = null;
$this->handleException($handlerException);
$this->exceptionHandler = null;
if ($exception === $handlerException) {
self::$reservedMemory = null; // Disable the fatal error handler
throw $exception; // Give back $exception to the native handler
}
$this->handleException($handlerException);
}
/**
@ -587,15 +590,30 @@ class ErrorHandler
return;
}
self::$reservedMemory = null;
$handler = self::$reservedMemory = null;
$handlers = array();
$handler = set_error_handler('var_dump');
$handler = is_array($handler) ? $handler[0] : null;
restore_error_handler();
while (!is_array($handler) || !$handler[0] instanceof self) {
$handler = set_exception_handler('var_dump');
restore_exception_handler();
if (!$handler instanceof self) {
if (!$handler) {
break;
}
restore_exception_handler();
array_unshift($handlers, $handler);
}
foreach ($handlers as $h) {
set_exception_handler($h);
}
if (!$handler) {
return;
}
if ($handler !== $h) {
$handler[0]->setExceptionHandler($h);
}
$handler = $handler[0];
$handlers = array();
if ($exit = null === $error) {
$error = error_get_last();

View File

@ -0,0 +1,36 @@
--TEST--
Test rethrowing in custom exception handler
--FILE--
<?php
namespace Symfony\Component\Debug;
$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
$vendor = dirname($vendor);
}
require $vendor.'/vendor/autoload.php';
if (true) {
class TestLogger extends \Psr\Log\AbstractLogger
{
public function log($level, $message, array $context = array())
{
echo $message, "\n";
}
}
}
set_exception_handler(function ($e) { echo 123; throw $e; });
ErrorHandler::register()->setDefaultLogger(new TestLogger());
ini_set('display_errors', 1);
throw new \Exception('foo');
?>
--EXPECTF--
Uncaught Exception: foo
123
Fatal error: Uncaught %s:25
Stack trace:
%a

View File

@ -0,0 +1,40 @@
--TEST--
Test catching fatal errors when handlers are nested
--FILE--
<?php
namespace Symfony\Component\Debug;
$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
$vendor = dirname($vendor);
}
require $vendor.'/vendor/autoload.php';
Debug::enable();
ini_set('display_errors', 0);
$eHandler = set_error_handler('var_dump');
$xHandler = set_exception_handler('var_dump');
var_dump(array(
$eHandler[0] === $xHandler[0] ? 'Error and exception handlers do match' : 'Error and exception handlers are different',
));
$eHandler[0]->setExceptionHandler('print_r');
if (true) {
class Broken implements \Serializable {};
}
?>
--EXPECTF--
array(1) {
[0]=>
string(37) "Error and exception handlers do match"
}
object(Symfony\Component\Debug\Exception\FatalErrorException)#4 (8) {
["message":protected]=>
string(199) "Error: Class Symfony\Component\Debug\Broken contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Serializable::serialize, Serializable::unserialize)"
%a
}

View File

@ -1914,6 +1914,25 @@ abstract class AbstractBootstrap3LayoutTest extends AbstractLayoutTest
);
}
public function testMoneyWithoutCurrency()
{
$form = $this->factory->createNamed('name', 'money', 1234.56, array(
'currency' => false,
));
$this->assertWidgetMatchesXpath($form->createView(), array('id' => 'my&id', 'attr' => array('class' => 'my&class')),
'/input
[@id="my&id"]
[@type="text"]
[@name="name"]
[@class="my&class form-control"]
[@value="1234.56"]
[not(preceding-sibling::*)]
[not(following-sibling::*)]
'
);
}
public function testNumber()
{
$form = $this->factory->createNamed('name', 'Symfony\Component\Form\Extension\Core\Type\NumberType', 1234.56);

View File

@ -43,7 +43,7 @@ class MoneyTypeTest extends BaseTypeTest
$view = $this->factory->create(static::TESTED_TYPE, null, array('currency' => 'JPY'))
->createView();
$this->assertTrue((bool) strstr($view->vars['money_pattern'], '¥'));
$this->assertSame('¥ {{ widget }}', $view->vars['money_pattern']);
}
// https://github.com/symfony/symfony/issues/5458
@ -62,4 +62,12 @@ class MoneyTypeTest extends BaseTypeTest
{
parent::testSubmitNull($expected, $norm, '');
}
public function testMoneyPatternWithoutCurrency()
{
$view = $this->factory->create(static::TESTED_TYPE, null, array('currency' => false))
->createView();
$this->assertSame('{{ widget }}', $view->vars['money_pattern']);
}
}

View File

@ -123,6 +123,10 @@ class IpUtils
if (false !== strpos($ip, '/')) {
list($address, $netmask) = explode('/', $ip, 2);
if ('0' === $netmask) {
return (bool) unpack('n*', @inet_pton($address));
}
if ($netmask < 1 || $netmask > 128) {
return self::$checkedIps[$cacheKey] = false;
}

View File

@ -62,6 +62,8 @@ class IpUtilsTest extends TestCase
array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'),
array(true, '0:0:0:0:0:0:0:1', '::1'),
array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'),
array(true, '0:0:603:0:396e:4789:8e99:0001', '::/0'),
array(true, '0:0:603:0:396e:4789:8e99:0001', '2a01:198:603:0::/0'),
array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '2a01:198:603:0::/65')),
array(true, '2a01:198:603:0:396e:4789:8e99:890f', array('2a01:198:603:0::/65', '::1')),
array(false, '2a01:198:603:0:396e:4789:8e99:890f', array('::1', '1a01:198:603:0::/65')),

View File

@ -37,6 +37,7 @@ class DebugHandlersListener implements EventSubscriberInterface
private $fileLinkFormat;
private $scope;
private $firstCall = true;
private $hasTerminatedWithException;
/**
* @param callable|null $exceptionHandler A handler that will be called on Exception
@ -63,14 +64,16 @@ class DebugHandlersListener implements EventSubscriberInterface
*/
public function configure(Event $event = null)
{
if (!$this->firstCall) {
if (!$event instanceof KernelEvent ? !$this->firstCall : !$event->isMasterRequest()) {
return;
}
$this->firstCall = false;
$this->firstCall = $this->hasTerminatedWithException = false;
$handler = set_exception_handler('var_dump');
$handler = is_array($handler) ? $handler[0] : null;
restore_exception_handler();
if ($this->logger || null !== $this->throwAt) {
$handler = set_error_handler('var_dump');
$handler = is_array($handler) ? $handler[0] : null;
restore_error_handler();
if ($handler instanceof ErrorHandler) {
if ($this->logger) {
$handler->setDefaultLogger($this->logger, $this->levels);
@ -99,8 +102,16 @@ class DebugHandlersListener implements EventSubscriberInterface
}
if (!$this->exceptionHandler) {
if ($event instanceof KernelEvent) {
if (method_exists($event->getKernel(), 'terminateWithException')) {
$this->exceptionHandler = array($event->getKernel(), 'terminateWithException');
if (method_exists($kernel = $event->getKernel(), 'terminateWithException')) {
$request = $event->getRequest();
$hasRun = &$this->hasTerminatedWithException;
$this->exceptionHandler = function (\Exception $e) use ($kernel, $request, &$hasRun) {
if ($hasRun) {
throw $e;
}
$hasRun = true;
$kernel->terminateWithException($e, $request);
};
}
} elseif ($event instanceof ConsoleEvent && $app = $event->getCommand()->getApplication()) {
$output = $event->getOutput();
@ -113,9 +124,6 @@ class DebugHandlersListener implements EventSubscriberInterface
}
}
if ($this->exceptionHandler) {
$handler = set_exception_handler('var_dump');
$handler = is_array($handler) ? $handler[0] : null;
restore_exception_handler();
if ($handler instanceof ErrorHandler) {
$h = $handler->setExceptionHandler('var_dump') ?: $this->exceptionHandler;
$handler->setExceptionHandler($h);

View File

@ -89,14 +89,12 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
}
/**
* @throws \LogicException If the request stack is empty
*
* @internal
*/
public function terminateWithException(\Exception $exception)
public function terminateWithException(\Exception $exception, Request $request = null)
{
if (!$request = $this->requestStack->getMasterRequest()) {
throw new \LogicException('Request stack is empty', 0, $exception);
if (!$request = $request ?: $this->requestStack->getMasterRequest()) {
throw $exception;
}
$response = $this->handleException($exception, $request, self::MASTER_REQUEST);