Merge branch '3.3' into 3.4
* 3.3: [MonologBridge] Do not silence errors in ServerLogHandler::formatRecord bumped Symfony version to 3.3.3 updated VERSION for 3.3.2 updated CHANGELOG for 3.3.2 [HttpKernel][Debug] Fix missing trace on deprecations collected during bootstrapping & silenced errors
This commit is contained in:
commit
25f13686f9
@ -7,6 +7,13 @@ in 3.3 minor versions.
|
|||||||
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
|
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
|
||||||
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.3.0...v3.3.1
|
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v3.3.0...v3.3.1
|
||||||
|
|
||||||
|
* 3.3.2 (2017-06-06)
|
||||||
|
|
||||||
|
* bug #23073 [TwigBridge] Fix namespaced classes (ogizanagi)
|
||||||
|
* bug #23063 [Cache] Fix extensibility of TagAwareAdapter::TAGS_PREFIX (wucdbm)
|
||||||
|
* bug #22936 [Form] Mix attr option between guessed options and user options (yceruto)
|
||||||
|
* bug #22976 [DependencyInjection] Use more clear message when unused environment variables detected (voronkovich)
|
||||||
|
|
||||||
* 3.3.1 (2017-06-05)
|
* 3.3.1 (2017-06-05)
|
||||||
|
|
||||||
* bug #23067 [HttpFoundation][FrameworkBundle] Revert "trusted proxies" BC break (nicolas-grekas)
|
* bug #23067 [HttpFoundation][FrameworkBundle] Revert "trusted proxies" BC break (nicolas-grekas)
|
||||||
|
@ -51,9 +51,15 @@ class ServerLogHandler extends AbstractHandler
|
|||||||
if (!$this->socket = $this->socket ?: $this->createSocket()) {
|
if (!$this->socket = $this->socket ?: $this->createSocket()) {
|
||||||
return false === $this->bubble;
|
return false === $this->bubble;
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
restore_error_handler();
|
||||||
|
}
|
||||||
|
|
||||||
$recordFormatted = $this->formatRecord($record);
|
$recordFormatted = $this->formatRecord($record);
|
||||||
|
|
||||||
|
set_error_handler(self::class.'::nullErrorHandler');
|
||||||
|
|
||||||
|
try {
|
||||||
if (-1 === stream_socket_sendto($this->socket, $recordFormatted)) {
|
if (-1 === stream_socket_sendto($this->socket, $recordFormatted)) {
|
||||||
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
|
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);
|
||||||
|
|
||||||
|
@ -100,6 +100,8 @@ class ErrorHandler
|
|||||||
private static $stackedErrors = array();
|
private static $stackedErrors = array();
|
||||||
private static $stackedErrorLevels = array();
|
private static $stackedErrorLevels = array();
|
||||||
private static $toStringException = null;
|
private static $toStringException = null;
|
||||||
|
private static $silencedErrorCache = array();
|
||||||
|
private static $silencedErrorCount = 0;
|
||||||
private static $exitCode = 0;
|
private static $exitCode = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -407,7 +409,24 @@ class ErrorHandler
|
|||||||
$errorAsException = self::$toStringException;
|
$errorAsException = self::$toStringException;
|
||||||
self::$toStringException = null;
|
self::$toStringException = null;
|
||||||
} elseif (!$throw && !($type & $level)) {
|
} elseif (!$throw && !($type & $level)) {
|
||||||
$errorAsException = new SilencedErrorContext($type, $file, $line);
|
if (isset(self::$silencedErrorCache[$message])) {
|
||||||
|
$lightTrace = null;
|
||||||
|
$errorAsException = self::$silencedErrorCache[$message];
|
||||||
|
++$errorAsException->count;
|
||||||
|
} else {
|
||||||
|
$lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), $type, $file, $line, false) : array();
|
||||||
|
$errorAsException = new SilencedErrorContext($type, $file, $line, $lightTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (100 < ++self::$silencedErrorCount) {
|
||||||
|
self::$silencedErrorCache = $lightTrace = array();
|
||||||
|
self::$silencedErrorCount = 1;
|
||||||
|
}
|
||||||
|
self::$silencedErrorCache[$message] = $errorAsException;
|
||||||
|
|
||||||
|
if (null === $lightTrace) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($scope) {
|
if ($scope) {
|
||||||
$errorAsException = new ContextErrorException($logMessage, 0, $type, $file, $line, $context);
|
$errorAsException = new ContextErrorException($logMessage, 0, $type, $file, $line, $context);
|
||||||
@ -418,19 +437,7 @@ class ErrorHandler
|
|||||||
// Clean the trace by removing function arguments and the first frames added by the error handler itself.
|
// Clean the trace by removing function arguments and the first frames added by the error handler itself.
|
||||||
if ($throw || $this->tracedErrors & $type) {
|
if ($throw || $this->tracedErrors & $type) {
|
||||||
$backtrace = $backtrace ?: $errorAsException->getTrace();
|
$backtrace = $backtrace ?: $errorAsException->getTrace();
|
||||||
$lightTrace = $backtrace;
|
$lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw);
|
||||||
|
|
||||||
for ($i = 0; isset($backtrace[$i]); ++$i) {
|
|
||||||
if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
|
|
||||||
$lightTrace = array_slice($lightTrace, 1 + $i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!($throw || $this->scopedErrors & $type)) {
|
|
||||||
for ($i = 0; isset($lightTrace[$i]); ++$i) {
|
|
||||||
unset($lightTrace[$i]['args']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->traceReflector->setValue($errorAsException, $lightTrace);
|
$this->traceReflector->setValue($errorAsException, $lightTrace);
|
||||||
} else {
|
} else {
|
||||||
$this->traceReflector->setValue($errorAsException, array());
|
$this->traceReflector->setValue($errorAsException, array());
|
||||||
@ -687,4 +694,23 @@ class ErrorHandler
|
|||||||
new ClassNotFoundFatalErrorHandler(),
|
new ClassNotFoundFatalErrorHandler(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function cleanTrace($backtrace, $type, $file, $line, $throw)
|
||||||
|
{
|
||||||
|
$lightTrace = $backtrace;
|
||||||
|
|
||||||
|
for ($i = 0; isset($backtrace[$i]); ++$i) {
|
||||||
|
if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
|
||||||
|
$lightTrace = array_slice($lightTrace, 1 + $i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!($throw || $this->scopedErrors & $type)) {
|
||||||
|
for ($i = 0; isset($lightTrace[$i]); ++$i) {
|
||||||
|
unset($lightTrace[$i]['args']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $lightTrace;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,15 +18,20 @@ namespace Symfony\Component\Debug\Exception;
|
|||||||
*/
|
*/
|
||||||
class SilencedErrorContext implements \JsonSerializable
|
class SilencedErrorContext implements \JsonSerializable
|
||||||
{
|
{
|
||||||
|
public $count = 1;
|
||||||
|
|
||||||
private $severity;
|
private $severity;
|
||||||
private $file;
|
private $file;
|
||||||
private $line;
|
private $line;
|
||||||
|
private $trace;
|
||||||
|
|
||||||
public function __construct($severity, $file, $line)
|
public function __construct($severity, $file, $line, array $trace = array(), $count = 1)
|
||||||
{
|
{
|
||||||
$this->severity = $severity;
|
$this->severity = $severity;
|
||||||
$this->file = $file;
|
$this->file = $file;
|
||||||
$this->line = $line;
|
$this->line = $line;
|
||||||
|
$this->trace = $trace;
|
||||||
|
$this->count = $count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSeverity()
|
public function getSeverity()
|
||||||
@ -44,12 +49,19 @@ class SilencedErrorContext implements \JsonSerializable
|
|||||||
return $this->line;
|
return $this->line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTrace()
|
||||||
|
{
|
||||||
|
return $this->trace;
|
||||||
|
}
|
||||||
|
|
||||||
public function JsonSerialize()
|
public function JsonSerialize()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
'severity' => $this->severity,
|
'severity' => $this->severity,
|
||||||
'file' => $this->file,
|
'file' => $this->file,
|
||||||
'line' => $this->line,
|
'line' => $this->line,
|
||||||
|
'trace' => $this->trace,
|
||||||
|
'count' => $this->count,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,12 +221,17 @@ class ErrorHandlerTest extends TestCase
|
|||||||
|
|
||||||
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
|
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
|
||||||
|
|
||||||
$logArgCheck = function ($level, $message, $context) {
|
$line = null;
|
||||||
|
$logArgCheck = function ($level, $message, $context) use (&$line) {
|
||||||
$this->assertEquals('Notice: Undefined variable: undefVar', $message);
|
$this->assertEquals('Notice: Undefined variable: undefVar', $message);
|
||||||
$this->assertArrayHasKey('exception', $context);
|
$this->assertArrayHasKey('exception', $context);
|
||||||
$exception = $context['exception'];
|
$exception = $context['exception'];
|
||||||
$this->assertInstanceOf(SilencedErrorContext::class, $exception);
|
$this->assertInstanceOf(SilencedErrorContext::class, $exception);
|
||||||
$this->assertSame(E_NOTICE, $exception->getSeverity());
|
$this->assertSame(E_NOTICE, $exception->getSeverity());
|
||||||
|
$this->assertSame(__FILE__, $exception->getFile());
|
||||||
|
$this->assertSame($line, $exception->getLine());
|
||||||
|
$this->assertNotEmpty($exception->getTrace());
|
||||||
|
$this->assertSame(1, $exception->count);
|
||||||
};
|
};
|
||||||
|
|
||||||
$logger
|
$logger
|
||||||
@ -239,6 +244,7 @@ class ErrorHandlerTest extends TestCase
|
|||||||
$handler->setDefaultLogger($logger, E_NOTICE);
|
$handler->setDefaultLogger($logger, E_NOTICE);
|
||||||
$handler->screamAt(E_NOTICE);
|
$handler->screamAt(E_NOTICE);
|
||||||
unset($undefVar);
|
unset($undefVar);
|
||||||
|
$line = __LINE__ + 1;
|
||||||
@$undefVar++;
|
@$undefVar++;
|
||||||
|
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
|
@ -49,10 +49,8 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
|||||||
public function lateCollect()
|
public function lateCollect()
|
||||||
{
|
{
|
||||||
if (null !== $this->logger) {
|
if (null !== $this->logger) {
|
||||||
$this->data = $this->computeErrorsCount();
|
|
||||||
|
|
||||||
$containerDeprecationLogs = $this->getContainerDeprecationLogs();
|
$containerDeprecationLogs = $this->getContainerDeprecationLogs();
|
||||||
$this->data['deprecation_count'] += count($containerDeprecationLogs);
|
$this->data = $this->computeErrorsCount($containerDeprecationLogs);
|
||||||
$this->data['compiler_logs'] = $this->getContainerCompilerLogs();
|
$this->data['compiler_logs'] = $this->getContainerCompilerLogs();
|
||||||
$this->data['logs'] = $this->sanitizeLogs(array_merge($this->logger->getLogs(), $containerDeprecationLogs));
|
$this->data['logs'] = $this->sanitizeLogs(array_merge($this->logger->getLogs(), $containerDeprecationLogs));
|
||||||
$this->data = $this->cloneVar($this->data);
|
$this->data = $this->cloneVar($this->data);
|
||||||
@ -113,11 +111,10 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
|||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$stubs = array();
|
|
||||||
$bootTime = filemtime($file);
|
$bootTime = filemtime($file);
|
||||||
$logs = array();
|
$logs = array();
|
||||||
foreach (unserialize(file_get_contents($file)) as $log) {
|
foreach (unserialize(file_get_contents($file)) as $log) {
|
||||||
$log['context'] = array('exception' => new SilencedErrorContext($log['type'], $log['file'], $log['line']));
|
$log['context'] = array('exception' => new SilencedErrorContext($log['type'], $log['file'], $log['line'], $log['trace'], $log['count']));
|
||||||
$log['timestamp'] = $bootTime;
|
$log['timestamp'] = $bootTime;
|
||||||
$log['priority'] = 100;
|
$log['priority'] = 100;
|
||||||
$log['priorityName'] = 'DEBUG';
|
$log['priorityName'] = 'DEBUG';
|
||||||
@ -159,15 +156,34 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$message = $log['message'];
|
||||||
$exception = $log['context']['exception'];
|
$exception = $log['context']['exception'];
|
||||||
$errorId = md5("{$exception->getSeverity()}/{$exception->getLine()}/{$exception->getFile()}\0{$log['message']}", true);
|
|
||||||
|
if ($exception instanceof SilencedErrorContext) {
|
||||||
|
if (isset($silencedLogs[$h = spl_object_hash($exception)])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$silencedLogs[$h] = true;
|
||||||
|
|
||||||
|
if (!isset($sanitizedLogs[$message])) {
|
||||||
|
$sanitizedLogs[$message] = $log + array(
|
||||||
|
'errorCount' => 0,
|
||||||
|
'scream' => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$sanitizedLogs[$message]['errorCount'] += $exception->count;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$errorId = md5("{$exception->getSeverity()}/{$exception->getLine()}/{$exception->getFile()}\0{$message}", true);
|
||||||
|
|
||||||
if (isset($sanitizedLogs[$errorId])) {
|
if (isset($sanitizedLogs[$errorId])) {
|
||||||
++$sanitizedLogs[$errorId]['errorCount'];
|
++$sanitizedLogs[$errorId]['errorCount'];
|
||||||
} else {
|
} else {
|
||||||
$log += array(
|
$log += array(
|
||||||
'errorCount' => 1,
|
'errorCount' => 1,
|
||||||
'scream' => $exception instanceof SilencedErrorContext,
|
'scream' => false,
|
||||||
);
|
);
|
||||||
|
|
||||||
$sanitizedLogs[$errorId] = $log;
|
$sanitizedLogs[$errorId] = $log;
|
||||||
@ -196,8 +212,9 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function computeErrorsCount()
|
private function computeErrorsCount(array $containerDeprecationLogs)
|
||||||
{
|
{
|
||||||
|
$silencedLogs = array();
|
||||||
$count = array(
|
$count = array(
|
||||||
'error_count' => $this->logger->countErrors(),
|
'error_count' => $this->logger->countErrors(),
|
||||||
'deprecation_count' => 0,
|
'deprecation_count' => 0,
|
||||||
@ -220,14 +237,23 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isSilencedOrDeprecationErrorLog($log)) {
|
if ($this->isSilencedOrDeprecationErrorLog($log)) {
|
||||||
if ($log['context']['exception'] instanceof SilencedErrorContext) {
|
$exception = $log['context']['exception'];
|
||||||
++$count['scream_count'];
|
if ($exception instanceof SilencedErrorContext) {
|
||||||
|
if (isset($silencedLogs[$h = spl_object_hash($exception)])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$silencedLogs[$h] = true;
|
||||||
|
$count['scream_count'] += $exception->count;
|
||||||
} else {
|
} else {
|
||||||
++$count['deprecation_count'];
|
++$count['deprecation_count'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach ($containerDeprecationLogs as $deprecationLog) {
|
||||||
|
$count['deprecation_count'] += $deprecationLog['count'];
|
||||||
|
}
|
||||||
|
|
||||||
ksort($count['priorities']);
|
ksort($count['priorities']);
|
||||||
|
|
||||||
return $count;
|
return $count;
|
||||||
|
@ -545,11 +545,28 @@ abstract class Kernel implements KernelInterface, TerminableInterface
|
|||||||
return $previousHandler ? $previousHandler($type, $message, $file, $line) : false;
|
return $previousHandler ? $previousHandler($type, $message, $file, $line) : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$collectedLogs[] = array(
|
if (isset($collectedLogs[$message])) {
|
||||||
|
++$collectedLogs[$message]['count'];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||||
|
// Clean the trace by removing first frames added by the error handler itself.
|
||||||
|
for ($i = 0; isset($backtrace[$i]); ++$i) {
|
||||||
|
if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
|
||||||
|
$backtrace = array_slice($backtrace, 1 + $i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$collectedLogs[$message] = array(
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
'message' => $message,
|
'message' => $message,
|
||||||
'file' => $file,
|
'file' => $file,
|
||||||
'line' => $line,
|
'line' => $line,
|
||||||
|
'trace' => $backtrace,
|
||||||
|
'count' => 1,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -562,7 +579,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
|
|||||||
if ($this->debug) {
|
if ($this->debug) {
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
|
|
||||||
file_put_contents($this->getCacheDir().'/'.$class.'Deprecations.log', serialize($collectedLogs));
|
file_put_contents($this->getCacheDir().'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs)));
|
||||||
file_put_contents($this->getCacheDir().'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
|
file_put_contents($this->getCacheDir().'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,6 @@ class ExceptionCaster
|
|||||||
public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, $isNested)
|
public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, $isNested)
|
||||||
{
|
{
|
||||||
$sPrefix = "\0".SilencedErrorContext::class."\0";
|
$sPrefix = "\0".SilencedErrorContext::class."\0";
|
||||||
$xPrefix = "\0Exception\0";
|
|
||||||
|
|
||||||
if (!isset($a[$s = $sPrefix.'severity'])) {
|
if (!isset($a[$s = $sPrefix.'severity'])) {
|
||||||
return $a;
|
return $a;
|
||||||
@ -93,12 +92,17 @@ class ExceptionCaster
|
|||||||
$a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]);
|
$a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$trace = array(
|
$trace = array(array(
|
||||||
'file' => $a[$sPrefix.'file'],
|
'file' => $a[$sPrefix.'file'],
|
||||||
'line' => $a[$sPrefix.'line'],
|
'line' => $a[$sPrefix.'line'],
|
||||||
);
|
));
|
||||||
unset($a[$sPrefix.'file'], $a[$sPrefix.'line']);
|
|
||||||
$a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub(array($trace));
|
if (isset($a[$sPrefix.'trace'])) {
|
||||||
|
$trace = array_merge($trace, $a[$sPrefix.'trace']);
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($a[$sPrefix.'file'], $a[$sPrefix.'line'], $a[$sPrefix.'trace']);
|
||||||
|
$a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace);
|
||||||
|
|
||||||
return $a;
|
return $a;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user