Merge branch '3.1' into 3.2

* 3.1:
  [TwigBundle] fixed usage when Templating is not installed
  [Validator] Check cascasdedGroups for being countable
  [Cache] Add changelog
  [Filesystem] Check that the directory is writable after created it in dumpFile()
  [HttpFoundation] Improved set cookie header tests
  [Serializer] int is valid when float is expected when deserializing JSON
  [Console] increased code coverage of Output classes
  Added missing headers in fixture files
  [Profiler][VarDumper] Fix minor color issue & duplicated selector
This commit is contained in:
Fabien Potencier 2017-01-08 12:47:33 -08:00
commit 831d0a0553
15 changed files with 155 additions and 34 deletions

View File

@ -49,7 +49,7 @@ class ExtensionPass implements CompilerPassInterface
if ($container->has('form.extension')) {
$container->getDefinition('twig.extension.form')->addTag('twig.extension');
$reflClass = new \ReflectionClass('Symfony\Bridge\Twig\Extension\FormExtension');
$container->getDefinition('twig.loader.filesystem')->addMethodCall('addPath', array(dirname(dirname($reflClass->getFileName())).'/Resources/views/Form'));
$container->getDefinition('twig.loader.native_filesystem')->addMethodCall('addPath', array(dirname(dirname($reflClass->getFileName())).'/Resources/views/Form'));
}
if ($container->has('translator')) {
@ -85,15 +85,15 @@ class ExtensionPass implements CompilerPassInterface
}
$composerRootDir = $this->getComposerRootDir($container->getParameter('kernel.root_dir'));
$loader = $container->getDefinition('twig.loader.filesystem');
$loader->replaceArgument(2, $composerRootDir);
if (!$container->has('templating')) {
$loader = $container->getDefinition('twig.loader.native_filesystem');
$loader->replaceArgument(1, $composerRootDir);
$loader->addTag('twig.loader');
$loader->setMethodCalls($container->getDefinition('twig.loader.filesystem')->getMethodCalls());
$twigLoader = $container->getDefinition('twig.loader.native_filesystem');
if ($container->has('templating')) {
$loader = $container->getDefinition('twig.loader.filesystem');
$loader->setMethodCalls($twigLoader->getMethodCalls());
$loader->replaceArgument(2, $composerRootDir);
$twigLoader->clearTag('twig.loader');
} else {
$twigLoader->replaceArgument(1, $composerRootDir);
$container->setAlias('twig.loader.filesystem', new Alias('twig.loader.native_filesystem', false));
}

View File

@ -78,7 +78,7 @@ class TwigExtension extends Extension
$envConfiguratorDefinition->replaceArgument(4, $config['number_format']['decimal_point']);
$envConfiguratorDefinition->replaceArgument(5, $config['number_format']['thousands_separator']);
$twigFilesystemLoaderDefinition = $container->getDefinition('twig.loader.filesystem');
$twigFilesystemLoaderDefinition = $container->getDefinition('twig.loader.native_filesystem');
// register user-configured paths
foreach ($config['paths'] as $path => $namespace) {

View File

@ -11,8 +11,6 @@
<tag name="twig.loader"/>
</service>
<service id="twig.loader" alias="twig.loader.filesystem" />
<service id="templating.engine.twig" class="Symfony\Bundle\TwigBundle\TwigEngine" public="false">
<argument type="service" id="twig" />
<argument type="service" id="templating.name_parser" />

View File

@ -153,7 +153,7 @@ class TwigExtensionTest extends TestCase
$this->loadFromFile($container, 'extra', $format);
$this->compileContainer($container);
$def = $container->getDefinition('twig.loader.filesystem');
$def = $container->getDefinition('twig.loader.native_filesystem');
$paths = array();
foreach ($def->getMethodCalls() as $call) {
if ('addPath' === $call[0] && false === strpos($call[1][0], 'Form')) {

View File

@ -172,11 +172,10 @@
font-size: 11px;
padding: 4px 8px 4px 0;
}
.sf-toolbar-block .sf-toolbar-info-piece span {
.sf-toolbar-block:not(.sf-toolbar-block-dump) .sf-toolbar-info-piece span {
color: #F5F5F5;
}
.sf-toolbar-block .sf-toolbar-info-piece span {
color: #F5F5F5;
font-size: 12px;
}

View File

@ -11,6 +11,7 @@
namespace Symfony\Component\Console\Tests\Output;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\Output;
@ -22,4 +23,19 @@ class ConsoleOutputTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity(), '__construct() takes the verbosity as its first argument');
$this->assertSame($output->getFormatter(), $output->getErrorOutput()->getFormatter(), '__construct() takes a formatter or null as the third argument');
}
public function testSetFormatter()
{
$output = new ConsoleOutput();
$outputFormatter = new OutputFormatter();
$output->setFormatter($outputFormatter);
$this->assertSame($outputFormatter, $output->getFormatter());
}
public function testSetVerbosity()
{
$output = new ConsoleOutput();
$output->setVerbosity(Output::VERBOSITY_VERBOSE);
$this->assertSame(Output::VERBOSITY_VERBOSE, $output->getVerbosity());
}
}

View File

@ -11,7 +11,9 @@
namespace Symfony\Component\Console\Tests\Output;
use Symfony\Component\Console\Formatter\OutputFormatter;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\Output;
use Symfony\Component\Console\Output\OutputInterface;
class NullOutputTest extends \PHPUnit_Framework_TestCase
@ -36,4 +38,50 @@ class NullOutputTest extends \PHPUnit_Framework_TestCase
$output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE);
$this->assertSame(OutputInterface::VERBOSITY_QUIET, $output->getVerbosity(), '->getVerbosity() always returns VERBOSITY_QUIET for NullOutput');
}
public function testSetFormatter()
{
$output = new NullOutput();
$outputFormatter = new OutputFormatter();
$output->setFormatter($outputFormatter);
$this->assertNotSame($outputFormatter, $output->getFormatter());
}
public function testSetVerbosity()
{
$output = new NullOutput();
$output->setVerbosity(Output::VERBOSITY_NORMAL);
$this->assertEquals(Output::VERBOSITY_QUIET, $output->getVerbosity());
}
public function testSetDecorated()
{
$output = new NullOutput();
$output->setDecorated(true);
$this->assertFalse($output->isDecorated());
}
public function testIsQuiet()
{
$output = new NullOutput();
$this->assertTrue($output->isQuiet());
}
public function testIsVerbose()
{
$output = new NullOutput();
$this->assertFalse($output->isVerbose());
}
public function testIsVeryVerbose()
{
$output = new NullOutput();
$this->assertFalse($output->isVeryVerbose());
}
public function testIsDebug()
{
$output = new NullOutput();
$this->assertFalse($output->isDebug());
}
}

