Merge branch '3.2'

* 3.2:
  [WebProfilerBundle] Fix AJAX panel with fetch requests
  Don’t compile when Opcache is not enabled on CLI
  DateIntervalType: 'invert' should not inherit the 'required' option
  [Form] DateIntervalType: Do not try to translate choices
  [TwigBridge] fix constructor args check
  Allow simple-phpunit to be used with an HTTP proxy
  Minor fixes for 3.2
  Fix a web profiler form issue with fields added to the form after the form was built
  do not trigger deprecations for valid YAML
  Write an exception message in a one heading line
  [Workflow] Added missing docblock
  [Finder] Refine phpdoc about argument for NumberComparator
  Fixed max width from ajax request url element (td)
  Fix unresolved parameters from default bundle configs in debug:config
  [github] Tweak PR template
  [Serializer] Optimize max depth checking
This commit is contained in:
Fabien Potencier 2016-12-13 10:39:51 +01:00
commit 40280f203c
26 changed files with 123 additions and 66 deletions

View File

@ -1,11 +1,19 @@
| Q | A
| ------------- | ---
| Branch? | "master" for new features / 2.7, 2.8 or 3.1 for fixes
| Branch? | master / 2.7, 2.8, 3.1 or 3.2 <!--see comment below-->
| Bug fix? | yes/no
| New feature? | yes/no
| BC breaks? | yes/no
| Deprecations? | yes/no
| Tests pass? | yes/no
| Fixed tickets | comma-separated list of tickets fixed by the PR, if any
| Fixed tickets | #... <!-- #-prefixed issue number(s), if any -->
| License | MIT
| Doc PR | reference to the documentation PR, if any
| Doc PR | symfony/symfony-docs#... <!--highly recommended for new features-->
<!--
- Bug fixes must be submitted against the lowest branch where they apply
(lowest branches are regularly merged to upper ones so they get the fixes too).
- Features and deprecations must be submitted against the master branch.
- Please fill in this template according to the PR you're about to submit.
- Replace this comment by a description of what your PR is solving.
-->

View File

