Merge branch '3.2'

* 3.2:
  [ClassLoader] Use only forward slashes in generated class map
  [VarDumper][HttpKernel] Enhance perf of ExceptionCaster & DataCollector
  ensure the proper context for nested validations
  bug #20653 [WebProfilerBundle] Profiler includes ghost panels
  Fixed getRouteParams() when no parameters are available
  bumped Symfony version to 3.2.0
  updated VERSION for 3.2.0-RC2
  updated CHANGELOG for 3.2.0-RC2
This commit is contained in:
Fabien Potencier 2016-11-29 09:26:23 +01:00
commit 8835522120
15 changed files with 160 additions and 61 deletions

View File

@ -7,6 +7,33 @@ 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-RC2 (2016-11-27)
* bug #20601 [FrameworkBundle] Don't rely on any parent definition for "cache.annotations" (nicolas-grekas)
* bug #20638 Fix legacy tests that do not trigger any depreciation (julienfalque)
* bug #20374 [FrameworkBundle] Improve performance of ControllerNameParser (enumag)
* bug #20474 [Routing] Fail properly when a route parameter name cannot be used as a PCRE subpattern name (fancyweb)
* bug #20616 [Bridge/Doctrine] Use cache.prefix.seed parameter for generating cache namespace (nicolas-grekas)
* bug #20566 [DI] Initialize properties before method calls (ro0NL)
* bug #20583 [Workflow] Fixed graphviz dumper for state machine (lyrixx)
* bug #20621 [HttpKernel] Fix exception when serializing request attributes (nicolas-grekas)
* bug #20609 [DI] Fixed custom services definition BC break introduced in ec7e70fb… (kiler129)
* bug #20598 [DI] Aliases should preserve the aliased invalid behavior (nicolas-grekas)
* bug #20600 [Process] Fix process continuing after reached timeout using getIterator() (chalasr)
* bug #20603 [HttpKernel] Deprecate checking for cacheable HTTP methods in Request::isMethodSafe() (nicolas-grekas)
* bug #20602 [HttpKernel] Revert BC breaking change of Request::isMethodSafe() (nicolas-grekas)
* bug #20610 [FrameworkBundle] Add framework.cache.prefix_seed for predictible cache key prefixes (nicolas-grekas)
* bug #20595 [WebProfilerBundle] Fix deprecated uses of profiler_dump (nicolas-grekas)
* bug #20589 [SecurityBundle] Fix FirewallConfig nullable arguments (ogizanagi)
* bug #20590 [DI] Allow null as default env value (sroze)
* bug #20499 [Doctrine][Form] support large integers (xabbuh)
* bug #20559 [FrameworkBundle] Avoid warming up the validator cache for non-existent class (Seldaek)
* bug #20576 [Process] Do feat test before enabling TTY mode (nicolas-grekas)
* bug #20577 [FrameworkBundle] Mark cache.default_*_provider services private (nicolas-grekas)
* bug #20550 [YAML] Fix processing timestamp strings with timezone (myesain)
* bug #20543 [DI] Fix error when trying to resolve a DefinitionDecorator (nicolas-grekas)
* bug #20544 [PhpUnitBridge] Fix time-sensitive tests that use data providers (julienfalque)
* 3.2.0-RC1 (2016-11-17)
* feature #20533 [DI] Revert "deprecate get() for uncompiled container builders" (nicolas-grekas)

View File

@ -114,7 +114,7 @@
{% for name, template in templates %}
{% set menu -%}
{% with { collector: profile.getcollector(name), profiler_markup_version: profiler_markup_version } %}
{{ block('menu', template) }}
{{- block('menu', template) -}}
{% endwith %}
{%- endset %}
{% if menu is not empty %}

View File

@ -60,7 +60,7 @@ class ClassCollectionLoader
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;
@ -140,7 +140,7 @@ class ClassCollectionLoader
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) {
@ -153,7 +153,7 @@ REGEX;
$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]) {
@ -161,11 +161,11 @@ REGEX;
}
}
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;}";

View File

@ -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 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
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);
}
}

View File

@ -276,8 +276,13 @@ class RequestDataCollector extends DataCollector implements EventSubscriberInter
}
$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);
}

View File

@ -59,6 +59,16 @@ class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase
$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 @@ class RequestDataCollectorTest extends \PHPUnit_Framework_TestCase
$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());

View File

@ -269,6 +269,11 @@ class ExecutionContext implements ExecutionContextInterface
return $this->group;
}
public function getConstraint()
{
return $this->constraint;
}
/**
* {@inheritdoc}
*/

View File

@ -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;
@ -651,4 +652,22 @@ abstract class AbstractTest extends AbstractValidatorTest
$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());
}
}

View File

@ -14,6 +14,7 @@ namespace Symfony\Component\Validator\Validator;
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 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
$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 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
$this->context->setNode($previousValue, $previousObject, $previousMetadata, $previousPath);
$this->context->setGroup($previousGroup);
if (null !== $previousConstraint) {
$this->context->setConstraint($previousConstraint);
}
return $this;
}

View File

@ -57,15 +57,20 @@ class Caster
}
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;

View File

@ -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 @@ class ExceptionCaster
$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;
$cacheKey = $f;
unset($cacheKey['object'], $cacheKey['args']);
$cacheKey[] = self::$srcContext;
$cacheKey = implode('-', $cacheKey);
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']));
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;
$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 (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']));
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']];
$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(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(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']);
}
}
$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 @@ class ExceptionCaster
));
}
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 @@ class ExceptionCaster
} while (0 > $i && null !== $pad);
--$ltrim;
$srcArray = array();
foreach ($src as $i => $c) {
if ($ltrim) {
@ -274,9 +286,9 @@ class ExceptionCaster
}
}
$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);
}
}

View File

@ -252,14 +252,15 @@ abstract class AbstractCloner implements ClonerInterface
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);
}

View File

@ -179,14 +179,14 @@ EODUMP;
$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 @@ array:2 [
0 => {
class: "__TwigTemplate_VarDumperFixture_u75a09"
src: {
bar.twig:2: {
%sTwig.php:1: {
:
: foo bar
: twig source
:
}
}
}
@ -208,7 +208,7 @@ array:2 [
%A
}
src: {
foo.twig:2: {
%sExceptionCasterTest.php:2: {
: foo bar
: twig source
:

View File

@ -271,7 +271,7 @@ stream resource {@{$ref}
: 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
:

View File

@ -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 @@ class __TwigTemplate_VarDumperFixture_u75a09 extends Twig_Template
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__);
}
}