View File

@ -632,7 +632,9 @@ class Filesystem
if (!is_dir($dir)) {
$this->mkdir($dir);
} elseif (!is_writable($dir)) {
}
if (!is_writable($dir)) {
throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, null, $dir);
}

View File

@ -124,11 +124,11 @@ class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase
$bag = new ResponseHeaderBag(array());
$bag->setCookie(new Cookie('foo', 'bar'));
$this->assertContains('Set-Cookie: foo=bar; path=/; httponly', explode("\r\n", $bag->__toString()));
$this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
$bag->clearCookie('foo');
$this->assertRegExp('#^Set-Cookie: foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; httponly#m', $bag->__toString());
$this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; httponly', $bag);
}
public function testClearCookieSecureNotHttpOnly()
@ -137,7 +137,7 @@ class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase
$bag->clearCookie('foo', '/', null, true, false);
$this->assertRegExp('#^Set-Cookie: foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; secure#m', $bag->__toString());
$this->assertSetCookieHeader('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/; secure', $bag);
}
public function testReplace()
@ -173,11 +173,10 @@ class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase
$this->assertCount(4, $bag->getCookies());
$headers = explode("\r\n", $bag->__toString());
$this->assertContains('Set-Cookie: foo=bar; path=/path/foo; domain=foo.bar; httponly', $headers);
$this->assertContains('Set-Cookie: foo=bar; path=/path/foo; domain=foo.bar; httponly', $headers);
$this->assertContains('Set-Cookie: foo=bar; path=/path/bar; domain=bar.foo; httponly', $headers);
$this->assertContains('Set-Cookie: foo=bar; path=/; httponly', $headers);
$this->assertSetCookieHeader('foo=bar; path=/path/foo; domain=foo.bar; httponly', $bag);
$this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=foo.bar; httponly', $bag);
$this->assertSetCookieHeader('foo=bar; path=/path/bar; domain=bar.foo; httponly', $bag);
$this->assertSetCookieHeader('foo=bar; path=/; httponly', $bag);
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertTrue(isset($cookies['foo.bar']['/path/foo']['foo']));
@ -231,7 +230,7 @@ class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase
{
$bag = new ResponseHeaderBag();
$cookies = $bag->getCookies('invalid_argument');
$bag->getCookies('invalid_argument');
}
/**
@ -302,4 +301,9 @@ class ResponseHeaderBagTest extends \PHPUnit_Framework_TestCase
array('attachment', 'föö.html'),
);
}
private function assertSetCookieHeader($expected, ResponseHeaderBag $actual)
{
$this->assertRegExp('#^Set-Cookie:\s+'.preg_quote($expected, '#').'$#m', str_replace("\r\n", "\n", (string) $actual));
}
}

View File

@ -1,5 +1,14 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpKernel\Tests\Fixtures\Controller;
class BasicTypesController

View File

@ -1,5 +1,14 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpKernel\Tests\Fixtures\Controller;
use Symfony\Component\HttpFoundation\Request;

View File

@ -1,5 +1,14 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpKernel\Tests\Fixtures\Controller;
class VariadicController

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Serializer\Normalizer;
use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Exception\CircularReferenceException;
use Symfony\Component\Serializer\Exception\LogicException;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
@ -260,6 +261,16 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer
}
}
// JSON only has a Number type corresponding to both int and float PHP types.
// PHP's json_encode, JavaScript's JSON.stringify, Go's json.Marshal as well as most other JSON encoders convert
// floating-point numbers like 12.0 to 12 (the decimal part is dropped when possible).
// PHP's json_decode automatically converts Numbers without a decimal part to integers.
// To circumvent this behavior, integers are converted to floats when denormalizing JSON based formats and when
// a float is expected.
if (Type::BUILTIN_TYPE_FLOAT === $builtinType && is_int($data) && false !== strpos($format, JsonEncoder::FORMAT)) {
return (float) $data;
}
if (call_user_func('is_'.$builtinType, $data)) {
return $data;
}

View File

@ -580,11 +580,21 @@ class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase
'inners' => array(array('foo' => 1), array('foo' => 2)),
), ObjectOuter::class);
$this->assertEquals('foo', $obj->getInner()->foo);
$this->assertEquals('bar', $obj->getInner()->bar);
$this->assertEquals('1988-01-21', $obj->getDate()->format('Y-m-d'));
$this->assertEquals(1, $obj->getInners()[0]->foo);
$this->assertEquals(2, $obj->getInners()[1]->foo);
$this->assertSame('foo', $obj->getInner()->foo);
$this->assertSame('bar', $obj->getInner()->bar);
$this->assertSame('1988-01-21', $obj->getDate()->format('Y-m-d'));
$this->assertSame(1, $obj->getInners()[0]->foo);
$this->assertSame(2, $obj->getInners()[1]->foo);
}
public function testAcceptJsonNumber()
{
$extractor = new PropertyInfoExtractor(array(), array(new PhpDocExtractor(), new ReflectionExtractor()));
$normalizer = new ObjectNormalizer(null, null, null, $extractor);
$serializer = new Serializer(array(new ArrayDenormalizer(), new DateTimeNormalizer(), $normalizer));
$this->assertSame(10.0, $serializer->denormalize(array('number' => 10), JsonNumber::class, 'json')->number);
$this->assertSame(10.0, $serializer->denormalize(array('number' => 10), JsonNumber::class, 'jsonld')->number);
}
/**
@ -892,3 +902,11 @@ class DummyWithConstructorInexistingObject
{
}
}
class JsonNumber
{
/**
* @var float
*/
public $number;
}

View File

@ -720,9 +720,7 @@ class RecursiveContextualValidator implements ContextualValidatorInterface
// The $cascadedGroups property is set, if the "Default" group is
// overridden by a group sequence
// See validateClassNode()
$cascadedGroups = count($cascadedGroups) > 0
? $cascadedGroups
: $groups;
$cascadedGroups = null !== $cascadedGroups && count($cascadedGroups) > 0 ? $cascadedGroups : $groups;
if (is_array($value)) {
// Arrays are always traversed, independent of the specified