Merge branch '3.4'

* 3.4:
  [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
  [PropertyInfo] Made ReflectionExtractor's prefix lists instance variables
This commit is contained in:
Nicolas Grekas 2017-06-07 16:10:47 +02:00
commit 77639e2564
12 changed files with 342 additions and 47 deletions

View File

@ -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 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)
* bug #23067 [HttpFoundation][FrameworkBundle] Revert "trusted proxies" BC break (nicolas-grekas)

View File

@ -51,9 +51,15 @@ class ServerLogHandler extends AbstractHandler
if (!$this->socket = $this->socket ?: $this->createSocket()) {
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)) {
stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR);

View File

@ -100,6 +100,8 @@ class ErrorHandler
private static $stackedErrors = array();
private static $stackedErrorLevels = array();
private static $toStringException = null;
private static $silencedErrorCache = array();
private static $silencedErrorCount = 0;
private static $exitCode = 0;
/**
@ -407,7 +409,24 @@ class ErrorHandler
$errorAsException = self::$toStringException;
self::$toStringException = null;
} 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 {
if ($scope) {
$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.
if ($throw || $this->tracedErrors & $type) {
$backtrace = $backtrace ?: $errorAsException->getTrace();
$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']);
}
}
$lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw);
$this->traceReflector->setValue($errorAsException, $lightTrace);
} else {
$this->traceReflector->setValue($errorAsException, array());
@ -683,4 +690,23 @@ class ErrorHandler
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;
}
}

View File

@ -18,15 +18,20 @@ namespace Symfony\Component\Debug\Exception;
*/
class SilencedErrorContext implements \JsonSerializable
{
public $count = 1;
private $severity;
private $file;
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->file = $file;
$this->line = $line;
$this->trace = $trace;
$this->count = $count;
}
public function getSeverity()
@ -44,12 +49,19 @@ class SilencedErrorContext implements \JsonSerializable
return $this->line;
}
public function getTrace()
{
return $this->trace;
}
public function JsonSerialize()
{
return array(
'severity' => $this->severity,
'file' => $this->file,
'line' => $this->line,
'trace' => $this->trace,
'count' => $this->count,
);
}
}

View File

@ -221,12 +221,17 @@ class ErrorHandlerTest extends TestCase
$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->assertArrayHasKey('exception', $context);
$exception = $context['exception'];
$this->assertInstanceOf(SilencedErrorContext::class, $exception);
$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
@ -239,6 +244,7 @@ class ErrorHandlerTest extends TestCase
$handler->setDefaultLogger($logger, E_NOTICE);
$handler->screamAt(E_NOTICE);
unset($undefVar);
$line = __LINE__ + 1;
@$undefVar++;
restore_error_handler();

View File

@ -49,10 +49,8 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
public function lateCollect()
{
if (null !== $this->logger) {
$this->data = $this->computeErrorsCount();
$containerDeprecationLogs = $this->getContainerDeprecationLogs();
$this->data['deprecation_count'] += count($containerDeprecationLogs);
$this->data = $this->computeErrorsCount($containerDeprecationLogs);
$this->data['compiler_logs'] = $this->getContainerCompilerLogs();
$this->data['logs'] = $this->sanitizeLogs(array_merge($this->logger->getLogs(), $containerDeprecationLogs));
$this->data = $this->cloneVar($this->data);
@ -113,11 +111,10 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
return array();
}
$stubs = array();
$bootTime = filemtime($file);
$logs = array();
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['priority'] = 100;
$log['priorityName'] = 'DEBUG';
@ -159,15 +156,34 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
continue;
}
$message = $log['message'];
$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])) {
++$sanitizedLogs[$errorId]['errorCount'];
} else {
$log += array(
'errorCount' => 1,
'scream' => $exception instanceof SilencedErrorContext,
'scream' => false,
);
$sanitizedLogs[$errorId] = $log;
@ -196,8 +212,9 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
return false;
}
private function computeErrorsCount()
private function computeErrorsCount(array $containerDeprecationLogs)
{
$silencedLogs = array();
$count = array(
'error_count' => $this->logger->countErrors(),
'deprecation_count' => 0,
@ -220,14 +237,23 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
}
if ($this->isSilencedOrDeprecationErrorLog($log)) {
if ($log['context']['exception'] instanceof SilencedErrorContext) {
++$count['scream_count'];
$exception = $log['context']['exception'];
if ($exception instanceof SilencedErrorContext) {
if (isset($silencedLogs[$h = spl_object_hash($exception)])) {
continue;
}
$silencedLogs[$h] = true;
$count['scream_count'] += $exception->count;
} else {
++$count['deprecation_count'];
}
}
}
foreach ($containerDeprecationLogs as $deprecationLog) {
$count['deprecation_count'] += $deprecationLog['count'];
}
ksort($count['priorities']);
return $count;

View File

@ -488,11 +488,28 @@ abstract class Kernel implements KernelInterface, TerminableInterface
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,
'message' => $message,
'file' => $file,
'line' => $line,
'trace' => $backtrace,
'count' => 1,
);
});
}
@ -505,7 +522,7 @@ abstract class Kernel implements KernelInterface, TerminableInterface
if ($this->debug) {
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()) : '');
}
}

