diff --git a/CHANGELOG-3.2.md b/CHANGELOG-3.2.md index de3c3fd9977b5..1a4d44e19981e 100644 --- a/CHANGELOG-3.2.md +++ b/CHANGELOG-3.2.md @@ -7,6 +7,22 @@ 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.12 (2017-07-17) + + * bug #23549 [PropertyInfo] conflict for phpdocumentor/reflection-docblock 3.2 (xabbuh) + * security #23507 [Security] validate empty passwords again (xabbuh) + * bug #23526 [HttpFoundation] Set meta refresh time to 0 in RedirectResponse content (jnvsor) + * bug #23540 Disable inlining deprecated services (alekitto) + * bug #23468 [DI] Handle root namespace in service definitions (ro0NL) + * bug #23256 [Security] Fix authentication.failure event not dispatched on AccountStatusException (chalasr) + * bug #23461 Use rawurlencode() to transform the Cookie into a string (javiereguiluz) + * bug #23459 [TwigBundle] allow to configure custom formats in XML configs (xabbuh) + * bug #23460 Don't display the Symfony debug toolbar when printing the page (javiereguiluz) + * bug #23469 [FrameworkBundle] do not wire namespaces for the ArrayAdapter (xabbuh) + * bug #23417 [DI][Security] Prevent unwanted deprecation notices when using Expression Languages (dunglas) + * bug #23261 Fixed absolute url generation for query strings and hash urls (alexander-schranz) + * bug #23398 [Filesystem] Dont copy perms when origin is remote (nicolas-grekas) + * 3.2.11 (2017-07-05) * bug #23390 [Cache] Handle APCu failures gracefully (nicolas-grekas) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 3432439481471..9cf36e6beb382 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -70,9 +70,9 @@ Symfony is the result of the work of many people who made the code better - Deni - Henrik Westphal (snc) - Dariusz Górecki (canni) + - Jáchym Toušek (enumag) - Titouan Galopin (tgalopin) - Douglas Greenshields (shieldo) - - Jáchym Toušek (enumag) - Konstantin Myakshin (koc) - Lee McDermott - Brandon Turner @@ -99,11 +99,12 @@ Symfony is the result of the work of many people who made the code better - Baptiste Clavié (talus) - Vladimir Reznichenko (kalessil) - marc.weistroff + - Yonel Ceruto González (yonelceruto) - lenar - Włodzimierz Gajda (gajdaw) - - Yonel Ceruto González (yonelceruto) - Alexander Schwenn (xelaris) - Jacob Dreesen (jdreesen) + - Tobias Nyholm (tobias) - Florian Voutzinos (florianv) - Colin Frei - Adrien Brault (adrienbrault) @@ -111,7 +112,6 @@ Symfony is the result of the work of many people who made the code better - Peter Kokot (maastermedia) - David Buchmann (dbu) - excelwebzone - - Tobias Nyholm (tobias) - Tomáš Votruba (tomas_votruba) - Fabien Pennequin (fabienpennequin) - Gordon Franke (gimler) @@ -133,12 +133,15 @@ Symfony is the result of the work of many people who made the code better - Guilherme Blanco (guilhermeblanco) - Pablo Godel (pgodel) - Jérémie Augustin (jaugustin) + - Dany Maillard (maidmaid) - Andréia Bohner (andreia) - Rafael Dohms (rdohms) - Arnaud Kleinpeter (nanocom) - jwdeitch + - David Maicher (dmaicher) - Mikael Pajunen - Joel Wurtz (brouznouf) + - Grégoire Paris (greg0ire) - Philipp Wahala (hifi) - Vyacheslav Pavlov - Richard van Laak (rvanlaak) @@ -147,11 +150,9 @@ Symfony is the result of the work of many people who made the code better - Thomas Rabaix (rande) - Rouven Weßling (realityking) - Teoh Han Hui (teohhanhui) - - David Maicher (dmaicher) - Jérôme Vasseur (jvasseur) - Clemens Tolboom - Helmer Aaviksoo - - Grégoire Paris (greg0ire) - Hiromi Hishida (77web) - Matthieu Ouellette-Vachon (maoueh) - Michał Pipa (michal.pipa) @@ -236,7 +237,6 @@ Symfony is the result of the work of many people who made the code better - Arjen Brouwer (arjenjb) - Katsuhiro OGAWA - Patrick McDougle (patrick-mcdougle) - - Dany Maillard (maidmaid) - Alif Rachmawadi - Kristen Gilden (kgilden) - Pierre-Yves LEBECQ (pylebecq) @@ -622,6 +622,7 @@ Symfony is the result of the work of many people who made the code better - develop - ReenExe - Mark Sonnabaum + - Maxime Veber (nek-) - Richard Quadling - jochenvdv - Arturas Smorgun (asarturas) @@ -644,6 +645,7 @@ Symfony is the result of the work of many people who made the code better - Trent Steel (trsteel88) - Yuen-Chi Lian - Besnik Br + - Jose Gonzalez - Dariusz Ruminski - Joshua Nye - Claudio Zizza @@ -1083,7 +1085,6 @@ Symfony is the result of the work of many people who made the code better - Max Summe - WedgeSama - Felds Liscia - - Maxime Veber (nek-) - Sullivan SENECHAL - Tadcka - Beth Binkovitz @@ -1094,12 +1095,12 @@ Symfony is the result of the work of many people who made the code better - Tomaz Ahlin - Marcus Stöhr (dafish) - Emmanuel Vella (emmanuel.vella) + - Adam Szaraniec (mimol) - Carsten Nielsen (phreaknerd) - Mathieu Rochette - Jay Severson - René Kerner - Nathaniel Catchpole - - Jose Gonzalez - Adrien Samson (adriensamson) - Samuel Gordalina (gordalina) - Max Romanovsky (maxromanovsky) @@ -1260,6 +1261,7 @@ Symfony is the result of the work of many people who made the code better - Aarón Nieves Fernández - Mike Meier - Kirill Saksin + - Julien Pauli - Koalabaerchen - michalmarcinkowski - Warwick @@ -1307,6 +1309,7 @@ Symfony is the result of the work of many people who made the code better - klemens - dened - Dmitry Korotovsky + - mcorteel - Michael van Tricht - Sam Ward - Walther Lalk @@ -1337,6 +1340,7 @@ Symfony is the result of the work of many people who made the code better - Jan Marek (janmarek) - Mark de Haan (markdehaan) - Dan Patrick (mdpatrick) + - Pedro Magalhães (pmmaga) - Rares Vlaseanu (raresvla) - tante kinast (tante) - Vincent LEFORT (vlefort) @@ -1544,6 +1548,7 @@ Symfony is the result of the work of many people who made the code better - Abdulkadir N. A. - Yevgen Kovalienia - Lebnik + - Shude - Ondřej Führer - Sema - Elan Ruusamäe diff --git a/composer.json b/composer.json index 75e9c916370b2..8730dd64159e8 100644 --- a/composer.json +++ b/composer.json @@ -17,6 +17,7 @@ ], "require": { "php": ">=5.5.9", + "ext-xml": "*", "doctrine/common": "~2.4", "twig/twig": "~1.34|~2.4", "psr/cache": "~1.0", @@ -94,7 +95,7 @@ "sensio/framework-extra-bundle": "^3.0.2" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0", "phpdocumentor/type-resolver": "<0.2.0", "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" }, diff --git a/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php b/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php index 90b515ee320ba..0dad40cfa0a3f 100644 --- a/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/HttpFoundationExtension.php @@ -72,6 +72,13 @@ public function generateAbsoluteUrl($path) $port = ':'.$this->requestContext->getHttpsPort(); } + if ('#' === $path[0]) { + $queryString = $this->requestContext->getQueryString(); + $path = $this->requestContext->getPathInfo().($queryString ? '?'.$queryString : '').$path; + } elseif ('?' === $path[0]) { + $path = $this->requestContext->getPathInfo().$path; + } + if ('/' !== $path[0]) { $path = rtrim($this->requestContext->getBaseUrl(), '/').'/'.$path; } @@ -82,6 +89,12 @@ public function generateAbsoluteUrl($path) return $path; } + if ('#' === $path[0]) { + $path = $request->getRequestUri().$path; + } elseif ('?' === $path[0]) { + $path = $request->getPathInfo().$path; + } + if (!$path || '/' !== $path[0]) { $prefix = $request->getPathInfo(); $last = strlen($prefix) - 1; diff --git a/src/Symfony/Bridge/Twig/Form/TwigRenderer.php b/src/Symfony/Bridge/Twig/Form/TwigRenderer.php index 9718c29cdb689..9d28b8c237d16 100644 --- a/src/Symfony/Bridge/Twig/Form/TwigRenderer.php +++ b/src/Symfony/Bridge/Twig/Form/TwigRenderer.php @@ -20,16 +20,19 @@ */ class TwigRenderer extends FormRenderer implements TwigRendererInterface { - /** - * @var TwigRendererEngineInterface - */ - private $engine; - public function __construct(TwigRendererEngineInterface $engine, CsrfTokenManagerInterface $csrfTokenManager = null) { parent::__construct($engine, $csrfTokenManager); + } - $this->engine = $engine; + /** + * Returns the engine used by this renderer. + * + * @return TwigRendererEngineInterface The renderer engine + */ + public function getEngine() + { + return parent::getEngine(); } /** @@ -37,6 +40,6 @@ public function __construct(TwigRendererEngineInterface $engine, CsrfTokenManage */ public function setEnvironment(Environment $environment) { - $this->engine->setEnvironment($environment); + $this->getEngine()->setEnvironment($environment); } } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php index 8f0c66ad78bb4..fcff0c0e1b93b 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/HttpFoundationExtensionTest.php @@ -42,6 +42,15 @@ public function getGenerateAbsoluteUrlData() array('http://example.com/baz', 'http://example.com/baz', '/'), array('https://example.com/baz', 'https://example.com/baz', '/'), array('//example.com/baz', '//example.com/baz', '/'), + + array('http://localhost/foo/bar?baz', '?baz', '/foo/bar'), + array('http://localhost/foo/bar?baz=1', '?baz=1', '/foo/bar?foo=1'), + array('http://localhost/foo/baz?baz=1', 'baz?baz=1', '/foo/bar?foo=1'), + + array('http://localhost/foo/bar#baz', '#baz', '/foo/bar'), + array('http://localhost/foo/bar?0#baz', '#baz', '/foo/bar?0'), + array('http://localhost/foo/bar?baz=1#baz', '?baz=1#baz', '/foo/bar?foo=1'), + array('http://localhost/foo/baz?baz=1#baz', 'baz?baz=1#baz', '/foo/bar?foo=1'), ); } diff --git a/src/Symfony/Bundle/DebugBundle/composer.json b/src/Symfony/Bundle/DebugBundle/composer.json index e5fe655296473..512639fa7e2e4 100644 --- a/src/Symfony/Bundle/DebugBundle/composer.json +++ b/src/Symfony/Bundle/DebugBundle/composer.json @@ -17,6 +17,7 @@ ], "require": { "php": ">=5.5.9", + "ext-xml": "*", "symfony/http-kernel": "~2.8|~3.0", "symfony/twig-bridge": "~2.8|~3.0", "symfony/var-dumper": "~2.8|~3.0" diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index efee66b41cecf..c1eb01d3284e3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -21,7 +21,8 @@ CHANGELOG * Deprecated using core form types without dependencies as services * Added `Symfony\Component\HttpHernel\DataCollector\RequestDataCollector::onKernelResponse()` * Added `Symfony\Bundle\FrameworkBundle\DataCollector\RequestDataCollector` - * Deprecated service `serializer.mapping.cache.apc` (use `serializer.mapping.cache.doctrine.apc` instead) + * The `framework.serializer.cache` option and the service `serializer.mapping.cache.apc` have been + deprecated. APCu should now be automatically used when available. 3.0.0 ----- diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php index db1430eb49b13..eda6f936bf7b5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/CachePoolPass.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; +use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\RedisAdapter; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -70,7 +71,7 @@ public function process(ContainerBuilder $container) } $i = 0; foreach ($attributes as $attr) { - if (isset($tags[0][$attr])) { + if (isset($tags[0][$attr]) && ('namespace' !== $attr || ArrayAdapter::class !== $adapter->getClass())) { $pool->replaceArgument($i++, $tags[0][$attr]); } unset($tags[0][$attr]); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php index 6fe6b30eb36c4..e9b9bbc778cac 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/CachePoolPassTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\CachePoolPass; +use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\DefinitionDecorator; @@ -49,6 +50,24 @@ public function testNamespaceArgumentIsReplaced() $this->assertSame('D07rhFx97S', $cachePool->getArgument(0)); } + public function testNamespaceArgumentIsNotReplacedIfArrayAdapterIsUsed() + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.environment', 'prod'); + $container->setParameter('kernel.name', 'app'); + $container->setParameter('kernel.root_dir', 'foo'); + + $container->register('cache.adapter.array', ArrayAdapter::class)->addArgument(0); + + $cachePool = new DefinitionDecorator('cache.adapter.array'); + $cachePool->addTag('cache.pool'); + $container->setDefinition('app.cache_pool', $cachePool); + + $this->cachePoolPass->process($container); + + $this->assertCount(0, $container->getDefinition('app.cache_pool')->getArguments()); + } + public function testArgsAreReplaced() { $container = new ContainerBuilder(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php index fb5395ea6d26d..fd9ed77b2f7ab 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php @@ -25,6 +25,10 @@ public function testRedirectWhenNoSlash() $coll->add('foo', new Route('/foo/')); $matcher = new RedirectableUrlMatcher($coll, $context = new RequestContext()); + $parameters = $matcher->match('/foo'); + if ('foo' === $parameters['_route']) { + $parameters['_route'] = null; // FC with behavior on 3.4 + } $this->assertEquals(array( '_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction', @@ -35,7 +39,7 @@ public function testRedirectWhenNoSlash() 'httpsPort' => $context->getHttpsPort(), '_route' => null, ), - $matcher->match('/foo') + $parameters ); } diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index dbcec82819615..4759d44a2e5f5 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -17,6 +17,7 @@ ], "require": { "php": ">=5.5.9", + "ext-xml": "*", "symfony/security": "~3.2", "symfony/http-kernel": "~3.2", "symfony/polyfill-php70": "~1.0" diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd b/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd index 3314091a08802..61b57798b27cf 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/schema/twig-1.0.xsd @@ -9,6 +9,8 @@ + + @@ -26,6 +28,18 @@ + + + + + + + + + + + + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/formats.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/formats.php new file mode 100644 index 0000000000000..630a9a9edc01a --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/php/formats.php @@ -0,0 +1,14 @@ +loadFromExtension('twig', array( + 'date' => array( + 'format' => 'Y-m-d', + 'interval_format' => '%d', + 'timezone' => 'Europe/Berlin', + ), + 'number_format' => array( + 'decimals' => 2, + 'decimal_point' => ',', + 'thousands_separator' => '.', + ), +)); diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/formats.xml b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/formats.xml new file mode 100644 index 0000000000000..1ab39e49229cd --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/xml/formats.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/formats.yml b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/formats.yml new file mode 100644 index 0000000000000..290921630f9e6 --- /dev/null +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/Fixtures/yml/formats.yml @@ -0,0 +1,9 @@ +twig: + date: + format: Y-m-d + interval_format: '%d' + timezone: Europe/Berlin + number_format: + decimals: 2 + decimal_point: ',' + thousands_separator: . diff --git a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php index d2fb85bd921a1..df3d11945622f 100644 --- a/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php +++ b/src/Symfony/Bundle/TwigBundle/Tests/DependencyInjection/TwigExtensionTest.php @@ -116,6 +116,26 @@ public function testLoadDefaultTemplateEscapingGuesserConfiguration($format) $this->assertEquals('name', $options['autoescape']); } + /** + * @dataProvider getFormats + */ + public function testLoadCustomDateFormats($fileFormat) + { + $container = $this->createContainer(); + $container->registerExtension(new TwigExtension()); + $this->loadFromFile($container, 'formats', $fileFormat); + $this->compileContainer($container); + + $environmentConfigurator = $container->getDefinition('twig.configurator.environment'); + + $this->assertSame('Y-m-d', $environmentConfigurator->getArgument(0)); + $this->assertSame('%d', $environmentConfigurator->getArgument(1)); + $this->assertSame('Europe/Berlin', $environmentConfigurator->getArgument(2)); + $this->assertSame(2, $environmentConfigurator->getArgument(3)); + $this->assertSame(',', $environmentConfigurator->getArgument(4)); + $this->assertSame('.', $environmentConfigurator->getArgument(5)); + } + public function testGlobalsWithDifferentTypesAndValues() { $globals = array( diff --git a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php index 37f47df6a6e40..7ebd04b574c22 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php +++ b/src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php @@ -128,34 +128,6 @@ public function panelAction(Request $request, $token) )), 200, array('Content-Type' => 'text/html')); } - /** - * Displays information page. - * - * @param Request $request The current HTTP Request - * @param string $about The about message - * - * @return Response A Response instance - * - * @throws NotFoundHttpException - */ - public function infoAction(Request $request, $about) - { - if (null === $this->profiler) { - throw new NotFoundHttpException('The profiler must be enabled.'); - } - - $this->profiler->disable(); - - if (null !== $this->cspHandler) { - $this->cspHandler->disableCsp(); - } - - return new Response($this->twig->render('@WebProfiler/Profiler/info.html.twig', array( - 'about' => $about, - 'request' => $request, - )), 200, array('Content-Type' => 'text/html')); - } - /** * Renders the Web Debug Toolbar. * diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml index 1a4bd39c621a6..0be717b19d6e7 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/config/routing/profiler.xml @@ -16,10 +16,6 @@ web_profiler.controller.profiler:searchBarAction - - web_profiler.controller.profiler:infoAction - - web_profiler.controller.profiler:phpinfoAction diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig index ef381fe4a73a7..0227532e1208a 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/info.html.twig @@ -1,25 +1,10 @@ {% extends '@WebProfiler/Profiler/layout.html.twig' %} {% set messages = { - 'purge' : { - status: 'success', - title: 'The profiler database was purged successfully', - message: 'Now you need to browse some pages with the Symfony Profiler enabled to collect data.' - }, 'no_token' : { status: 'error', title: (token|default('') == 'latest') ? 'There are no profiles' : 'Token not found', message: (token|default('') == 'latest') ? 'No profiles found in the database.' : 'Token "' ~ token|default('') ~ '" was not found in the database.' - }, - 'upload_error' : { - status: 'error', - title: 'A problem occurred when uploading the data', - message: 'No file given or the file was not uploaded successfully.' - }, - 'already_exists' : { - status: 'error', - title: 'A problem occurred when uploading the data', - message: 'The token already exists in the database.' } } %} 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 590d030c7a55a..862b7f43c6ec6 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -552,3 +552,10 @@ padding: 5px 0; margin-right: 10px; } + +/***** Media query print: Do not print the Toolbar. *****/ +@media print { + .sf-toolbar { + display: none; + } +} diff --git a/src/Symfony/Component/BrowserKit/Cookie.php b/src/Symfony/Component/BrowserKit/Cookie.php index 42f184d532e02..c042c6a525295 100644 --- a/src/Symfony/Component/BrowserKit/Cookie.php +++ b/src/Symfony/Component/BrowserKit/Cookie.php @@ -62,7 +62,7 @@ public function __construct($name, $value, $expires = null, $path = null, $domai $this->rawValue = $value; } else { $this->value = $value; - $this->rawValue = urlencode($value); + $this->rawValue = rawurlencode($value); } $this->name = $name; $this->path = empty($path) ? '/' : $path; diff --git a/src/Symfony/Component/BrowserKit/Tests/CookieTest.php b/src/Symfony/Component/BrowserKit/Tests/CookieTest.php index 38ea81220bb2c..2f5a08d104143 100644 --- a/src/Symfony/Component/BrowserKit/Tests/CookieTest.php +++ b/src/Symfony/Component/BrowserKit/Tests/CookieTest.php @@ -16,6 +16,21 @@ class CookieTest extends TestCase { + public function testToString() + { + $cookie = new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true); + $this->assertEquals('foo=bar; expires=Fri, 20 May 2011 15:25:52 GMT; domain=.myfoodomain.com; path=/; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie'); + + $cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true); + $this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20 May 2011 15:25:52 GMT; domain=.myfoodomain.com; path=/; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)'); + + $cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com'); + $this->assertEquals('foo=; expires=Thu, 01 Jan 1970 00:00:01 GMT; domain=.myfoodomain.com; path=/admin/; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL'); + + $cookie = new Cookie('foo', 'bar', 0, '/', ''); + $this->assertEquals('foo=bar; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; httponly', (string) $cookie); + } + /** * @dataProvider getTestsForToFromString */ diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php index 84bb841d85df2..628638ea969c4 100644 --- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php @@ -39,6 +39,10 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface */ protected $maxIdLength; + /** + * @param string $namespace + * @param int $defaultLifetime + */ protected function __construct($namespace = '', $defaultLifetime = 0) { $this->namespace = '' === $namespace ? '' : $this->getId($namespace).':'; @@ -81,6 +85,15 @@ function ($deferred, $namespace, &$expiredIds) { ); } + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string $version + * @param string $directory + * @param LoggerInterface|null $logger + * + * @return AdapterInterface + */ public static function createSystemCache($namespace, $defaultLifetime, $version, $directory, LoggerInterface $logger = null) { if (null === self::$apcuSupported) { @@ -139,7 +152,7 @@ abstract protected function doHave($id); /** * Deletes all items in the pool. * - * @param string The prefix used for all identifiers managed by this pool + * @param string $namespace The prefix used for all identifiers managed by this pool * * @return bool True if the pool was successfully cleared, false otherwise */ diff --git a/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php b/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php index ad3aadf1a5257..08179dd8faf5c 100644 --- a/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php @@ -24,6 +24,13 @@ public static function isSupported() return function_exists('apcu_fetch') && ini_get('apc.enabled'); } + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string|null $version + * + * @throws CacheException if APCu is not enabled + */ public function __construct($namespace = '', $defaultLifetime = 0, $version = null) { if (!static::isSupported()) { diff --git a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php b/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php index ed91bf56cd0e5..70426b5dd5125 100644 --- a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php @@ -20,6 +20,11 @@ class DoctrineAdapter extends AbstractAdapter { private $provider; + /** + * @param CacheProvider $provider + * @param string $namespace + * @param int $defaultLifetime + */ public function __construct(CacheProvider $provider, $namespace = '', $defaultLifetime = 0) { parent::__construct('', $defaultLifetime); diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php index 1c62641cf6d67..c7422d36a2164 100644 --- a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php @@ -20,6 +20,11 @@ class FilesystemAdapter extends AbstractAdapter { use FilesystemAdapterTrait; + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string|null $directory + */ public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) { parent::__construct('', $defaultLifetime); diff --git a/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php index 88107cb9899dc..2ce605a40fca0 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php @@ -29,6 +29,13 @@ public static function isSupported() return function_exists('opcache_invalidate') && ini_get('opcache.enable'); } + /** + * @param string $namespace + * @param int $defaultLifetime + * @param string|null $directory + * + * @throws CacheException if OPcache is not enabled + */ public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) { if (!static::isSupported()) { diff --git a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php index 2ed895c873a3e..4f37ffd731931 100644 --- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php @@ -26,6 +26,11 @@ class ProxyAdapter implements AdapterInterface private $createCacheItem; private $poolHash; + /** + * @param CacheItemPoolInterface $pool + * @param string $namespace + * @param int $defaultLifetime + */ public function __construct(CacheItemPoolInterface $pool, $namespace = '', $defaultLifetime = 0) { $this->pool = $pool; diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php index 50206bb278b52..8c6eacf4bbec5 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Cache\Tests\Adapter; +use Psr\Log\NullLogger; use Symfony\Component\Cache\Adapter\ApcuAdapter; class ApcuAdapterTest extends AdapterTestCase @@ -23,9 +24,14 @@ class ApcuAdapterTest extends AdapterTestCase public function createCachePool($defaultLifetime = 0) { - if (!function_exists('apcu_fetch') || !ini_get('apc.enabled') || ('cli' === PHP_SAPI && !ini_get('apc.enable_cli'))) { + if (!function_exists('apcu_fetch') || !ini_get('apc.enabled')) { $this->markTestSkipped('APCu extension is required.'); } + if ('cli' === PHP_SAPI && !ini_get('apc.enable_cli')) { + if ('testWithCliSapi' !== $this->getName()) { + $this->markTestSkipped('APCu extension is required.'); + } + } if ('\\' === DIRECTORY_SEPARATOR) { $this->markTestSkipped('Fails transiently on Windows.'); } @@ -70,4 +76,24 @@ public function testVersion() $this->assertFalse($item->isHit()); $this->assertNull($item->get()); } + + public function testWithCliSapi() + { + try { + // disable PHPUnit error handler to mimic a production environment + $isCalled = false; + set_error_handler(function () use (&$isCalled) { + $isCalled = true; + }); + $pool = new ApcuAdapter(str_replace('\\', '.', __CLASS__)); + $pool->setLogger(new NullLogger()); + + $item = $pool->getItem('foo'); + $item->isHit(); + $pool->save($item->set('bar')); + $this->assertFalse($isCalled); + } finally { + restore_error_handler(); + } + } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index a4e2e041b24fe..3bec493e91848 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -111,7 +111,7 @@ private function isInlineableDefinition($id, Definition $definition) return true; } - if ($definition->isPublic() || $definition->isLazy()) { + if ($definition->isDeprecated() || $definition->isPublic() || $definition->isLazy()) { return false; } diff --git a/src/Symfony/Component/DependencyInjection/Container.php b/src/Symfony/Component/DependencyInjection/Container.php index 2f96abca34097..1ed239b939747 100644 --- a/src/Symfony/Component/DependencyInjection/Container.php +++ b/src/Symfony/Component/DependencyInjection/Container.php @@ -28,16 +28,6 @@ * * Parameter and service keys are case insensitive. * - * A service id can contain lowercased letters, digits, underscores, and dots. - * Underscores are used to separate words, and dots to group services - * under namespaces: - * - * - * * A service can also be defined by creating a method named * getXXXService(), where XXX is the camelized version of the id: * diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index dd514962ea47c..2b7d0b2a6a3fe 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -389,15 +389,9 @@ private function addServiceReturn($id, $definition) */ private function addServiceInstance($id, Definition $definition) { - $class = $definition->getClass(); - - if ('\\' === substr($class, 0, 1)) { - $class = substr($class, 1); - } - - $class = $this->dumpValue($class); + $class = $this->dumpValue($definition->getClass()); - if (0 === strpos($class, "'") && false === strpos($class, '$') && !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { + if (0 === strpos($class, "'") && false === strpos($class, '$') && !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { throw new InvalidArgumentException(sprintf('"%s" is not a valid class name for the "%s" service.', $class, $id)); } @@ -1453,11 +1447,13 @@ private function dumpLiteralClass($class) if (false !== strpos($class, '$')) { return sprintf('${($_ = %s) && false ?: "_"}', $class); } - if (0 !== strpos($class, "'") || !preg_match('/^\'[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { + if (0 !== strpos($class, "'") || !preg_match('/^\'(?:\\\{2})?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(?:\\\{2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)*\'$/', $class)) { throw new RuntimeException(sprintf('Cannot dump definition because of invalid class name (%s)', $class ?: 'n/a')); } - return '\\'.substr(str_replace('\\\\', '\\', $class), 1, -1); + $class = substr(str_replace('\\\\', '\\', $class), 1, -1); + + return 0 === strpos($class, '\\') ? $class : '\\'.$class; } /** diff --git a/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php b/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php index acc97bcf4973d..d80985fa670e5 100644 --- a/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php +++ b/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php @@ -12,7 +12,6 @@ namespace Symfony\Component\DependencyInjection; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; -use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; /** * Adds some function to the default ExpressionLanguage. @@ -23,7 +22,10 @@ */ class ExpressionLanguage extends BaseExpressionLanguage { - public function __construct(ParserCacheInterface $cache = null, array $providers = array()) + /** + * {@inheritdoc} + */ + public function __construct($cache = null, array $providers = array()) { // prepend the default provider to let users override it easily array_unshift($providers, new ExpressionLanguageProvider()); diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index f8693684b8e12..e8673cee950c9 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -823,6 +823,27 @@ public function testAutowiring() $this->assertEquals('a', (string) $container->getDefinition('b')->getArgument(0)); } + + /** + * This test checks the trigger of a deprecation note and should not be removed in major releases. + * + * @group legacy + * @expectedDeprecation The "foo" service is deprecated. You should stop using it, as it will soon be removed. + */ + public function testPrivateServiceTriggersDeprecation() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass') + ->setPublic(false) + ->setDeprecated(true); + $container->register('bar', 'stdClass') + ->setPublic(true) + ->setProperty('foo', new Reference('foo')); + + $container->compile(); + + $container->get('bar'); + } } class FooClass diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php index 8d5eda9ce5fe8..4b31cb6fe0082 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php @@ -144,8 +144,8 @@ public function testGetLegacyServiceIds() public function testSet() { $sc = new Container(); - $sc->set('foo', $foo = new \stdClass()); - $this->assertSame($foo, $sc->get('foo'), '->set() sets a service'); + $sc->set('._. \\o/', $foo = new \stdClass()); + $this->assertSame($foo, $sc->get('._. \\o/'), '->set() sets a service'); } public function testSetWithNullResetTheService() diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index ee81219ef01d0..2ff01d120b169 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -424,4 +424,43 @@ public function testPrivateWithIgnoreOnInvalidReference() $container = new \Symfony_DI_PhpDumper_Test_Private_With_Ignore_On_Invalid_Reference(); $this->assertInstanceOf('BazClass', $container->get('bar')->getBaz()); } + + public function testDumpHandlesLiteralClassWithRootNamespace() + { + $container = new ContainerBuilder(); + $container->register('foo', '\\stdClass'); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Literal_Class_With_Root_Namespace'))); + + $container = new \Symfony_DI_PhpDumper_Test_Literal_Class_With_Root_Namespace(); + + $this->assertInstanceOf('stdClass', $container->get('foo')); + } + + /** + * This test checks the trigger of a deprecation note and should not be removed in major releases. + * + * @group legacy + * @expectedDeprecation The "foo" service is deprecated. You should stop using it, as it will soon be removed. + */ + public function testPrivateServiceTriggersDeprecation() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass') + ->setPublic(false) + ->setDeprecated(true); + $container->register('bar', 'stdClass') + ->setPublic(true) + ->setProperty('foo', new Reference('foo')); + + $container->compile(); + + $dumper = new PhpDumper($container); + eval('?>'.$dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Private_Service_Triggers_Deprecation'))); + + $container = new \Symfony_DI_PhpDumper_Test_Private_Service_Triggers_Deprecation(); + + $container->get('bar'); + } } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index 1a67a971358fe..9e3135460b700 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -37,7 +37,8 @@ class Filesystem */ public function copy($originFile, $targetFile, $overwriteNewerFiles = false) { - if (stream_is_local($originFile) && !is_file($originFile)) { + $originIsLocal = stream_is_local($originFile) || 0 === stripos($originFile, 'file://'); + if ($originIsLocal && !is_file($originFile)) { throw new FileNotFoundException(sprintf('Failed to copy "%s" because file does not exist.', $originFile), 0, null, $originFile); } @@ -68,11 +69,13 @@ public function copy($originFile, $targetFile, $overwriteNewerFiles = false) throw new IOException(sprintf('Failed to copy "%s" to "%s".', $originFile, $targetFile), 0, null, $originFile); } - // Like `cp`, preserve executable permission bits - @chmod($targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111)); + if ($originIsLocal) { + // Like `cp`, preserve executable permission bits + @chmod($targetFile, fileperms($targetFile) | (fileperms($originFile) & 0111)); - if (stream_is_local($originFile) && $bytesCopied !== ($bytesOrigin = filesize($originFile))) { - throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile); + if ($bytesCopied !== $bytesOrigin = filesize($originFile)) { + throw new IOException(sprintf('Failed to copy the whole content of "%s" to "%s" (%g of %g bytes copied).', $originFile, $targetFile, $bytesCopied, $bytesOrigin), 0, null, $originFile); + } } } } diff --git a/src/Symfony/Component/HttpFoundation/Cookie.php b/src/Symfony/Component/HttpFoundation/Cookie.php index be13dab85e46d..492c6fbe75193 100644 --- a/src/Symfony/Component/HttpFoundation/Cookie.php +++ b/src/Symfony/Component/HttpFoundation/Cookie.php @@ -96,7 +96,7 @@ public function __toString() if ('' === (string) $this->getValue()) { $str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001); } else { - $str .= $this->isRaw() ? $this->getValue() : urlencode($this->getValue()); + $str .= $this->isRaw() ? $this->getValue() : rawurlencode($this->getValue()); if (0 !== $this->getExpiresTime()) { $str .= '; expires='.gmdate('D, d-M-Y H:i:s T', $this->getExpiresTime()); diff --git a/src/Symfony/Component/HttpFoundation/RedirectResponse.php b/src/Symfony/Component/HttpFoundation/RedirectResponse.php index 7435999a019f6..cb1c58e8999a5 100644 --- a/src/Symfony/Component/HttpFoundation/RedirectResponse.php +++ b/src/Symfony/Component/HttpFoundation/RedirectResponse.php @@ -87,7 +87,7 @@ public function setTargetUrl($url) - + Redirecting to %1$s diff --git a/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php b/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php index c2d1c9b7527a7..3986a7e8f8796 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/CookieTest.php @@ -160,6 +160,9 @@ public function testToString() $cookie = new Cookie('foo', 'bar', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true); $this->assertEquals('foo=bar; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() returns string representation of the cookie'); + $cookie = new Cookie('foo', 'bar with white spaces', strtotime('Fri, 20-May-2011 15:25:52 GMT'), '/', '.myfoodomain.com', true); + $this->assertEquals('foo=bar%20with%20white%20spaces; expires=Fri, 20-May-2011 15:25:52 GMT; path=/; domain=.myfoodomain.com; secure; httponly', (string) $cookie, '->__toString() encodes the value of the cookie according to RFC 3986 (white space = %20)'); + $cookie = new Cookie('foo', null, 1, '/admin/', '.myfoodomain.com'); $this->assertEquals('foo=deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; path=/admin/; domain=.myfoodomain.com; httponly', (string) $cookie, '->__toString() returns string representation of a cleared cookie if value is NULL'); @@ -171,7 +174,7 @@ public function testRawCookie() { $cookie = new Cookie('foo', 'b a r', 0, '/', null, false, false); $this->assertFalse($cookie->isRaw()); - $this->assertEquals('foo=b+a+r; path=/', (string) $cookie); + $this->assertEquals('foo=b%20a%20r; path=/', (string) $cookie); $cookie = new Cookie('foo', 'b+a+r', 0, '/', null, false, false, true); $this->assertTrue($cookie->isRaw()); diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index bdfd2b2d9c4e7..d0acb4af856a4 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -58,11 +58,11 @@ abstract class Kernel implements KernelInterface, TerminableInterface protected $startTime; protected $loadClassCache; - const VERSION = '3.2.11'; - const VERSION_ID = 30211; + const VERSION = '3.2.12'; + const VERSION_ID = 30212; const MAJOR_VERSION = 3; const MINOR_VERSION = 2; - const RELEASE_VERSION = 11; + const RELEASE_VERSION = 12; const EXTRA_VERSION = ''; const END_OF_MAINTENANCE = '07/2017'; diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index 9a306e533e006..e642e3c33715f 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -50,7 +50,7 @@ public function testDump() ); $this->assertEquals($xDump, $dump); - $this->assertStringMatchesFormat('a:3:{i:0;a:5:{s:4:"data";O:39:"Symfony\Component\VarDumper\Cloner\Data":%a', $collector->serialize()); + $this->assertStringMatchesFormat('a:3:{i:0;a:5:{s:4:"data";%c:39:"Symfony\Component\VarDumper\Cloner\Data":%a', $collector->serialize()); $this->assertSame(0, $collector->getDumpsCount()); $this->assertSame('a:2:{i:0;b:0;i:1;s:5:"UTF-8";}', $collector->serialize()); } diff --git a/src/Symfony/Component/PropertyInfo/composer.json b/src/Symfony/Component/PropertyInfo/composer.json index 23069a1e79870..f9df8807396c3 100644 --- a/src/Symfony/Component/PropertyInfo/composer.json +++ b/src/Symfony/Component/PropertyInfo/composer.json @@ -33,7 +33,7 @@ "doctrine/annotations": "~1.0" }, "conflict": { - "phpdocumentor/reflection-docblock": "<3.0", + "phpdocumentor/reflection-docblock": "<3.0||>=3.2.0", "phpdocumentor/type-resolver": "<0.2.0" }, "suggest": { diff --git a/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php b/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php index 16de8daaeda93..4c0a7459d070c 100644 --- a/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php +++ b/src/Symfony/Component/Security/Core/Authentication/AuthenticationProviderManager.php @@ -83,9 +83,9 @@ public function authenticate(TokenInterface $token) break; } } catch (AccountStatusException $e) { - $e->setToken($token); + $lastException = $e; - throw $e; + break; } catch (AuthenticationException $e) { $lastException = $e; } diff --git a/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php b/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php index c2925af87121a..d4c8f33e06491 100644 --- a/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php +++ b/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php @@ -12,7 +12,6 @@ namespace Symfony\Component\Security\Core\Authorization; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; -use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface; /** * Adds some function to the default ExpressionLanguage. @@ -23,7 +22,10 @@ */ class ExpressionLanguage extends BaseExpressionLanguage { - public function __construct(ParserCacheInterface $cache = null, array $providers = array()) + /** + * {@inheritdoc} + */ + public function __construct($cache = null, array $providers = array()) { // prepend the default provider to let users override it easily array_unshift($providers, new ExpressionLanguageProvider()); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php index 9b8105012c3d8..373369d455959 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/AuthenticationProviderManagerTest.php @@ -13,6 +13,9 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager; +use Symfony\Component\Security\Core\AuthenticationEvents; +use Symfony\Component\Security\Core\Event\AuthenticationEvent; +use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent; use Symfony\Component\Security\Core\Exception\ProviderNotFoundException; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\AccountStatusException; @@ -124,6 +127,50 @@ public function testEraseCredentialFlag() $this->assertEquals('bar', $token->getCredentials()); } + public function testAuthenticateDispatchesAuthenticationFailureEvent() + { + $token = new UsernamePasswordToken('foo', 'bar', 'key'); + $provider = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface')->getMock(); + $provider->expects($this->once())->method('supports')->willReturn(true); + $provider->expects($this->once())->method('authenticate')->willThrowException($exception = new AuthenticationException()); + + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); + $dispatcher + ->expects($this->once()) + ->method('dispatch') + ->with(AuthenticationEvents::AUTHENTICATION_FAILURE, $this->equalTo(new AuthenticationFailureEvent($token, $exception))); + + $manager = new AuthenticationProviderManager(array($provider)); + $manager->setEventDispatcher($dispatcher); + + try { + $manager->authenticate($token); + $this->fail('->authenticate() should rethrow exceptions'); + } catch (AuthenticationException $e) { + $this->assertSame($token, $exception->getToken()); + } + } + + public function testAuthenticateDispatchesAuthenticationSuccessEvent() + { + $token = new UsernamePasswordToken('foo', 'bar', 'key'); + + $provider = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface')->getMock(); + $provider->expects($this->once())->method('supports')->willReturn(true); + $provider->expects($this->once())->method('authenticate')->willReturn($token); + + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')->getMock(); + $dispatcher + ->expects($this->once()) + ->method('dispatch') + ->with(AuthenticationEvents::AUTHENTICATION_SUCCESS, $this->equalTo(new AuthenticationEvent($token))); + + $manager = new AuthenticationProviderManager(array($provider)); + $manager->setEventDispatcher($dispatcher); + + $this->assertSame($token, $manager->authenticate($token)); + } + protected function getAuthenticationProvider($supports, $token = null, $exception = null) { $provider = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface')->getMock(); diff --git a/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php b/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php index 7ebe65c647d99..942a4a6350f27 100644 --- a/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Validator/Constraints/UserPasswordValidatorTest.php @@ -90,6 +90,29 @@ public function testPasswordIsNotValid() ->assertRaised(); } + /** + * @dataProvider emptyPasswordData + */ + public function testEmptyPasswordsAreNotValid($password) + { + $constraint = new UserPassword(array( + 'message' => 'myMessage', + )); + + $this->validator->validate($password, $constraint); + + $this->buildViolation('myMessage') + ->assertRaised(); + } + + public function emptyPasswordData() + { + return array( + array(null), + array(''), + ); + } + /** * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException */ diff --git a/src/Symfony/Component/Security/Core/User/UserProviderInterface.php b/src/Symfony/Component/Security/Core/User/UserProviderInterface.php index 146ed65ad303d..9570edc2776ab 100644 --- a/src/Symfony/Component/Security/Core/User/UserProviderInterface.php +++ b/src/Symfony/Component/Security/Core/User/UserProviderInterface.php @@ -48,7 +48,7 @@ interface UserProviderInterface public function loadUserByUsername($username); /** - * Refreshes the user for the account interface. + * Refreshes the user. * * It is up to the implementation to decide if the user data should be * totally reloaded (e.g. from the database), or if the UserInterface @@ -59,7 +59,7 @@ public function loadUserByUsername($username); * * @return UserInterface * - * @throws UnsupportedUserException if the account is not supported + * @throws UnsupportedUserException if the user is not supported */ public function refreshUser(UserInterface $user); diff --git a/src/Symfony/Component/Security/Core/Validator/Constraints/UserPasswordValidator.php b/src/Symfony/Component/Security/Core/Validator/Constraints/UserPasswordValidator.php index 5f4c146cab469..c2ab13b2f6d29 100644 --- a/src/Symfony/Component/Security/Core/Validator/Constraints/UserPasswordValidator.php +++ b/src/Symfony/Component/Security/Core/Validator/Constraints/UserPasswordValidator.php @@ -40,6 +40,8 @@ public function validate($password, Constraint $constraint) } if (null === $password || '' === $password) { + $this->context->addViolation($constraint->message); + return; } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 2b3433c567d4a..188b79ade85ff 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -13,7 +13,6 @@ 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; use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface; @@ -52,8 +51,6 @@ public function supportsNormalization($data, $format = null) /** * {@inheritdoc} - * - * @throws CircularReferenceException */ public function normalize($object, $format = null, array $context = array()) { diff --git a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php index 7d3d87c510c55..9217d51a95908 100644 --- a/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php @@ -31,8 +31,6 @@ class ArrayDenormalizer implements DenormalizerInterface, SerializerAwareInterfa /** * {@inheritdoc} - * - * @throws UnexpectedValueException */ public function denormalize($data, $class, $format = null, array $context = array()) { diff --git a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php index 988a491b7c800..9e5af130d03b7 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/DataUriNormalizer.php @@ -79,9 +79,6 @@ public function supportsNormalization($data, $format = null) * Regex adapted from Brian Grinstead code. * * @see https://gist.github.com/bgrins/6194623 - * - * @throws InvalidArgumentException - * @throws UnexpectedValueException */ public function denormalize($data, $class, $format = null, array $context = array()) { diff --git a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php index 23df4829a8cbd..2b3b206287373 100644 --- a/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/DenormalizerInterface.php @@ -11,6 +11,13 @@ namespace Symfony\Component\Serializer\Normalizer; +use Symfony\Component\Serializer\Exception\BadMethodCallException; +use Symfony\Component\Serializer\Exception\ExtraAttributesException; +use Symfony\Component\Serializer\Exception\InvalidArgumentException; +use Symfony\Component\Serializer\Exception\LogicException; +use Symfony\Component\Serializer\Exception\RuntimeException; +use Symfony\Component\Serializer\Exception\UnexpectedValueException; + /** * Defines the interface of denormalizers. * @@ -27,6 +34,13 @@ interface DenormalizerInterface * @param array $context options available to the denormalizer * * @return object + * + * @throws BadMethodCallException Occurs when the normalizer is not called in an expected context + * @throws InvalidArgumentException Occurs when the arguments are not coherent or not supported + * @throws UnexpectedValueException Occurs when the item cannot be hydrated with the given data + * @throws ExtraAttributesException Occurs when the item doesn't have attribute to receive given data + * @throws LogicException Occurs when the normalizer is not supposed to denormalize + * @throws RuntimeException Occurs if the class cannot be instantiated */ public function denormalize($data, $class, $format = null, array $context = array()); diff --git a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php index f7007840da1f4..2779053f741e9 100644 --- a/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php +++ b/src/Symfony/Component/Serializer/Normalizer/NormalizerInterface.php @@ -11,6 +11,10 @@ namespace Symfony\Component\Serializer\Normalizer; +use Symfony\Component\Serializer\Exception\CircularReferenceException; +use Symfony\Component\Serializer\Exception\InvalidArgumentException; +use Symfony\Component\Serializer\Exception\LogicException; + /** * Defines the interface of normalizers. * @@ -26,6 +30,11 @@ interface NormalizerInterface * @param array $context Context options for the normalizer * * @return array|scalar + * + * @throws InvalidArgumentException Occurs when the object given is not an attempted type for the normalizer + * @throws CircularReferenceException Occurs when the normalizer detects a circular reference when no circular + * reference handler can fix it + * @throws LogicException Occurs when the normalizer is not called in an expected context */ public function normalize($object, $format = null, array $context = array());