diff --git a/CHANGELOG-3.3.md b/CHANGELOG-3.3.md index 8a6626da32..6c3cb3540f 100644 --- a/CHANGELOG-3.3.md +++ b/CHANGELOG-3.3.md @@ -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) diff --git a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php index fb258808f3..d1d4968df4 100644 --- a/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php +++ b/src/Symfony/Bridge/Monolog/Handler/ServerLogHandler.php @@ -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); diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 744492a905..6389b1c8c2 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -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; + } } diff --git a/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php b/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php index 0c3a0e1d9f..4be83491b9 100644 --- a/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php +++ b/src/Symfony/Component/Debug/Exception/SilencedErrorContext.php @@ -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, ); } } diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index 85d62eecea..723a4eb935 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -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(); diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php index 7ae1fbbd61..f45f99718d 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php @@ -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; diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 8d7eba0f00..3510cff875 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -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()) : ''); } } diff --git a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php index e90579efbb..f9fb721fb8 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/PhpDocExtractor.php @@ -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) { diff --git a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php index 87778caa6b..45e82de1e7 100644 --- a/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php +++ b/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php @@ -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]; } diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php index d0eb3eed06..8d1d9ab0ca 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractors/PhpDocExtractorTest.php @@ -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')); diff --git a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php b/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php index 4a2cf3fc83..a8f2bb7d08 100644 --- a/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php +++ b/src/Symfony/Component/PropertyInfo/Tests/Extractors/ReflectionExtractorTest.php @@ -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 */ diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php index f52972a0ef..f1f58c0a87 100644 --- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php @@ -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; }