View File

@ -53,11 +53,35 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
*/
private $phpDocTypeHelper;
public function __construct(DocBlockFactoryInterface $docBlockFactory = null)
/**
* @var string[]
*/
private $mutatorPrefixes;
/**
* @var string[]
*/
private $accessorPrefixes;
/**
* @var string[]
*/
private $arrayMutatorPrefixes;
/**
* @param DocBlockFactoryInterface $docBlockFactory
* @param string[]|null $mutatorPrefixes
* @param string[]|null $accessorPrefixes
* @param string[]|null $arrayMutatorPrefixes
*/
public function __construct(DocBlockFactoryInterface $docBlockFactory = null, array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null)
{
$this->docBlockFactory = $docBlockFactory ?: DocBlockFactory::createInstance();
$this->contextFactory = new ContextFactory();
$this->phpDocTypeHelper = new PhpDocTypeHelper();
$this->mutatorPrefixes = null !== $mutatorPrefixes ? $mutatorPrefixes : ReflectionExtractor::$defaultMutatorPrefixes;
$this->accessorPrefixes = null !== $accessorPrefixes ? $accessorPrefixes : ReflectionExtractor::$defaultAccessorPrefixes;
$this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : ReflectionExtractor::$defaultArrayMutatorPrefixes;
}
/**
@ -137,7 +161,7 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
return;
}
if (!in_array($prefix, ReflectionExtractor::$arrayMutatorPrefixes)) {
if (!in_array($prefix, $this->arrayMutatorPrefixes)) {
return $types;
}
@ -217,7 +241,7 @@ class PhpDocExtractor implements PropertyDescriptionExtractorInterface, Property
*/
private function getDocBlockFromMethod($class, $ucFirstProperty, $type)
{
$prefixes = $type === self::ACCESSOR ? ReflectionExtractor::$accessorPrefixes : ReflectionExtractor::$mutatorPrefixes;
$prefixes = $type === self::ACCESSOR ? $this->accessorPrefixes : $this->mutatorPrefixes;
$prefix = null;
foreach ($prefixes as $prefix) {

View File

@ -31,21 +31,48 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
*
* @var string[]
*/
public static $mutatorPrefixes = array('add', 'remove', 'set');
public static $defaultMutatorPrefixes = array('add', 'remove', 'set');
/**
* @internal
*
* @var string[]
*/
public static $accessorPrefixes = array('is', 'can', 'get');
public static $defaultAccessorPrefixes = array('is', 'can', 'get');
/**
* @internal
*
* @var string[]
*/
public static $arrayMutatorPrefixes = array('add', 'remove');
public static $defaultArrayMutatorPrefixes = array('add', 'remove');
/**
* @var string[]
*/
private $mutatorPrefixes;
/**
* @var string[]
*/
private $accessorPrefixes;
/**
* @var string[]
*/
private $arrayMutatorPrefixes;
/**
* @param string[]|null $mutatorPrefixes
* @param string[]|null $accessorPrefixes
* @param string[]|null $arrayMutatorPrefixes
*/
public function __construct(array $mutatorPrefixes = null, array $accessorPrefixes = null, array $arrayMutatorPrefixes = null)
{
$this->mutatorPrefixes = null !== $mutatorPrefixes ? $mutatorPrefixes : self::$defaultMutatorPrefixes;
$this->accessorPrefixes = null !== $accessorPrefixes ? $accessorPrefixes : self::$defaultAccessorPrefixes;
$this->arrayMutatorPrefixes = null !== $arrayMutatorPrefixes ? $arrayMutatorPrefixes : self::$defaultArrayMutatorPrefixes;
}
/**
* {@inheritdoc}
@ -155,7 +182,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
return;
}
if (in_array($prefix, self::$arrayMutatorPrefixes)) {
if (in_array($prefix, $this->arrayMutatorPrefixes)) {
$type = new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), $type);
}
@ -247,7 +274,7 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
{
$ucProperty = ucfirst($property);
foreach (self::$accessorPrefixes as $prefix) {
foreach ($this->accessorPrefixes as $prefix) {
try {
$reflectionMethod = new \ReflectionMethod($class, $prefix.$ucProperty);
if ($reflectionMethod->isStatic()) {
@ -279,9 +306,9 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
$ucProperty = ucfirst($property);
$ucSingulars = (array) Inflector::singularize($ucProperty);
foreach (self::$mutatorPrefixes as $prefix) {
foreach ($this->mutatorPrefixes as $prefix) {
$names = array($ucProperty);
if (in_array($prefix, self::$arrayMutatorPrefixes)) {
if (in_array($prefix, $this->arrayMutatorPrefixes)) {
$names = array_merge($names, $ucSingulars);
}
@ -313,10 +340,10 @@ class ReflectionExtractor implements PropertyListExtractorInterface, PropertyTyp
*/
private function getPropertyName($methodName, array $reflectionProperties)
{
$pattern = implode('|', array_merge(self::$accessorPrefixes, self::$mutatorPrefixes));
$pattern = implode('|', array_merge($this->accessorPrefixes, $this->mutatorPrefixes));
if (preg_match('/^('.$pattern.')(.+)$/i', $methodName, $matches)) {
if (!in_array($matches[1], self::$arrayMutatorPrefixes)) {
if ('' !== $pattern && preg_match('/^('.$pattern.')(.+)$/i', $methodName, $matches)) {
if (!in_array($matches[1], $this->arrayMutatorPrefixes)) {
return $matches[2];
}

View File

@ -40,6 +40,26 @@ class PhpDocExtractorTest extends TestCase
$this->assertSame($longDescription, $this->extractor->getLongDescription('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
}
/**
* @dataProvider typesWithCustomPrefixesProvider
*/
public function testExtractTypesWithCustomPrefixes($property, array $type = null)
{
$customExtractor = new PhpDocExtractor(null, array('add', 'remove'), array('is', 'can'));
$this->assertEquals($type, $customExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
}
/**
* @dataProvider typesWithNoPrefixesProvider
*/
public function testExtractTypesWithNoPrefixes($property, array $type = null)
{
$noPrefixExtractor = new PhpDocExtractor(null, array(), array(), array());
$this->assertEquals($type, $noPrefixExtractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property));
}
public function typesProvider()
{
return array(
@ -75,6 +95,76 @@ class PhpDocExtractorTest extends TestCase
);
}
public function typesWithCustomPrefixesProvider()
{
return array(
array('foo', null, 'Short description.', 'Long description.'),
array('bar', array(new Type(Type::BUILTIN_TYPE_STRING)), 'This is bar', null),
array('baz', array(new Type(Type::BUILTIN_TYPE_INT)), 'Should be used.', null),
array('foo2', array(new Type(Type::BUILTIN_TYPE_FLOAT)), null, null),
array('foo3', array(new Type(Type::BUILTIN_TYPE_CALLABLE)), null, null),
array('foo4', array(new Type(Type::BUILTIN_TYPE_NULL)), null, null),
array('foo5', null, null, null),
array(
'files',
array(
new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'SplFileInfo')),
new Type(Type::BUILTIN_TYPE_RESOURCE),
),
null,
null,
),
array('bal', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')), null, null),
array('parent', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), null, null),
array('collection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null),
array('a', null, 'A.', null),
array('b', null, 'B.', null),
array('c', array(new Type(Type::BUILTIN_TYPE_BOOL, true)), null, null),
array('d', array(new Type(Type::BUILTIN_TYPE_BOOL)), null, null),
array('e', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_RESOURCE))), null, null),
array('f', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null),
array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null),
array('donotexist', null, null, null),
array('staticGetter', null, null, null),
array('staticSetter', null, null, null),
);
}
public function typesWithNoPrefixesProvider()
{
return array(
array('foo', null, 'Short description.', 'Long description.'),
array('bar', array(new Type(Type::BUILTIN_TYPE_STRING)), 'This is bar', null),
array('baz', array(new Type(Type::BUILTIN_TYPE_INT)), 'Should be used.', null),
array('foo2', array(new Type(Type::BUILTIN_TYPE_FLOAT)), null, null),
array('foo3', array(new Type(Type::BUILTIN_TYPE_CALLABLE)), null, null),
array('foo4', array(new Type(Type::BUILTIN_TYPE_NULL)), null, null),
array('foo5', null, null, null),
array(
'files',
array(
new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'SplFileInfo')),
new Type(Type::BUILTIN_TYPE_RESOURCE),
),
null,
null,
),
array('bal', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime')), null, null),
array('parent', array(new Type(Type::BUILTIN_TYPE_OBJECT, false, 'Symfony\Component\PropertyInfo\Tests\Fixtures\ParentDummy')), null, null),
array('collection', array(new Type(Type::BUILTIN_TYPE_ARRAY, false, null, true, new Type(Type::BUILTIN_TYPE_INT), new Type(Type::BUILTIN_TYPE_OBJECT, false, 'DateTime'))), null, null),
array('a', null, 'A.', null),
array('b', null, 'B.', null),
array('c', null, null, null),
array('d', null, null, null),
array('e', null, null, null),
array('f', null, null, null),
array('g', array(new Type(Type::BUILTIN_TYPE_ARRAY, true, null, true)), 'Nullable array.', null),
array('donotexist', null, null, null),
array('staticGetter', null, null, null),
array('staticSetter', null, null, null),
);
}
public function testReturnNullOnEmptyDocBlock()
{
$this->assertNull($this->extractor->getShortDescription(EmptyDocBlock::class, 'foo'));

View File

@ -60,6 +60,56 @@ class ReflectionExtractorTest extends TestCase
);
}
public function testGetPropertiesWithCustomPrefixes()
{
$customExtractor = new ReflectionExtractor(array('add', 'remove'), array('is', 'can'));
$this->assertSame(
array(
'bal',
'parent',
'collection',
'B',
'Guid',
'g',
'foo',
'foo2',
'foo3',
'foo4',
'foo5',
'files',
'c',
'd',
'e',
'f',
),
$customExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')
);
}
public function testGetPropertiesWithNoPrefixes()
{
$noPrefixExtractor = new ReflectionExtractor(array(), array(), array());
$this->assertSame(
array(
'bal',
'parent',
'collection',
'B',
'Guid',
'g',
'foo',
'foo2',
'foo3',
'foo4',
'foo5',
'files',
),
$noPrefixExtractor->getProperties('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy')
);
}
/**
* @dataProvider typesProvider
*/

View File

@ -83,7 +83,6 @@ class ExceptionCaster
public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, $isNested)
{
$sPrefix = "\0".SilencedErrorContext::class."\0";
$xPrefix = "\0Exception\0";
if (!isset($a[$s = $sPrefix.'severity'])) {
return $a;
@ -93,12 +92,17 @@ class ExceptionCaster
$a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]);
}
$trace = array(
$trace = array(array(
'file' => $a[$sPrefix.'file'],
'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;
}