diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php index 0dec79debc..abb2fed46d 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php @@ -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)); } diff --git a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php index 2b4fa48028..a994ce6e67 100644 --- a/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php +++ b/src/Symfony/Bundle/TwigBundle/DependencyInjection/TwigExtension.php @@ -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) { diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/templating.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/templating.xml index abfe1b47b1..fae523eb6f 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/templating.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/templating.xml @@ -11,8 +11,6 @@ - - diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php index 8bcea690ae..1b6f783fc6 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php @@ -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')) { diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index 4386b8d06e..d2db7b700d 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -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; } diff --git a/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php b/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php index 1afbbb6e6c..b3808c07cf 100644 --- a/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php +++ b/src/Symfony/Component/Console/Tests/Output/ConsoleOutputTest.php @@ -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()); + } } diff --git a/src/Symfony/Component/Console/Tests/Output/NullOutputTest.php b/src/Symfony/Component/Console/Tests/Output/NullOutputTest.php index b20ae4e8d0..f09573f04d 100644 --- a/src/Symfony/Component/Console/Tests/Output/NullOutputTest.php +++ b/src/Symfony/Component/Console/Tests/Output/NullOutputTest.php @@ -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()); + } } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index b24caa07a8..51795944dc 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -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); } diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php index 1101b6ac26..12da3879e0 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseHeaderBagTest.php @@ -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)); + } } diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/BasicTypesController.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/BasicTypesController.php index 1a603c2c08..e8e0b60346 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/BasicTypesController.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/BasicTypesController.php @@ -1,5 +1,14 @@ + * + * 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 diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/ExtendingRequest.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/ExtendingRequest.php index e90e87c700..9b4754b46f 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/ExtendingRequest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/ExtendingRequest.php @@ -1,5 +1,14 @@ + * + * 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; diff --git a/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/VariadicController.php b/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/VariadicController.php index a540f9d1e1..c39812453b 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/VariadicController.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fixtures/Controller/VariadicController.php @@ -1,5 +1,14 @@ + * + * 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 diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index e891bb5171..2b3433c567 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -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; } diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index cc25d488c6..0b016b829f 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -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; +} diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index a57a5919ba..838d12b403 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -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