From 01b2f66a65bbf12a726d1ede144f6a99853a4e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Parmentier?= Date: Wed, 23 Nov 2016 18:18:18 +0100 Subject: [PATCH 01/14] Switch to NoRFCWarningsValidation --- .../Component/Validator/Constraints/EmailValidator.php | 4 ++-- .../Validator/Tests/Constraints/EmailValidatorTest.php | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Validator/Constraints/EmailValidator.php b/src/Symfony/Component/Validator/Constraints/EmailValidator.php index 614bf23240baf..2d779e68809b2 100644 --- a/src/Symfony/Component/Validator/Constraints/EmailValidator.php +++ b/src/Symfony/Component/Validator/Constraints/EmailValidator.php @@ -12,7 +12,7 @@ namespace Symfony\Component\Validator\Constraints; use Egulias\EmailValidator\Validation\EmailValidation; -use Egulias\EmailValidator\Validation\RFCValidation; +use Egulias\EmailValidator\Validation\NoRFCWarningsValidation; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Exception\RuntimeException; @@ -63,7 +63,7 @@ public function validate($value, Constraint $constraint) $strictValidator = new \Egulias\EmailValidator\EmailValidator(); - if (interface_exists(EmailValidation::class) && !$strictValidator->isValid($value, new RFCValidation())) { + if (interface_exists(EmailValidation::class) && !$strictValidator->isValid($value, new NoRFCWarningsValidation())) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $this->formatValue($value)) ->setCode(Email::INVALID_FORMAT_ERROR) diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php index bb69e694b3d9f..d8b1e3d6adb61 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php @@ -174,6 +174,7 @@ public function getInvalidEmailsForStrictChecks() array('test@email>'), array('test@email<'), array('test@email{'), + array(str_repeat('x', 254).'@example.com'), //email with warnings ); } From 0896b02be073b8fa60fe6e8b3cf5d4f81b748bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Thu, 24 Nov 2016 19:36:31 +0100 Subject: [PATCH 02/14] [Serializer] Remove unused GetSetMethodNormalizer::denormalize --- .../Normalizer/GetSetMethodNormalizer.php | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index a0dc515ca30c8..2a15d46d63b10 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -36,40 +36,6 @@ class GetSetMethodNormalizer extends AbstractObjectNormalizer { private static $setterAccessibleCache = array(); - /** - * {@inheritdoc} - * - * @throws RuntimeException - */ - public function denormalize($data, $class, $format = null, array $context = array()) - { - $allowedAttributes = $this->getAllowedAttributes($class, $context, true); - $normalizedData = $this->prepareForDenormalization($data); - - $reflectionClass = new \ReflectionClass($class); - $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes); - - $classMethods = get_class_methods($object); - foreach ($normalizedData as $attribute => $value) { - if ($this->nameConverter) { - $attribute = $this->nameConverter->denormalize($attribute); - } - - $allowed = $allowedAttributes === false || in_array($attribute, $allowedAttributes); - $ignored = in_array($attribute, $this->ignoredAttributes); - - if ($allowed && !$ignored) { - $setter = 'set'.ucfirst($attribute); - - if (in_array($setter, $classMethods) && !$reflectionClass->getMethod($setter)->isStatic()) { - $object->$setter($value); - } - } - } - - return $object; - } - /** * {@inheritdoc} */ From 02dc4be9e418b34ef97449a2fd604032a7ad9968 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 26 Nov 2016 21:16:08 -0800 Subject: [PATCH 03/14] bumped Symfony version to 3.2.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 0bd98579b5a7a..f1013f23c61b2 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,12 +58,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '3.2.0-RC2'; + const VERSION = '3.2.0-DEV'; const VERSION_ID = 30200; const MAJOR_VERSION = 3; const MINOR_VERSION = 2; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'RC2'; + const EXTRA_VERSION = 'DEV'; const END_OF_MAINTENANCE = '07/2017'; const END_OF_LIFE = '01/2018'; From 040da8116eb7d753cdeca0514ecbb3c6ffa14bc1 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sun, 27 Nov 2016 12:45:23 +0100 Subject: [PATCH 04/14] Fixed getRouteParams() when no parameters are available --- .../DataCollector/RequestDataCollector.php | 7 ++++++- .../DataCollector/RequestDataCollectorTest.php | 14 ++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php index 55c2f0e6f50a4..deafe10745732 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/RequestDataCollector.php @@ -276,8 +276,13 @@ public function getRouteParams() } $data = $this->data['request_attributes']['_route_params']; + $rawData = $data->getRawData(); + if (!isset($rawData[1])) { + return array(); + } + $params = array(); - foreach ($data->getRawData()[1] as $k => $v) { + foreach ($rawData[1] as $k => $v) { $params[$k] = $data->seek($k); } diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php index 245cd4c159693..0e4cfe0d6ba91 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/RequestDataCollectorTest.php @@ -59,6 +59,16 @@ public function testCollect() $this->assertSame('application/json', $c->getContentType()); } + public function testCollectWithoutRouteParams() + { + $request = $this->createRequest(array()); + + $c = new RequestDataCollector(); + $c->collect($request, $this->createResponse()); + + $this->assertEquals(array(), $c->getRouteParams()); + } + public function testKernelResponseDoesNotStartSession() { $kernel = $this->getMock(HttpKernelInterface::class); @@ -197,12 +207,12 @@ public function testItIgnoresInvalidCallables() $this->assertSame('n/a', $c->getController()); } - protected function createRequest() + protected function createRequest($routeParams = array('name' => 'foo')) { $request = Request::create('http://test.com/foo?bar=baz'); $request->attributes->set('foo', 'bar'); $request->attributes->set('_route', 'foobar'); - $request->attributes->set('_route_params', array('name' => 'foo')); + $request->attributes->set('_route_params', $routeParams); $request->attributes->set('resource', fopen(__FILE__, 'r')); $request->attributes->set('object', new \stdClass()); From 73cf7960297b2892d064717c5fe68c8dc0320c30 Mon Sep 17 00:00:00 2001 From: Jerzy Zawadzki Date: Sun, 27 Nov 2016 21:34:49 +0000 Subject: [PATCH 05/14] bug #20653 [WebProfilerBundle] Profiler includes ghost panels --- .../WebProfilerBundle/Resources/views/Profiler/layout.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig index bcf97ddcbfb46..a3d761deedba2 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/layout.html.twig @@ -38,7 +38,7 @@ {% for name, template in templates %} {% set menu -%} {% with { 'collector': profile.getcollector(name) } %} - {{ block('menu', template) }} + {{- block('menu', template) -}} {% endwith %} {%- endset %} {% if menu != '' %} From 828c76154fa338f49731edb85508a51afba2a899 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 28 Nov 2016 07:44:11 +0100 Subject: [PATCH 06/14] mark alias as private during creation --- .../DependencyInjection/FrameworkExtension.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index de71b26140ae7..9763442977ee1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -12,6 +12,7 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection; use Doctrine\Common\Annotations\Reader; +use Symfony\Component\DependencyInjection\Alias; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Definition; @@ -1044,8 +1045,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con foreach (array('doctrine', 'psr6', 'redis') as $name) { if (isset($config[$name = 'default_'.$name.'_provider'])) { - $container->setAlias('cache.'.$name, Compiler\CachePoolPass::getServiceProvider($container, $config[$name])); - $container->getAlias('cache.'.$name)->setPublic(false); + $container->setAlias('cache.'.$name, new Alias(Compiler\CachePoolPass::getServiceProvider($container, $config[$name]), false)); } } foreach (array('app', 'system') as $name) { From 56c8ff8b219cbb4d151f2459cb1c2b2718f6059a Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 28 Nov 2016 07:53:19 +0100 Subject: [PATCH 07/14] ensure the proper context for nested validations --- .../Validator/Context/ExecutionContext.php | 5 +++++ .../Tests/Validator/Abstract2Dot5ApiTest.php | 19 +++++++++++++++++++ .../RecursiveContextualValidator.php | 10 ++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/Symfony/Component/Validator/Context/ExecutionContext.php b/src/Symfony/Component/Validator/Context/ExecutionContext.php index dce975c2038e6..721766a253383 100644 --- a/src/Symfony/Component/Validator/Context/ExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/ExecutionContext.php @@ -287,6 +287,11 @@ public function getGroup() return $this->group; } + public function getConstraint() + { + return $this->constraint; + } + /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php b/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php index 27d5a25ce26a8..1e6f3403e7284 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/Abstract2Dot5ApiTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Tests\Validator; use Symfony\Component\Validator\Constraints\Callback; +use Symfony\Component\Validator\Constraints\Collection; use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\Traverse; @@ -720,4 +721,22 @@ public function testPassConstraintToViolation() $this->assertCount(1, $violations); $this->assertSame($constraint, $violations[0]->getConstraint()); } + + public function testCollectionConstraitViolationHasCorrectContext() + { + $data = array( + 'foo' => 'fooValue', + ); + + // Missing field must not be the first in the collection validation + $constraint = new Collection(array( + 'foo' => new NotNull(), + 'bar' => new NotNull(), + )); + + $violations = $this->validate($data, $constraint); + + $this->assertCount(1, $violations); + $this->assertSame($constraint, $violations[0]->getConstraint()); + } } diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index db5d8809690d1..bbd09b821241c 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -14,6 +14,7 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\ConstraintValidatorFactoryInterface; +use Symfony\Component\Validator\Context\ExecutionContext; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Exception\ConstraintDefinitionException; use Symfony\Component\Validator\Exception\NoSuchMetadataException; @@ -110,6 +111,11 @@ public function validate($value, $constraints = null, $groups = null) $previousMetadata = $this->context->getMetadata(); $previousPath = $this->context->getPropertyPath(); $previousGroup = $this->context->getGroup(); + $previousConstraint = null; + + if ($this->context instanceof ExecutionContext || method_exists($this->context, 'getConstraint')) { + $previousConstraint = $this->context->getConstraint(); + } // If explicit constraints are passed, validate the value against // those constraints @@ -138,6 +144,10 @@ public function validate($value, $constraints = null, $groups = null) $this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath); $this->context->setGroup($previousGroup); + if (null !== $previousConstraint) { + $this->context->setConstraint($previousConstraint); + } + return $this; } From be2b7df5c5181564cbe69de77fce4c2793659693 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sun, 27 Nov 2016 20:32:10 +0100 Subject: [PATCH 08/14] [VarDumper][HttpKernel] Enhance perf of ExceptionCaster & DataCollector --- .../DataCollector/DataCollector.php | 9 +- .../Component/VarDumper/Caster/Caster.php | 7 +- .../VarDumper/Caster/ExceptionCaster.php | 86 +++++++++++-------- .../VarDumper/Cloner/AbstractCloner.php | 5 +- .../Tests/Caster/ExceptionCasterTest.php | 10 +-- .../VarDumper/Tests/CliDumperTest.php | 2 +- .../VarDumper/Tests/Fixtures/Twig.php | 10 +-- 7 files changed, 76 insertions(+), 53 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php index 7bca1ddfe199f..9674e08bbbc05 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php @@ -42,6 +42,8 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable */ private $cloner; + private static $stubsCache = array(); + public function serialize() { return serialize($this->data); @@ -124,14 +126,17 @@ private function decorateVar($var) return $var; } if (is_string($var)) { + if (isset(self::$stubsCache[$var])) { + return self::$stubsCache[$var]; + } if (false !== strpos($var, '\\')) { $c = (false !== $i = strpos($var, '::')) ? substr($var, 0, $i) : $var; if (class_exists($c, false) || interface_exists($c, false) || trait_exists($c, false)) { - return new ClassStub($var); + return self::$stubsCache[$var] = new ClassStub($var); } } if (false !== strpos($var, DIRECTORY_SEPARATOR) && false === strpos($var, '://') && false === strpos($var, "\0") && is_file($var)) { - return new LinkStub($var); + return self::$stubsCache[$var] = new LinkStub($var); } } diff --git a/src/Symfony/Component/VarDumper/Caster/Caster.php b/src/Symfony/Component/VarDumper/Caster/Caster.php index dd955c4c475d6..5428fecc2b27b 100644 --- a/src/Symfony/Component/VarDumper/Caster/Caster.php +++ b/src/Symfony/Component/VarDumper/Caster/Caster.php @@ -57,15 +57,20 @@ public static function castObject($obj, \ReflectionClass $reflector) } if ($a) { + $combine = false; $p = array_keys($a); foreach ($p as $i => $k) { if (isset($k[0]) && "\0" !== $k[0] && !$reflector->hasProperty($k)) { + $combine = true; $p[$i] = self::PREFIX_DYNAMIC.$k; } elseif (isset($k[16]) && "\0" === $k[16] && 0 === strpos($k, "\0class@anonymous\0")) { + $combine = true; $p[$i] = "\0".$reflector->getParentClass().'@anonymous'.strrchr($k, "\0"); } } - $a = array_combine($p, $a); + if ($combine) { + $a = array_combine($p, $a); + } } return $a; diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php index 4e26b3a056c9b..aa32d8b90bb98 100644 --- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php @@ -41,6 +41,8 @@ class ExceptionCaster E_STRICT => 'E_STRICT', ); + private static $framesCache = array(); + public static function castError(\Error $e, array $a, Stub $stub, $isNested, $filter = 0) { return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter); @@ -142,43 +144,52 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is $prefix = Caster::PREFIX_VIRTUAL; if (isset($f['file'], $f['line'])) { - if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) { - $f['file'] = substr($f['file'], 0, -strlen($match[0])); - $f['line'] = (int) $match[1]; - } - $caller = isset($f['function']) ? sprintf('in %s() on line %d', (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'], $f['line']) : null; - $src = $f['line']; - $srcKey = $f['file']; - $ellipsis = explode(DIRECTORY_SEPARATOR, $srcKey); - $ellipsis = 3 < count($ellipsis) ? 2 + strlen(implode(array_slice($ellipsis, -2))) : 0; - - if (file_exists($f['file']) && 0 <= self::$srcContext) { - if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig_Template') && method_exists($f['class'], 'getDebugInfo')) { - $template = isset($f['object']) ? $f['object'] : unserialize(sprintf('O:%d:"%s":0:{}', strlen($f['class']), $f['class'])); - - $ellipsis = 0; - $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : ''); - $templateInfo = $template->getDebugInfo(); - if (isset($templateInfo[$f['line']])) { - $templatePath = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null; - - if ($templateSrc) { - $templateSrc = explode("\n", $templateSrc); - $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, $caller, 'twig', $templatePath); - $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; + $cacheKey = $f; + unset($cacheKey['object'], $cacheKey['args']); + $cacheKey[] = self::$srcContext; + $cacheKey = implode('-', $cacheKey); + + if (isset(self::$framesCache[$cacheKey])) { + $a[$prefix.'src'] = self::$framesCache[$cacheKey]; + } else { + if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) { + $f['file'] = substr($f['file'], 0, -strlen($match[0])); + $f['line'] = (int) $match[1]; + } + $caller = isset($f['function']) ? sprintf('in %s() on line %d', (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'], $f['line']) : null; + $src = $f['line']; + $srcKey = $f['file']; + $ellipsis = explode(DIRECTORY_SEPARATOR, $srcKey); + $ellipsis = 3 < count($ellipsis) ? 2 + strlen(implode(array_slice($ellipsis, -2))) : 0; + + if (file_exists($f['file']) && 0 <= self::$srcContext) { + if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig_Template') && method_exists($f['class'], 'getDebugInfo')) { + $template = isset($f['object']) ? $f['object'] : unserialize(sprintf('O:%d:"%s":0:{}', strlen($f['class']), $f['class'])); + + $ellipsis = 0; + $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : ''); + $templateInfo = $template->getDebugInfo(); + if (isset($templateInfo[$f['line']])) { + if (!method_exists($template, 'getSourceContext') || !file_exists($templatePath = $template->getSourceContext()->getPath())) { + $templatePath = null; + } + if ($templateSrc) { + $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, $caller, 'twig', $templatePath); + $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; + } } } - } - if ($srcKey == $f['file']) { - $src = self::extractSource(explode("\n", file_get_contents($f['file'])), $f['line'], self::$srcContext, $caller, 'php', $f['file']); - $srcKey .= ':'.$f['line']; - if ($ellipsis) { - $ellipsis += 1 + strlen($f['line']); + if ($srcKey == $f['file']) { + $src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, $caller, 'php', $f['file']); + $srcKey .= ':'.$f['line']; + if ($ellipsis) { + $ellipsis += 1 + strlen($f['line']); + } } } + $srcAttr = $ellipsis ? 'ellipsis='.$ellipsis : ''; + self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(array("\0~$srcAttr\0$srcKey" => $src)); } - $srcAttr = $ellipsis ? 'ellipsis='.$ellipsis : ''; - $a[$prefix.'src'] = new EnumStub(array("\0~$srcAttr\0$srcKey" => $src)); } unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']); @@ -232,14 +243,16 @@ private static function traceUnshift(&$trace, $class, $file, $line) )); } - private static function extractSource(array $srcArray, $line, $srcContext, $title, $lang, $file = null) + private static function extractSource($srcLines, $line, $srcContext, $title, $lang, $file = null) { + $srcLines = explode("\n", $srcLines); $src = array(); for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) { - $src[] = (isset($srcArray[$i]) ? $srcArray[$i] : '')."\n"; + $src[] = (isset($srcLines[$i]) ? $srcLines[$i] : '')."\n"; } + $srcLines = array(); $ltrim = 0; do { $pad = null; @@ -257,7 +270,6 @@ private static function extractSource(array $srcArray, $line, $srcContext, $titl } while (0 > $i && null !== $pad); --$ltrim; - $srcArray = array(); foreach ($src as $i => $c) { if ($ltrim) { @@ -274,9 +286,9 @@ private static function extractSource(array $srcArray, $line, $srcContext, $titl } } $c->attr['lang'] = $lang; - $srcArray[sprintf("\0~%d\0", $i + $line - $srcContext)] = $c; + $srcLines[sprintf("\0~%d\0", $i + $line - $srcContext)] = $c; } - return new EnumStub($srcArray); + return new EnumStub($srcLines); } } diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php index d49d2b545e1d1..9f8f76af049f1 100644 --- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php @@ -252,14 +252,15 @@ protected function castObject(Stub $stub, $isNested) new \ReflectionClass($class), array_reverse(array($class => $class) + class_parents($class) + class_implements($class) + array('*' => '*')), ); + $classInfo[1] = array_map('strtolower', $classInfo[1]); $this->classInfo[$class] = $classInfo; } - $a = $this->callCaster('Symfony\Component\VarDumper\Caster\Caster::castObject', $obj, $classInfo[0], null, $isNested); + $a = Caster::castObject($obj, $classInfo[0]); foreach ($classInfo[1] as $p) { - if (!empty($this->casters[$p = strtolower($p)])) { + if (!empty($this->casters[$p])) { foreach ($this->casters[$p] as $p) { $a = $this->callCaster($p, $obj, $a, $stub, $isNested); } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php index 1ecf4671e5fc9..f43c4d4f4693c 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php @@ -179,14 +179,14 @@ public function testFrameWithTwig() $f = array( new FrameStub(array( 'file' => dirname(__DIR__).'/Fixtures/Twig.php', - 'line' => 21, + 'line' => 20, 'class' => '__TwigTemplate_VarDumperFixture_u75a09', )), new FrameStub(array( 'file' => dirname(__DIR__).'/Fixtures/Twig.php', 'line' => 21, 'class' => '__TwigTemplate_VarDumperFixture_u75a09', - 'object' => new \__TwigTemplate_VarDumperFixture_u75a09(null, false), + 'object' => new \__TwigTemplate_VarDumperFixture_u75a09(null, __FILE__), )), ); @@ -195,10 +195,10 @@ public function testFrameWithTwig() 0 => { class: "__TwigTemplate_VarDumperFixture_u75a09" src: { - bar.twig:2: { + %sTwig.php:1: { + : : foo bar : twig source - : } } } @@ -208,7 +208,7 @@ class: "__TwigTemplate_VarDumperFixture_u75a09" %A } src: { - foo.twig:2: { + %sExceptionCasterTest.php:2: { : foo bar : twig source : diff --git a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php index eb13e727247eb..13629e9ff822b 100644 --- a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php @@ -271,7 +271,7 @@ public function testThrowingCaster() ⚠: Symfony\Component\VarDumper\Exception\ThrowingCasterException {{$r} #message: "Unexpected Exception thrown from a caster: Foobar" -trace: { - bar.twig:%d: { + %sTwig.php:2: { : foo bar : twig source : diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php index 9eaa39c88909c..bf64d85c1e920 100644 --- a/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php @@ -3,16 +3,16 @@ /* foo.twig */ class __TwigTemplate_VarDumperFixture_u75a09 extends Twig_Template { - private $filename; + private $path; - public function __construct(Twig_Environment $env = null, $filename = null) + public function __construct(Twig_Environment $env = null, $path = null) { if (null !== $env) { parent::__construct($env); } $this->parent = false; $this->blocks = array(); - $this->filename = $filename; + $this->path = $path; } protected function doDisplay(array $context, array $blocks = array()) @@ -28,11 +28,11 @@ public function getTemplateName() public function getDebugInfo() { - return array(21 => 2); + return array(20 => 1, 21 => 2); } public function getSourceContext() { - return new Twig_Source(" foo bar\n twig source\n\n", 'foo.twig', false === $this->filename ? null : ($this->filename ?: 'bar.twig')); + return new Twig_Source(" foo bar\n twig source\n\n", 'foo.twig', $this->path ?: __FILE__); } } From 6d1f1b5d4ade77b53de83064cecadfb01fb20eed Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 28 Nov 2016 20:51:39 +0100 Subject: [PATCH 09/14] [ClassLoader] Use only forward slashes in generated class map --- .../Component/ClassLoader/ClassCollectionLoader.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php index 593ed44ca0eb5..5d09848952e8e 100644 --- a/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php +++ b/src/Symfony/Component/ClassLoader/ClassCollectionLoader.php @@ -63,7 +63,7 @@ public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = throw new \RuntimeException(sprintf('Class Collection Loader was not able to create directory "%s"', $cacheDir)); } $cacheDir = rtrim(realpath($cacheDir) ?: $cacheDir, '/'.DIRECTORY_SEPARATOR); - $cache = $cacheDir.DIRECTORY_SEPARATOR.$name.$extension; + $cache = $cacheDir.'/'.$name.$extension; // auto-reload $reload = false; @@ -114,7 +114,7 @@ public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = REGEX; $dontInlineRegex = str_replace('.', $spacesRegex, $dontInlineRegex); - $cacheDir = explode(DIRECTORY_SEPARATOR, $cacheDir); + $cacheDir = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $cacheDir)); $files = array(); $content = ''; foreach (self::getOrderedClasses($classes) as $class) { @@ -126,7 +126,7 @@ public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = $c = file_get_contents($file); if (preg_match($dontInlineRegex, $c)) { - $file = explode(DIRECTORY_SEPARATOR, $file); + $file = explode('/', str_replace(DIRECTORY_SEPARATOR, '/', $file)); for ($i = 0; isset($file[$i], $cacheDir[$i]); ++$i) { if ($file[$i] !== $cacheDir[$i]) { @@ -134,11 +134,11 @@ public static function load($classes, $cacheDir, $name, $autoReload, $adaptive = } } if (1 >= $i) { - $file = var_export(implode(DIRECTORY_SEPARATOR, $file), true); + $file = var_export(implode('/', $file), true); } else { $file = array_slice($file, $i); - $file = str_repeat('..'.DIRECTORY_SEPARATOR, count($cacheDir) - $i).implode(DIRECTORY_SEPARATOR, $file); - $file = '__DIR__.'.var_export(DIRECTORY_SEPARATOR.$file, true); + $file = str_repeat('../', count($cacheDir) - $i).implode('/', $file); + $file = '__DIR__.'.var_export('/'.$file, true); } $c = "\nnamespace {require $file;}"; From ebc23cf222e132484aa0655aabd8a5d7c68e0177 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 29 Nov 2016 11:01:23 +0100 Subject: [PATCH 10/14] [VarDumper] Use default color for ellipsed namespaces/paths --- .../Resources/views/Profiler/profiler.css.twig | 1 + src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index 5846eb42ca8ce..bb5aea2f444ae 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -900,6 +900,7 @@ table.logs .metadata { #collector-content .sf-dump-note { color: #6897BB; } #collector-content .sf-dump-key { color: #789339; } #collector-content .sf-dump-ref { color: #6E6E6E; } +#collector-content .sf-dump-ellipsis { color: #CC7832; max-width: 100em; } #collector-content .sf-dump { margin: 0; diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php index c944184ae6af7..198ec106be998 100644 --- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php +++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php @@ -43,6 +43,7 @@ class HtmlDumper extends CliDumper 'meta' => 'color:#B729D9', 'key' => 'color:#56DB3A', 'index' => 'color:#1299DA', + 'ellipsis' => 'color:#FF8400', ); private $displayOptions = array( @@ -380,7 +381,7 @@ function isCtrlKey(e) { display: inline-block; overflow: visible; text-overflow: ellipsis; - width: 5em; + max-width: 5em; white-space: nowrap; overflow: hidden; vertical-align: top; From 609245e9536961fcaa5a871b46a7717fd1f9a639 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 28 Nov 2016 14:17:01 +0100 Subject: [PATCH 11/14] [Config] ConfigCache::isFresh() should return false on __PHP_Incomplete_Class --- src/Symfony/Component/Config/ConfigCache.php | 27 ++++++++++++++++++- .../Config/Tests/ConfigCacheTest.php | 9 +++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Config/ConfigCache.php b/src/Symfony/Component/Config/ConfigCache.php index cc99bc9211cde..a34328ee8f31f 100644 --- a/src/Symfony/Component/Config/ConfigCache.php +++ b/src/Symfony/Component/Config/ConfigCache.php @@ -85,8 +85,33 @@ public function isFresh() return false; } + $e = null; + $meta = false; $time = filemtime($this->file); - $meta = unserialize(file_get_contents($metadata)); + $signalingException = new \UnexpectedValueException(); + $prevUnserializeHandler = ini_set('unserialize_callback_func', ''); + $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context) use (&$prevErrorHandler, $signalingException) { + if (E_WARNING === $type && 'Class __PHP_Incomplete_Class has no unserializer' === $msg) { + throw $signalingException; + } + + return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false; + }); + + try { + $meta = unserialize(file_get_contents($metadata)); + } catch (\Error $e) { + } catch (\Exception $e) { + } + restore_error_handler(); + ini_set('unserialize_callback_func', $prevUnserializeHandler); + if (null !== $e && $e !== $signalingException) { + throw $e; + } + if (false === $meta) { + return false; + } + foreach ($meta as $resource) { if (!$resource->isFresh($time)) { return false; diff --git a/src/Symfony/Component/Config/Tests/ConfigCacheTest.php b/src/Symfony/Component/Config/Tests/ConfigCacheTest.php index f3f2a446a2bf6..ee30d0b394fdd 100644 --- a/src/Symfony/Component/Config/Tests/ConfigCacheTest.php +++ b/src/Symfony/Component/Config/Tests/ConfigCacheTest.php @@ -93,6 +93,15 @@ public function testCacheIsNotFreshIfOneOfTheResourcesIsNotFresh() $this->assertFalse($cache->isFresh()); } + public function testCacheIsNotFreshWhenUnserializeFails() + { + file_put_contents($this->metaFile, str_replace('FileResource', 'ClassNotHere', file_get_contents($this->metaFile))); + + $cache = new ConfigCache($this->cacheFile, true); + + $this->assertFalse($cache->isFresh()); + } + public function testWriteDumpsFile() { unlink($this->cacheFile); From a931002ca25b6e3c127dc4df027040a33e24b672 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 29 Nov 2016 18:09:44 +0100 Subject: [PATCH 12/14] [FrameworkBundle] Forbid env parameters in routing configuration --- .../Bundle/FrameworkBundle/Routing/Router.php | 4 ++++ .../FrameworkBundle/Tests/Routing/RouterTest.php | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php index afef4093db290..35bd5ef1efed0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php +++ b/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php @@ -146,6 +146,10 @@ private function resolve($value) return '%%'; } + if (preg_match('/^env\(\w+\)$/', $match[1])) { + throw new RuntimeException(sprintf('Using "%%%s%%" is not allowed in routing configuration.', $match[1])); + } + $resolved = $container->getParameter($match[1]); if (is_string($resolved) || is_numeric($resolved)) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php index 330e0659ca144..4727fc4b82f53 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RouterTest.php @@ -131,6 +131,20 @@ public function testPatternPlaceholders() ); } + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Using "%env(FOO)%" is not allowed in routing configuration. + */ + public function testEnvPlaceholders() + { + $routes = new RouteCollection(); + + $routes->add('foo', new Route('/%env(FOO)%')); + + $router = new Router($this->getServiceContainer($routes), 'foo'); + $router->getRouteCollection(); + } + public function testHostPlaceholders() { $routes = new RouteCollection(); From 7bfe723ba7223e3cec3f95b09d7cb7d5ca19fb9c Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 30 Nov 2016 09:45:53 +0100 Subject: [PATCH 13/14] updated CHANGELOG for 3.2.0 --- CHANGELOG-3.2.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG-3.2.md b/CHANGELOG-3.2.md index b2de770775c93..c892ebb6755b7 100644 --- a/CHANGELOG-3.2.md +++ b/CHANGELOG-3.2.md @@ -7,6 +7,17 @@ in 3.2 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.2.0...v3.2.1 +* 3.2.0 (2016-11-30) + + * bug #20687 [FrameworkBundle] Forbid env parameters in routing configuration (nicolas-grekas) + * bug #20607 [Validator] Bring egulias/email-validator ~2.0 to parity with ~1.2 (Lctrs) + * bug #20671 [Config] ConfigCache::isFresh() should return false when unserialize() fails (nicolas-grekas) + * bug #20679 [VarDumper] Use default color for ellipsed namespaces/paths (nicolas-grekas) + * bug #20676 [ClassLoader] Use only forward slashes in generated class map (nicolas-grekas) + * bug #20664 [Validator] ensure the proper context for nested validations (xabbuh) + * bug #20661 bug #20653 [WebProfilerBundle] Profiler includes ghost panels (jzawadzki) + * bug #20652 Fixed getRouteParams() when no parameters are available (wouterj) + * 3.2.0-RC2 (2016-11-27) * bug #20601 [FrameworkBundle] Don't rely on any parent definition for "cache.annotations" (nicolas-grekas) From df8b30b81387f5d4ac91b34689489e913986c826 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 30 Nov 2016 09:46:02 +0100 Subject: [PATCH 14/14] updated VERSION for 3.2.0 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index f1013f23c61b2..b76db82d81b7d 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,12 +58,12 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '3.2.0-DEV'; + const VERSION = '3.2.0'; const VERSION_ID = 30200; const MAJOR_VERSION = 3; const MINOR_VERSION = 2; const RELEASE_VERSION = 0; - const EXTRA_VERSION = 'DEV'; + const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '07/2017'; const END_OF_LIFE = '01/2018';