@ -37,7 +37,7 @@ if (!file_exists("$PHPUNIT_DIR/phpunit-$PHPUNIT_VERSION/phpunit") || md5_file(__
if (file_exists("phpunit-$PHPUNIT_VERSION")) {
passthru(sprintf('\\' === DIRECTORY_SEPARATOR ? '(del /S /F /Q %s & rmdir %1$s) >nul': 'rm -rf %s', "phpunit-$PHPUNIT_VERSION"));
}
if (extension_loaded('openssl') && ini_get('allow_url_fopen')) {
if (extension_loaded('openssl') && ini_get('allow_url_fopen') && !isset($_SERVER['http_proxy']) && !isset($_SERVER['https_proxy'])) {
stream_copy_to_stream(fopen("https://github.com/sebastianbergmann/phpunit/archive/$PHPUNIT_VERSION.zip", 'rb'), fopen("$PHPUNIT_VERSION.zip", 'wb'));
} else {
@unlink("$PHPUNIT_VERSION.zip");

View File

@ -31,7 +31,7 @@ class FormExtension extends \Twig_Extension implements \Twig_Extension_InitRunti
public function __construct($renderer = null)
{
if ($this->renderer instanceof TwigRendererInterface) {
if ($renderer instanceof TwigRendererInterface) {
@trigger_error(sprintf('Passing a Twig Form Renderer to the "%s" constructor is deprecated since version 3.2 and won\'t be possible in 4.0. Pass the Twig_Environment to the TwigRendererEngine constructor instead.', static::class), E_USER_DEPRECATED);
} elseif (null !== $renderer && !(is_array($renderer) && isset($renderer[0], $renderer[1]) && $renderer[0] instanceof ContainerInterface)) {
throw new \InvalidArgumentException(sprintf('Passing any arguments the constructor of %s is reserved for internal use.', __CLASS__));

View File

@ -105,7 +105,7 @@ EOF
$io->title(sprintf('Current configuration for "%s.%s"', $extensionAlias, $path));
$io->writeln(Yaml::dump($config, 10));
$io->writeln(Yaml::dump($container->getParameterBag()->resolveValue($config), 10));
}
private function compileContainer()

View File

@ -17,7 +17,6 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Workflow\Dumper\GraphvizDumper;
use Symfony\Component\Workflow\Dumper\StateMachineGraphvizDumper;
use Symfony\Component\Workflow\Marking;
use Symfony\Component\Workflow\Workflow;
/**
* @author Grégoire Pineau <lyrixx@lyrixx.info>

View File

@ -137,7 +137,7 @@ abstract class Controller implements ContainerAwareInterface
protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT)
{
$response = new BinaryFileResponse($file);
$response->setContentDisposition($disposition, $fileName === null ? $response->getFile()->getFileName() : $fileName);
$response->setContentDisposition($disposition, $fileName === null ? $response->getFile()->getFilename() : $fileName);
return $response;
}

View File

@ -20,7 +20,6 @@ use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Adapter\ProxyAdapter;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;

View File

@ -9,9 +9,7 @@
<div class="text-exception">
<div class="open-quote">“</div>
<h1>
{{ exception.message|nl2br|format_file_from_text }}
</h1>
<h1>{{ exception.message|nl2br|format_file_from_text }}</h1>
<div>
<strong>{{ status_code }}</strong> {{ status_text }} - {{ exception.class|abbr_class }}

View File

@ -457,7 +457,7 @@
{% import _self as tree %}
<div class="tree-details{% if not show|default(false) %} hidden{% endif %}" {% if data.id is defined %}id="{{ data.id }}-details"{% endif %}>
<h2 class="dump-inline">
{{ name|default('(no name)') }} ({{ profiler_dump(data.type_class) }})
{{ name|default('(no name)') }} {% if data.type_class is defined %}({{ profiler_dump(data.type_class) }}){% endif %}
</h2>
{% if data.errors is defined and data.errors|length > 0 %}

View File

@ -234,15 +234,28 @@
var oldFetch = window.fetch;
window.fetch = function () {
var promise = oldFetch.apply(this, arguments);
if (!arguments[0].match(new RegExp({{ excluded_ajax_paths|json_encode|raw }}))) {
var url = arguments[0];
var params = arguments[1];
var paramType = Object.prototype.toString.call(arguments[0]);
if (paramType === '[object Request]') {
url = arguments[0].url;
params = {
method: arguments[0].method,
credentials: arguments[0].credentials,
headers: arguments[0].headers,
mode: arguments[0].mode,
redirect: arguments[0].redirect
};
}
if (!url.match(new RegExp({{ excluded_ajax_paths|json_encode|raw }}))) {
var method = 'GET';
if (arguments[1] && arguments[1].method !== undefined) {
method = arguments[1].method;
if (params && params.method !== undefined) {
method = params.method;
}
var stackElement = {
error: false,
url: arguments[0],
url: url,
method: method,
type: 'fetch',
start: new Date()

View File

@ -93,7 +93,7 @@ class PhpFilesAdapter extends AbstractAdapter
$ok = true;
$data = array($lifetime ? time() + $lifetime : PHP_INT_MAX, '');
foreach ($values as $id => $value) {
foreach ($values as $key => $value) {
if (null === $value || is_object($value)) {
$value = serialize($value);
} elseif (is_array($value)) {
@ -109,13 +109,16 @@ class PhpFilesAdapter extends AbstractAdapter
$value = serialize($value);
}
} elseif (!is_scalar($value)) {
throw new InvalidArgumentException(sprintf('Value of type "%s" is not serializable', $key, gettype($value)));
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, gettype($value)));
}
$data[1] = $value;
$file = $this->getFile($id, true);
$file = $this->getFile($key, true);
$ok = $this->write($file, '<?php return '.var_export($data, true).';') && $ok;
@opcache_compile_file($file);
if ('cli' !== PHP_SAPI || ini_get('opcache.enable_cli')) {
@opcache_compile_file($file);
}
}
return $ok;

View File

@ -30,7 +30,7 @@ class TagAwareAdapterTest extends AdapterTestCase
}
/**
* @expectedException Psr\Cache\InvalidArgumentException
* @expectedException \Psr\Cache\InvalidArgumentException
*/
public function testInvalidTag()
{

View File

@ -65,7 +65,7 @@ class CacheItemTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider provideInvalidKey
* @expectedException Symfony\Component\Cache\Exception\InvalidArgumentException
* @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException
* @expectedExceptionMessage Cache tag
*/
public function testInvalidTag($tag)

View File

@ -13,7 +13,6 @@ namespace Symfony\Component\Console\Tests\Style;
use PHPUnit_Framework_TestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
class SymfonyStyleTest extends PHPUnit_Framework_TestCase

View File

@ -37,7 +37,7 @@ class NumberComparator extends Comparator
/**
* Constructor.
*
* @param string $test A comparison string
* @param string|int $test A comparison string or an integer
*
* @throws \InvalidArgumentException If the test is not understood
*/

View File

@ -111,7 +111,7 @@ class Finder implements \IteratorAggregate, \Countable
* $finder->depth('> 1') // the Finder will start matching at level 1.
* $finder->depth('< 3') // the Finder will descend at most 3 levels of directories below the starting point.
*
* @param int $level The depth level expression
* @param string|int $level The depth level expression
*
* @return Finder|SplFileInfo[] The current Finder instance
*
@ -283,7 +283,7 @@ class Finder implements \IteratorAggregate, \Countable
* $finder->size('<= 1Ki');
* $finder->size(4);
*
* @param string $size A size range string
* @param string|int $size A size range string or an integer
*
* @return Finder|SplFileInfo[] The current Finder instance
*

View File

@ -102,14 +102,12 @@ class DateIntervalType extends AbstractType
$childOptions[$part] = array();
$childOptions[$part]['error_bubbling'] = true;
if ('choice' === $options['widget']) {
$childOptions[$part]['choice_translation_domain'] = false;
$childOptions[$part]['choices'] = $options[$part];
$childOptions[$part]['placeholder'] = $options['placeholder'][$part];
}
}
}
$invertOptions = array(
'error_bubbling' => true,
);
// Append generic carry-along options
foreach (array('required', 'translation_domain') as $passOpt) {
foreach ($this->timeParts as $part) {
@ -117,9 +115,6 @@ class DateIntervalType extends AbstractType
$childOptions[$part][$passOpt] = $options[$passOpt];
}
}
if ($options['with_invert']) {
$invertOptions[$passOpt] = $options[$passOpt];
}
}
foreach ($this->timeParts as $part) {
if ($options['with_'.$part]) {
@ -135,7 +130,11 @@ class DateIntervalType extends AbstractType
}
}
if ($options['with_invert']) {
$builder->add('invert', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', $invertOptions);
$builder->add('invert', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', array(
'error_bubbling' => true,
'required' => false,
'translation_domain' => $options['translation_domain'],
));
}
$builder->addViewTransformer(new DateIntervalToArrayTransformer($parts, 'text' === $options['widget']));
}

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
use Symfony\Component\Form\Extension\Core\Type\DateIntervalType;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\Test\TypeTestCase as TestCase;
@ -364,4 +365,38 @@ class DateIntervalTypeTest extends TestCase
$this->assertSame(array(), iterator_to_array($form['years']->getErrors()));
$this->assertSame(array($error), iterator_to_array($form->getErrors()));
}
public function testTranslationsAreDisabledForChoiceWidget()
{
$form = $this->factory->create(
DateIntervalType::class,
null,
array(
'widget' => 'choice',
'with_hours' => true,
'with_minutes' => true,
'with_seconds' => true,
)
);
$this->assertFalse($form->get('years')->getConfig()->getOption('choice_translation_domain'));
$this->assertFalse($form->get('months')->getConfig()->getOption('choice_translation_domain'));
$this->assertFalse($form->get('days')->getConfig()->getOption('choice_translation_domain'));
$this->assertFalse($form->get('hours')->getConfig()->getOption('choice_translation_domain'));
$this->assertFalse($form->get('minutes')->getConfig()->getOption('choice_translation_domain'));
$this->assertFalse($form->get('seconds')->getConfig()->getOption('choice_translation_domain'));
}
public function testInvertDoesNotInheritRequiredOption()
{
$form = $this->factory->create(
'Symfony\Component\Form\Extension\Core\Type\DateIntervalType',
null,
array(
'input' => 'dateinterval',
'with_invert' => true,
'required' => true,
)
);
$this->assertFalse($form->get('invert')->getConfig()->getOption('required'));
}
}

View File

@ -13,7 +13,6 @@ namespace Symfony\Component\Serializer\Encoder;
use Symfony\Component\Yaml\Dumper;
use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Yaml;
/**
* Encodes YAML data.

View File

@ -18,6 +18,7 @@ use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
@ -68,9 +69,10 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
$stack = array();
$attributes = $this->getAttributes($object, $format, $context);
$class = get_class($object);
$attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null;
foreach ($attributes as $attribute) {
if ($this->isMaxDepthReached($class, $attribute, $context)) {
if (null !== $attributesMetadata && $this->isMaxDepthReached($attributesMetadata, $class, $attribute, $context)) {
continue;
}
@ -300,42 +302,35 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
/**
* Is the max depth reached for the given attribute?
*
* @param string $class
* @param string $attribute
* @param array $context
* @param AttributeMetadataInterface[] $attributesMetadata
* @param string $class
* @param string $attribute
* @param array $context
*
* @return bool
*/
private function isMaxDepthReached($class, $attribute, array &$context)
private function isMaxDepthReached(array $attributesMetadata, $class, $attribute, array &$context)
{
if (!$this->classMetadataFactory || !isset($context[static::ENABLE_MAX_DEPTH])) {
return false;
}
$classMetadata = $this->classMetadataFactory->getMetadataFor($class);
$attributesMetadata = $classMetadata->getAttributesMetadata();
if (!isset($attributesMetadata[$attribute])) {
return false;
}
$maxDepth = $attributesMetadata[$attribute]->getMaxDepth();
if (null === $maxDepth) {
if (
!isset($context[static::ENABLE_MAX_DEPTH]) ||
!isset($attributesMetadata[$attribute]) ||
null === $maxDepth = $attributesMetadata[$attribute]->getMaxDepth()
) {
return false;
}
$key = sprintf(static::DEPTH_KEY_PATTERN, $class, $attribute);
$keyExist = isset($context[$key]);
if (!isset($context[$key])) {
$context[$key] = 1;
if ($keyExist && $context[$key] === $maxDepth) {
return false;
}
if ($context[$key] === $maxDepth) {
return true;
}
if ($keyExist) {
++$context[$key];
} else {
$context[$key] = 1;
}
++$context[$key];
return false;
}

View File

@ -31,8 +31,8 @@ class StubCaster
$stub->attr = $c->attr;
if (Stub::TYPE_REF === $c->type && !$c->class && is_string($c->value) && !preg_match('//u', $c->value)) {
$stub->type = self::TYPE_STRING;
$stub->class = self::STRING_BINARY;
$stub->type = Stub::TYPE_STRING;
$stub->class = Stub::STRING_BINARY;
}
return array();

View File

@ -79,6 +79,9 @@ class DefinitionBuilder
}
}
/**
* @param Transition[] $transitions
*/
public function addTransitions(array $transitions)
{
foreach ($transitions as $transition) {

View File

@ -30,6 +30,12 @@ class Registry
$this->workflows[] = array($workflow, $className);
}
/**
* @param object $subject
* @param string|null $workflowName
*
* @return Workflow
*/
public function get($subject, $workflowName = null)
{
$matched = null;

View File

@ -2,7 +2,6 @@
namespace Symfony\Component\Workflow\Tests;
use Symfony\Component\Workflow\Marking;
use Symfony\Component\Workflow\StateMachine;
class StateMachineTest extends \PHPUnit_Framework_TestCase

View File

@ -463,8 +463,8 @@ class Inline
break;
}
if (!isset($mapping[$i + 1]) || ' ' !== $mapping[$i + 1]) {
@trigger_error('Omitting the space after the colon that follows a mapping key definition is deprecated since version 3.2 and will throw a ParseException in 4.0.', E_USER_DEPRECATED);
if (!isset($mapping[$i + 1]) || !in_array($mapping[$i + 1], array(' ', '[', ']', '{', '}'), true)) {
@trigger_error('Using a colon that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}" is deprecated since version 3.2 and will throw a ParseException in 4.0.', E_USER_DEPRECATED);
}
// value

View File

@ -166,7 +166,7 @@ class InlineTest extends \PHPUnit_Framework_TestCase
/**
* @group legacy
* @expectedDeprecation Omitting the space after the colon that follows a mapping key definition is deprecated since version 3.2 and will throw a ParseException in 4.0.
* @expectedDeprecation Using a colon that is not followed by an indication character (i.e. " ", ",", "[", "]", "{", "}" is deprecated since version 3.2 and will throw a ParseException in 4.0.
* throws \Symfony\Component\Yaml\Exception\ParseException in 4.0
*/
public function testParseMappingKeyWithColonNotFollowedBySpace()
@ -395,6 +395,8 @@ class InlineTest extends \PHPUnit_Framework_TestCase
array('[foo, {bar: foo}]', array('foo', array('bar' => 'foo'))),
array('{ foo: {bar: foo} }', array('foo' => array('bar' => 'foo'))),
array('{ foo: [bar, foo] }', array('foo' => array('bar', 'foo'))),
array('{ foo:{bar: foo} }', array('foo' => array('bar' => 'foo'))),
array('{ foo:[bar, foo] }', array('foo' => array('bar', 'foo'))),
array('[ foo, [ bar, foo ] ]', array('foo', array('bar', 'foo'))),