diff --git a/.travis.yml b/.travis.yml
index b725715a41..76fd27aef3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,10 +6,15 @@ php:
- 5.4
- 5.5
+services: mongodb
+
before_script:
- sudo apt-get install parallel
- echo '' > ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini
+ - echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- sh -c 'if [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi;'
+ - echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
+ - echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- COMPOSER_ROOT_VERSION=dev-master composer --prefer-source --dev install
script:
diff --git a/CHANGELOG-2.3.md b/CHANGELOG-2.3.md
index 4f872ad96e..1ea4d10300 100644
--- a/CHANGELOG-2.3.md
+++ b/CHANGELOG-2.3.md
@@ -7,6 +7,39 @@ in 2.3 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/v2.3.0...v2.3.1
+* 2.3.8 (2013-12-16)
+
+ * bug #9758 [Console] fixed TableHelper when cell value has new line (k-przybyszewski)
+ * bug #9760 [Routing] Fix router matching pattern against multiple hosts (karolsojko)
+ * bug #9674 [Form] rename validators.ua.xlf to validators.uk.xlf (craue)
+ * bug #9722 [Validator]Fixed getting wrong msg when value is an object in Exception (aitboudad)
+ * bug #9750 allow TraceableEventDispatcher to reuse event instance in nested events (evillemez)
+ * bug #9718 [validator] throw an exception if isn't an instance of ConstraintValidatorInterface. (aitboudad)
+ * bug #9716 Reset the box model to content-box in the web debug toolbar (stof)
+ * bug #9711 [FrameworkBundle] Allowed "0" as a checkbox value in php templates (jakzal)
+ * bug #9665 [Bridge/Doctrine] ORMQueryBuilderLoader - handled the scenario when no entity manager is passed with closure query builder (jakzal)
+ * bug #9656 [DoctrineBridge] normalized class names in the ORM type guesser (fabpot)
+ * bug #9647 use the correct class name to retrieve mapped class' metadata and reposi... (xabbuh)
+ * bug #9648 [Debug] ensured that a fatal PHP error is actually fatal after being handled by our error handler (fabpot)
+ * bug #9643 [WebProfilerBundle] Fixed js escaping in time.html.twig (hason)
+ * bug #9641 [Debug] Avoid notice from being "eaten" by fatal error. (fabpot)
+ * bug #9639 Modified guessDefaultEscapingStrategy to not escape txt templates (fabpot)
+ * bug #9314 [Form] Fix DateType for 32bits computers. (WedgeSama)
+ * bug #9443 [FrameworkBundle] Fixed the registration of validation.xml file when the form is disabled (hason)
+ * bug #9625 [HttpFoundation] Do not return an empty session id if the session was closed (Taluu)
+ * bug #9637 [Validator] Replaced inexistent interface (jakzal)
+ * bug #9605 Adjusting CacheClear Warmup method to namespaced kernels (rdohms)
+ * bug #9610 Container::camelize also takes backslashes into consideration (ondrejmirtes)
+ * bug #9447 [BrowserKit] fixed protocol-relative url redirection (jong99)
+ * bug #9535 No Entity Manager defined exception (armetiz)
+ * bug #9485 [Acl] Fix for issue #9433 (guilro)
+ * bug #9516 [AclProvider] Fix incorrect behavior when partial results returned from cache (superdav42)
+ * bug #9352 [Intl] make currency bundle merge fallback locales when accessing data, ... (shieldo)
+ * bug #9537 [FrameworkBundle] Fix mistake in translation's service definition. (phpmike)
+ * bug #9367 [Process] Check if the pipe array is empty before calling stream_select() (jfposton)
+ * bug #9211 [Form] Fixed memory leak in FormValidator (bschussek)
+ * bug #9469 [Propel1] re-factor Propel1 ModelChoiceList (havvg)
+
* 2.3.7 (2013-11-14)
* bug #9499 Request::overrideGlobals() may call invalid ini value (denkiryokuhatsuden)
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 24834c7652..a14c40aaa2 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -58,8 +58,8 @@ Symfony2 is the result of the work of many people who made the code better
- Bart van den Burg (burgov)
- Antoine Hérault (herzult)
- Toni Uebernickel (havvg)
- - Michel Weimerskirch (mweimerskirch)
- Christian Raue
+ - Michel Weimerskirch (mweimerskirch)
- Arnaud Le Blanc (arnaud-lb)
- marc.weistroff
- Brice BERNARD (brikou)
@@ -68,10 +68,10 @@ Symfony2 is the result of the work of many people who made the code better
- Włodzimierz Gajda (gajdaw)
- Colin Frei
- excelwebzone
+ - Wouter De Jong (wouterj)
- Kevin Bond (kbond)
- Fabien Pennequin (fabienpennequin)
- Jacob Dreesen (jdreesen)
- - Wouter De Jong (wouterj)
- Adrien Brault (adrienbrault)
- Michal Piotrowski (eventhorizon)
- Robert Schönthal (digitalkaoz)
@@ -94,6 +94,7 @@ Symfony2 is the result of the work of many people who made the code better
- Amal Raghav (kertz)
- Jonathan Ingram (jonathaningram)
- Artur Kotyrba
+ - Ait Boudad Abdellatif (aitboudad)
- Pablo Godel (pgodel)
- Sebastiaan Stok (sstok)
- Dmitrii Chekaliuk (lazyhammer)
@@ -113,7 +114,6 @@ Symfony2 is the result of the work of many people who made the code better
- Guilherme Blanco (guilhermeblanco)
- Martin Schuhfuß (usefulthink)
- Thomas Rabaix (rande)
- - Ait Boudad Abdellatif (aitboudad)
- Matthieu Bontemps (mbontemps)
- Pierre Minnieur (pminnieur)
- fivestar
@@ -141,6 +141,7 @@ Symfony2 is the result of the work of many people who made the code better
- Alif Rachmawadi
- Joseph Rouff (rouffj)
- Félix Labrecque (woodspire)
+ - Christian Flothmann (xabbuh)
- GordonsLondon
- Jan Sorgalla (jsor)
- Ray
@@ -160,8 +161,8 @@ Symfony2 is the result of the work of many people who made the code better
- Florian Klein (docteurklein)
- Matthias Pigulla (mpdude)
- Manuel Kiessling (manuelkiessling)
- - Christian Flothmann (xabbuh)
- Bertrand Zuchuat (garfield-fr)
+ - Gabor Toth (tgabi333)
- Thomas Tourlourat (armetiz)
- Andrey Esaulov (andremaha)
- Grégoire Passault (gregwar)
@@ -169,6 +170,7 @@ Symfony2 is the result of the work of many people who made the code better
- Aurelijus Valeiša (aurelijus)
- Gustavo Piltcher
- Stepan Tanasiychuk (stfalcon)
+ - Luis Cordova (cordoval)
- Bob den Otter (bopp)
- Adrian Rudnik (kreischweide)
- Francesc Rosàs (frosas)
@@ -182,6 +184,7 @@ Symfony2 is the result of the work of many people who made the code better
- Vitaliy Zakharov (zakharovvi)
- Gyula Sallai (salla)
- Michele Orselli (orso)
+ - Christian Gärtner (dagardner)
- Tom Van Looy (tvlooy)
- Peter Kruithof (pkruithof)
- Felix Labrecque
@@ -196,7 +199,6 @@ Symfony2 is the result of the work of many people who made the code better
- Atsuhiro KUBO (iteman)
- sun (sun)
- Lars Strojny
- - Gabor Toth (tgabi333)
- Costin Bereveanu (schniper)
- realmfoo
- Tamas Szijarto
@@ -209,7 +211,6 @@ Symfony2 is the result of the work of many people who made the code better
- Kai
- Xavier HAUSHERR
- Albert Jessurum (ajessu)
- - Luis Cordova (cordoval)
- Laszlo Korte
- Tiago Ribeiro (fixe)
- Alessandro Desantis
@@ -235,7 +236,6 @@ Symfony2 is the result of the work of many people who made the code better
- Olivier Dolbeau (odolbeau)
- alquerci
- vagrant
- - Christian Gärtner (dagardner)
- Asier Illarramendi (doup)
- Christoph Mewes (xrstf)
- Vitaliy Tverdokhlib (vitaliytv)
@@ -276,6 +276,7 @@ Symfony2 is the result of the work of many people who made the code better
- sasezaki
- Denis Gorbachev (starfall)
- Steven Surowiec
+ - giulio de donato (liuggio)
- Marek Kalnik (marekkalnik)
- Chris Smith
- Anthon Pang
@@ -406,7 +407,6 @@ Symfony2 is the result of the work of many people who made the code better
- Timothée Barray (tyx)
- Christian Morgan
- Alexander Miehe (engerim)
- - giulio de donato (liuggio)
- Titouan Galopin (tgalopin)
- Don Pinkster
- Maksim Muruev
@@ -419,6 +419,7 @@ Symfony2 is the result of the work of many people who made the code better
- Arno Geurts
- Adán Lobato (adanlobato)
- Maks
+ - Gábor Tóth
- Daniel Cestari
- Magnus Nordlander (magnusnordlander)
- Mikhail Yurasov (mym)
@@ -575,6 +576,7 @@ Symfony2 is the result of the work of many people who made the code better
- Rick Prent
- Martin Eckhardt
- Michael Dowling (mtdowling)
+ - Nicolas Grekas (nicolas-grekas)
- BilgeXA
- Robert Queck
- mlively
@@ -746,6 +748,7 @@ Symfony2 is the result of the work of many people who made the code better
- Yorkie Chadwick (yorkie76)
- Yanick Witschi
- Ondrej Mirtes
+ - Youpie
- srsbiz
- Nicolas A. Bérard-Nault
- Gladhon
@@ -791,7 +794,6 @@ Symfony2 is the result of the work of many people who made the code better
- Kaipi Yann
- Sam Williams
- James Michael DuPont
- - Gábor Tóth
- Tammy D
- Ondrej Slinták
- vlechemin
@@ -800,8 +802,10 @@ Symfony2 is the result of the work of many people who made the code better
- mieszko4
- datibbaw
- Norbert Orzechowicz
+ - Markus Staab
- Pierre-Louis LAUNAY
- djama
+ - Eduardo Conceição
- Jon Cave
- Sébastien HOUZE
- Abdulkadir N. A.
diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php
index fe8fc709a6..282f860262 100644
--- a/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php
+++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Routing/RedirectableUrlMatcherTest.php
@@ -38,7 +38,7 @@ class RedirectableUrlMatcherTest extends \PHPUnit_Framework_TestCase
);
}
- public function testSchemeRedirect()
+ public function testSchemeRedirectBC()
{
$coll = new RouteCollection();
$coll->add('foo', new Route('/foo', array(), array('_scheme' => 'https')));
@@ -57,4 +57,24 @@ class RedirectableUrlMatcherTest extends \PHPUnit_Framework_TestCase
$matcher->match('/foo')
);
}
+
+ public function testSchemeRedirect()
+ {
+ $coll = new RouteCollection();
+ $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https')));
+
+ $matcher = new RedirectableUrlMatcher($coll, $context = new RequestContext());
+
+ $this->assertEquals(array(
+ '_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction',
+ 'path' => '/foo',
+ 'permanent' => true,
+ 'scheme' => 'https',
+ 'httpPort' => $context->getHttpPort(),
+ 'httpsPort' => $context->getHttpsPort(),
+ '_route' => 'foo',
+ ),
+ $matcher->match('/foo')
+ );
+ }
}
diff --git a/src/Symfony/Component/BrowserKit/Cookie.php b/src/Symfony/Component/BrowserKit/Cookie.php
index 8434299a8e..bce5613cf7 100644
--- a/src/Symfony/Component/BrowserKit/Cookie.php
+++ b/src/Symfony/Component/BrowserKit/Cookie.php
@@ -127,7 +127,7 @@ class Cookie
$parts = explode(';', $cookie);
if (false === strpos($parts[0], '=')) {
- throw new \InvalidArgumentException(sprintf('The cookie string "%s" is not valid.', $cookie));
+ throw new \InvalidArgumentException(sprintf('The cookie string "%s" is not valid.', $parts[0]));
}
list($name, $value) = explode('=', array_shift($parts), 2);
diff --git a/src/Symfony/Component/BrowserKit/CookieJar.php b/src/Symfony/Component/BrowserKit/CookieJar.php
index 6288fbc116..4ecfddbf4a 100644
--- a/src/Symfony/Component/BrowserKit/CookieJar.php
+++ b/src/Symfony/Component/BrowserKit/CookieJar.php
@@ -55,7 +55,25 @@ class CookieJar
$this->flushExpiredCookies();
if (!empty($domain)) {
- return isset($this->cookieJar[$domain][$path][$name]) ? $this->cookieJar[$domain][$path][$name] : null;
+ foreach ($this->cookieJar as $cookieDomain => $pathCookies) {
+ if ($cookieDomain) {
+ $cookieDomain = '.'.ltrim($cookieDomain, '.');
+ if ($cookieDomain != substr('.'.$domain, -strlen($cookieDomain))) {
+ continue;
+ }
+ }
+
+ foreach ($pathCookies as $cookiePath => $namedCookies) {
+ if ($cookiePath != substr($path, 0, strlen($cookiePath))) {
+ continue;
+ }
+ if (isset($namedCookies[$name])) {
+ return $namedCookies[$name];
+ }
+ }
+ }
+
+ return null;
}
// avoid relying on this behavior that is mainly here for BC reasons
diff --git a/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.php b/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.php
index 77c5ca49bb..843e49709a 100644
--- a/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.php
+++ b/src/Symfony/Component/BrowserKit/Tests/CookieJarTest.php
@@ -196,6 +196,30 @@ class CookieJarTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array('foo' => 'bar2'), $cookieJar->allValues('http://bar.example.com/'));
}
+ public function testCookieGetWithSubdomain()
+ {
+ $cookieJar = new CookieJar();
+ $cookieJar->set($cookie1 = new Cookie('foo', 'bar', null, '/', '.example.com'));
+ $cookieJar->set($cookie2 = new Cookie('foo1', 'bar', null, '/', 'test.example.com'));
+
+ $this->assertEquals($cookie1, $cookieJar->get('foo','/','foo.example.com'));
+ $this->assertEquals($cookie1, $cookieJar->get('foo','/','example.com'));
+ $this->assertEquals($cookie2, $cookieJar->get('foo1','/','test.example.com'));
+ }
+
+ public function testCookieGetWithSubdirectory()
+ {
+ $cookieJar = new CookieJar();
+ $cookieJar->set($cookie1 = new Cookie('foo', 'bar', null, '/test', '.example.com'));
+ $cookieJar->set($cookie2 = new Cookie('foo1', 'bar1', null, '/', '.example.com'));
+
+ $this->assertNull($cookieJar->get('foo','/','.example.com'));
+ $this->assertNull($cookieJar->get('foo','/bar','.example.com'));
+ $this->assertEquals($cookie1, $cookieJar->get('foo','/test','example.com'));
+ $this->assertEquals($cookie2, $cookieJar->get('foo1','/','example.com'));
+ $this->assertEquals($cookie2, $cookieJar->get('foo1','/bar','example.com'));
+ }
+
public function testCookieWithWildcardDomain()
{
$cookieJar = new CookieJar();
diff --git a/src/Symfony/Component/Config/Definition/ArrayNode.php b/src/Symfony/Component/Config/Definition/ArrayNode.php
index 4056bfcce7..6017194421 100644
--- a/src/Symfony/Component/Config/Definition/ArrayNode.php
+++ b/src/Symfony/Component/Config/Definition/ArrayNode.php
@@ -200,7 +200,7 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
public function addChild(NodeInterface $node)
{
$name = $node->getName();
- if (empty($name)) {
+ if (!strlen($name)) {
throw new \InvalidArgumentException('Child nodes must be named.');
}
if (isset($this->children[$name])) {
@@ -293,9 +293,9 @@ class ArrayNode extends BaseNode implements PrototypeNodeInterface
$value = $this->remapXml($value);
$normalized = array();
- foreach ($this->children as $name => $child) {
- if (array_key_exists($name, $value)) {
- $normalized[$name] = $child->normalize($value[$name]);
+ foreach ($value as $name => $val) {
+ if (isset($this->children[$name])) {
+ $normalized[$name] = $this->children[$name]->normalize($val);
unset($value[$name]);
}
}
diff --git a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php
index 34a2aece5e..39bc627a56 100644
--- a/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php
+++ b/src/Symfony/Component/Config/Tests/Definition/ArrayNodeTest.php
@@ -12,6 +12,8 @@
namespace Symfony\Component\Config\Tests\Definition;
use Symfony\Component\Config\Definition\ArrayNode;
+use Symfony\Component\Config\Definition\ScalarNode;
+use Symfony\Component\Serializer\Tests\Fixtures\ScalarDummy;
class ArrayNodeTest extends \PHPUnit_Framework_TestCase
{
@@ -79,4 +81,81 @@ class ArrayNodeTest extends \PHPUnit_Framework_TestCase
)
);
}
+
+ /**
+ * @dataProvider getZeroNamedNodeExamplesData
+ */
+ public function testNodeNameCanBeZero($denormalized, $normalized)
+ {
+ $zeroNode = new ArrayNode(0);
+ $zeroNode->addChild(new ScalarNode('name'));
+ $fiveNode = new ArrayNode(5);
+ $fiveNode->addChild(new ScalarNode(0));
+ $fiveNode->addChild(new ScalarNode('new_key'));
+ $rootNode = new ArrayNode('root');
+ $rootNode->addChild($zeroNode);
+ $rootNode->addChild($fiveNode);
+ $rootNode->addChild(new ScalarNode('string_key'));
+ $r = new \ReflectionMethod($rootNode, 'normalizeValue');
+ $r->setAccessible(true);
+
+ $this->assertSame($normalized, $r->invoke($rootNode, $denormalized));
+ }
+
+ public function getZeroNamedNodeExamplesData()
+ {
+ return array(
+ array(
+ array(
+ 0 => array(
+ 'name' => 'something',
+ ),
+ 5 => array(
+ 0 => 'this won\'t work too',
+ 'new_key' => 'some other value',
+ ),
+ 'string_key' => 'just value',
+ ),
+ array(
+ 0 => array (
+ 'name' => 'something',
+ ),
+ 5 => array (
+ 0 => 'this won\'t work too',
+ 'new_key' => 'some other value',
+ ),
+ 'string_key' => 'just value',
+ ),
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider getPreNormalizedNormalizedOrderedData
+ */
+ public function testChildrenOrderIsMaintainedOnNormalizeValue($prenormalized, $normalized)
+ {
+ $scalar1 = new ScalarNode('1');
+ $scalar2 = new ScalarNode('2');
+ $scalar3 = new ScalarNode('3');
+ $node = new ArrayNode('foo');
+ $node->addChild($scalar1);
+ $node->addChild($scalar3);
+ $node->addChild($scalar2);
+
+ $r = new \ReflectionMethod($node, 'normalizeValue');
+ $r->setAccessible(true);
+
+ $this->assertSame($normalized, $r->invoke($node, $prenormalized));
+ }
+
+ public function getPreNormalizedNormalizedOrderedData()
+ {
+ return array(
+ array(
+ array('2' => 'two', '1' => 'one', '3' => 'three'),
+ array('2' => 'two', '1' => 'one', '3' => 'three'),
+ ),
+ );
+ }
}
diff --git a/src/Symfony/Component/Console/Helper/TableHelper.php b/src/Symfony/Component/Console/Helper/TableHelper.php
index e1b1e149a1..bd95b3920b 100644
--- a/src/Symfony/Component/Console/Helper/TableHelper.php
+++ b/src/Symfony/Component/Console/Helper/TableHelper.php
@@ -161,6 +161,29 @@ class TableHelper extends Helper
{
$this->rows[] = array_values($row);
+ $keys = array_keys($this->rows);
+ $rowKey = array_pop($keys);
+
+ foreach ($row as $key => $cellValue) {
+ if (!strstr($cellValue, "\n")) {
+ continue;
+ }
+
+ $lines = explode("\n", $cellValue);
+ $this->rows[$rowKey][$key] = $lines[0];
+ unset($lines[0]);
+
+ foreach ($lines as $lineKey => $line) {
+ $nextRowKey = $rowKey + $lineKey + 1;
+
+ if (isset($this->rows[$nextRowKey])) {
+ $this->rows[$nextRowKey][$key] = $line;
+ } else {
+ $this->rows[$nextRowKey] = array($key => $line);
+ }
+ }
+ }
+
return $this;
}
diff --git a/src/Symfony/Component/Console/Tests/Helper/TableHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/TableHelperTest.php
index 0c9629aa90..343fb45e3a 100644
--- a/src/Symfony/Component/Console/Tests/Helper/TableHelperTest.php
+++ b/src/Symfony/Component/Console/Tests/Helper/TableHelperTest.php
@@ -172,6 +172,31 @@ TABLE
| 80-902734-1-6 | And Then There Were None | Agatha Christie |
+---------------+--------------------------+------------------+
+TABLE
+ ),
+ array(
+ array('ISBN', 'Title', 'Author'),
+ array(
+ array("99921-58-10-7", "Divine\nComedy", "Dante Alighieri"),
+ array("9971-5-0210-2", "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."),
+ array("9971-5-0210-2", "Harry Potter\nand the Chamber of Secrets", "Rowling\nJoanne K."),
+ array("960-425-059-0", "The Lord of the Rings", "J. R. R.\nTolkien"),
+ ),
+ TableHelper::LAYOUT_DEFAULT,
+<<
services[$id] = $service;
- if (method_exists($this, $method = 'synchronize'.strtr($id, array('_' => '', '.' => '_')).'Service')) {
+ if (method_exists($this, $method = 'synchronize'.strtr($id, array('_' => '', '.' => '_', '\\' => '_')).'Service')) {
$this->$method();
}
@@ -236,7 +236,7 @@ class Container implements IntrospectableContainerInterface
return isset($this->services[$id])
|| array_key_exists($id, $this->services)
|| isset($this->aliases[$id])
- || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_')).'Service')
+ || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_', '\\' => '_')).'Service')
;
}
@@ -264,7 +264,7 @@ class Container implements IntrospectableContainerInterface
// Attempt to retrieve the service by checking first aliases then
// available services. Service IDs are case insensitive, however since
// this method can be called thousands of times during a request, avoid
- // calling strotolower() unless necessary.
+ // calling strtolower() unless necessary.
foreach (array(false, true) as $strtolower) {
if ($strtolower) {
$id = strtolower($id);
@@ -284,7 +284,7 @@ class Container implements IntrospectableContainerInterface
if (isset($this->methodMap[$id])) {
$method = $this->methodMap[$id];
- } elseif (method_exists($this, $method = 'get'.strtr($id, array('_' => '', '.' => '_')).'Service')) {
+ } elseif (method_exists($this, $method = 'get'.strtr($id, array('_' => '', '.' => '_', '\\' => '_')).'Service')) {
// $method is set to the right value, proceed
} else {
if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
diff --git a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
index f9a1c2a2eb..9b2d46d28e 100644
--- a/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
+++ b/src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php
@@ -187,6 +187,10 @@ class XmlFileLoader extends FileLoader
continue;
}
+ if (false !== strpos($name, '-') && false === strpos($name, '_') && !array_key_exists($normalizedName = str_replace('-', '_', $name), $parameters)) {
+ $parameters[$normalizedName] = SimpleXMLElement::phpize($value);
+ }
+ // keep not normalized key for BC too
$parameters[$name] = SimpleXMLElement::phpize($value);
}
diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
index f2d0760192..e145c7a10c 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerTest.php
@@ -188,6 +188,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($sc->__bar, $sc->get('bar'), '->get() returns the service for the given id');
$this->assertEquals($sc->__foo_bar, $sc->get('foo_bar'), '->get() returns the service if a get*Method() is defined');
$this->assertEquals($sc->__foo_baz, $sc->get('foo.baz'), '->get() returns the service if a get*Method() is defined');
+ $this->assertEquals($sc->__foo_baz, $sc->get('foo\\baz'), '->get() returns the service if a get*Method() is defined');
$sc->set('bar', $bar = new \stdClass());
$this->assertEquals($bar, $sc->get('bar'), '->get() prefers to return a service defined with set() than one defined with a getXXXMethod()');
@@ -259,6 +260,7 @@ class ContainerTest extends \PHPUnit_Framework_TestCase
$this->assertTrue($sc->has('bar'), '->has() returns true if a get*Method() is defined');
$this->assertTrue($sc->has('foo_bar'), '->has() returns true if a get*Method() is defined');
$this->assertTrue($sc->has('foo.baz'), '->has() returns true if a get*Method() is defined');
+ $this->assertTrue($sc->has('foo\\baz'), '->has() returns true if a get*Method() is defined');
}
/**
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml
new file mode 100644
index 0000000000..a4da1bf74c
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/xml/services10.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
index 6d36e2404c..74db3c8eff 100644
--- a/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
+++ b/src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php
@@ -231,6 +231,29 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($aliases['another_alias_for_foo']->isPublic());
}
+ public function testParsesTags()
+ {
+ $container = new ContainerBuilder();
+ $loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'));
+ $loader->load('services10.xml');
+
+ $services = $container->findTaggedServiceIds('foo_tag');
+ $this->assertCount(1, $services);
+
+ foreach ($services as $id => $tagAttributes) {
+ foreach ($tagAttributes as $attributes) {
+ $this->assertArrayHasKey('other_option', $attributes);
+ $this->assertEquals('lorem', $attributes['other_option']);
+ $this->assertArrayHasKey('other-option', $attributes, 'unnormalized tag attributes should not be removed');
+
+ $this->assertEquals('ciz', $attributes['some_option'], 'no overriding should be done when normalizing');
+ $this->assertEquals('cat', $attributes['some-option']);
+
+ $this->assertArrayNotHasKey('an_other_option', $attributes, 'normalization should not be done when an underscore is already found');
+ }
+ }
+ }
+
public function testConvertDomElementToArray()
{
$doc = new \DOMDocument("1.0");
diff --git a/src/Symfony/Component/DependencyInjection/services10.xml b/src/Symfony/Component/DependencyInjection/services10.xml
new file mode 100644
index 0000000000..824d8b5d75
--- /dev/null
+++ b/src/Symfony/Component/DependencyInjection/services10.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php
index 3b1c9bde65..6371869b44 100644
--- a/src/Symfony/Component/Filesystem/Filesystem.php
+++ b/src/Symfony/Component/Filesystem/Filesystem.php
@@ -52,7 +52,7 @@ class Filesystem
if ($doCopy) {
// https://bugs.php.net/bug.php?id=64634
$source = fopen($originFile, 'r');
- $target = fopen($targetFile, 'w+');
+ $target = fopen($targetFile, 'w');
stream_copy_to_stream($source, $target);
fclose($source);
fclose($target);
diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php
index e4e8932ee9..9c1b92d741 100644
--- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php
+++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/BaseDateTimeTransformer.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface;
+use Symfony\Component\Form\Exception\InvalidArgumentException;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
abstract class BaseDateTimeTransformer implements DataTransformerInterface
@@ -35,6 +36,7 @@ abstract class BaseDateTimeTransformer implements DataTransformerInterface
* @param string $outputTimezone The name of the output timezone
*
* @throws UnexpectedTypeException if a timezone is not a string
+ * @throws InvalidArgumentException if a timezone is not valid
*/
public function __construct($inputTimezone = null, $outputTimezone = null)
{
@@ -48,5 +50,18 @@ abstract class BaseDateTimeTransformer implements DataTransformerInterface
$this->inputTimezone = $inputTimezone ?: date_default_timezone_get();
$this->outputTimezone = $outputTimezone ?: date_default_timezone_get();
+
+ // Check if input and output timezones are valid
+ try {
+ new \DateTimeZone($this->inputTimezone);
+ } catch (\Exception $e) {
+ throw new InvalidArgumentException(sprintf('Input timezone is invalid: %s.', $this->inputTimezone), $e->getCode(), $e);
+ }
+
+ try {
+ new \DateTimeZone($this->outputTimezone);
+ } catch (\Exception $e) {
+ throw new InvalidArgumentException(sprintf('Output timezone is invalid: %s.', $this->outputTimezone), $e->getCode(), $e);
+ }
}
}
diff --git a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php
index fe9ad7d145..5cd02daa91 100644
--- a/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php
+++ b/src/Symfony/Component/Form/Extension/Validator/ValidatorTypeGuesser.php
@@ -145,14 +145,10 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
case 'Symfony\Component\Validator\Constraints\Ip':
return new TypeGuess('text', array(), Guess::MEDIUM_CONFIDENCE);
- case 'Symfony\Component\Validator\Constraints\MaxLength':
- case 'Symfony\Component\Validator\Constraints\MinLength':
case 'Symfony\Component\Validator\Constraints\Length':
case 'Symfony\Component\Validator\Constraints\Regex':
return new TypeGuess('text', array(), Guess::LOW_CONFIDENCE);
- case 'Symfony\Component\Validator\Constraints\Min':
- case 'Symfony\Component\Validator\Constraints\Max':
case 'Symfony\Component\Validator\Constraints\Range':
return new TypeGuess('number', array(), Guess::LOW_CONFIDENCE);
@@ -196,9 +192,6 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
public function guessMaxLengthForConstraint(Constraint $constraint)
{
switch (get_class($constraint)) {
- case 'Symfony\Component\Validator\Constraints\MaxLength':
- return new ValueGuess($constraint->limit, Guess::HIGH_CONFIDENCE);
-
case 'Symfony\Component\Validator\Constraints\Length':
if (is_numeric($constraint->max)) {
return new ValueGuess($constraint->max, Guess::HIGH_CONFIDENCE);
@@ -211,9 +204,6 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
}
break;
- case 'Symfony\Component\Validator\Constraints\Max':
- return new ValueGuess(strlen((string) $constraint->limit), Guess::LOW_CONFIDENCE);
-
case 'Symfony\Component\Validator\Constraints\Range':
if (is_numeric($constraint->max)) {
return new ValueGuess(strlen((string) $constraint->max), Guess::LOW_CONFIDENCE);
@@ -234,9 +224,6 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
public function guessPatternForConstraint(Constraint $constraint)
{
switch (get_class($constraint)) {
- case 'Symfony\Component\Validator\Constraints\MinLength':
- return new ValueGuess(sprintf('.{%s,}', (string) $constraint->limit), Guess::LOW_CONFIDENCE);
-
case 'Symfony\Component\Validator\Constraints\Length':
if (is_numeric($constraint->min)) {
return new ValueGuess(sprintf('.{%s,}', (string) $constraint->min), Guess::LOW_CONFIDENCE);
@@ -251,9 +238,6 @@ class ValidatorTypeGuesser implements FormTypeGuesserInterface
}
break;
- case 'Symfony\Component\Validator\Constraints\Min':
- return new ValueGuess(sprintf('.{%s,}', strlen((string) $constraint->limit)), Guess::LOW_CONFIDENCE);
-
case 'Symfony\Component\Validator\Constraints\Range':
if (is_numeric($constraint->min)) {
return new ValueGuess(sprintf('.{%s,}', strlen((string) $constraint->min)), Guess::LOW_CONFIDENCE);
diff --git a/src/Symfony/Component/Form/Resources/translations/validators.ua.xlf b/src/Symfony/Component/Form/Resources/translations/validators.uk.xlf
similarity index 100%
rename from src/Symfony/Component/Form/Resources/translations/validators.ua.xlf
rename to src/Symfony/Component/Form/Resources/translations/validators.uk.xlf
diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BaseDateTimeTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BaseDateTimeTransformerTest.php
new file mode 100644
index 0000000000..8f2d16bb1b
--- /dev/null
+++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/BaseDateTimeTransformerTest.php
@@ -0,0 +1,41 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer;
+
+class BaseDateTimeTransformerTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException
+ * @expectedExceptionMessage this_timezone_does_not_exist
+ */
+ public function testConstructFailsIfInputTimezoneIsInvalid()
+ {
+ $this->getMock(
+ 'Symfony\Component\Form\Extension\Core\DataTransformer\BaseDateTimeTransformer',
+ array(),
+ array('this_timezone_does_not_exist')
+ );
+ }
+
+ /**
+ * @expectedException \Symfony\Component\Form\Exception\InvalidArgumentException
+ * @expectedExceptionMessage that_timezone_does_not_exist
+ */
+ public function testConstructFailsIfOutputTimezoneIsInvalid()
+ {
+ $this->getMock(
+ 'Symfony\Component\Form\Extension\Core\DataTransformer\BaseDateTimeTransformer',
+ array(),
+ array(null, 'that_timezone_does_not_exist')
+ );
+ }
+}
diff --git a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
index 1060393ccd..4d27435807 100644
--- a/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
+++ b/src/Symfony/Component/HttpFoundation/BinaryFileResponse.php
@@ -216,15 +216,18 @@ class BinaryFileResponse extends Response
$start = (int) $start;
}
- $start = max($start, 0);
- $end = min($end, $fileSize - 1);
+ if ($start <= $end) {
+ if ($start < 0 || $end > $fileSize - 1) {
+ $this->setStatusCode(416);
+ } elseif ($start !== 0 || $end !== $fileSize - 1) {
+ $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
+ $this->offset = $start;
- $this->maxlen = $end < $fileSize ? $end - $start + 1 : -1;
- $this->offset = $start;
-
- $this->setStatusCode(206);
- $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
- $this->headers->set('Content-Length', $end - $start + 1);
+ $this->setStatusCode(206);
+ $this->headers->set('Content-Range', sprintf('bytes %s-%s/%s', $start, $end, $fileSize));
+ $this->headers->set('Content-Length', $end - $start + 1);
+ }
+ }
}
}
diff --git a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
index c3d324fa9f..75863168d1 100644
--- a/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
+++ b/src/Symfony/Component/HttpFoundation/Tests/BinaryFileResponseTest.php
@@ -84,12 +84,73 @@ class BinaryFileResponseTest extends ResponseTestCase
return array(
array('bytes=1-4', 1, 4, 'bytes 1-4/35'),
array('bytes=-5', 30, 5, 'bytes 30-34/35'),
- array('bytes=-35', 0, 35, 'bytes 0-34/35'),
- array('bytes=-40', 0, 35, 'bytes 0-34/35'),
array('bytes=30-', 30, 5, 'bytes 30-34/35'),
array('bytes=30-30', 30, 1, 'bytes 30-30/35'),
array('bytes=30-34', 30, 5, 'bytes 30-34/35'),
- array('bytes=30-40', 30, 5, 'bytes 30-34/35')
+ );
+ }
+
+ /**
+ * @dataProvider provideFullFileRanges
+ */
+ public function testFullFileRequests($requestRange)
+ {
+ $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif')->setAutoEtag();
+
+ // prepare a request for a range of the testing file
+ $request = Request::create('/');
+ $request->headers->set('Range', $requestRange);
+
+ $file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
+ $data = fread($file, 35);
+ fclose($file);
+
+ $this->expectOutputString($data);
+ $response = clone $response;
+ $response->prepare($request);
+ $response->sendContent();
+
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertEquals('binary', $response->headers->get('Content-Transfer-Encoding'));
+ }
+
+ public function provideFullFileRanges()
+ {
+ return array(
+ array('bytes=0-'),
+ array('bytes=0-34'),
+ array('bytes=-35'),
+ // Syntactical invalid range-request should also return the full resource
+ array('bytes=20-10'),
+ array('bytes=50-40'),
+ );
+ }
+
+ /**
+ * @dataProvider provideInvalidRanges
+ */
+ public function testInvalidRequests($requestRange)
+ {
+ $response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif')->setAutoEtag();
+
+ // prepare a request for a range of the testing file
+ $request = Request::create('/');
+ $request->headers->set('Range', $requestRange);
+
+ $response = clone $response;
+ $response->prepare($request);
+ $response->sendContent();
+
+ $this->assertEquals(416, $response->getStatusCode());
+ $this->assertEquals('binary', $response->headers->get('Content-Transfer-Encoding'));
+ #$this->assertEquals('', $response->headers->get('Content-Range'));
+ }
+
+ public function provideInvalidRanges()
+ {
+ return array(
+ array('bytes=-40'),
+ array('bytes=30-40')
);
}
diff --git a/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php
index fe4ad29851..4c28abef61 100644
--- a/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php
+++ b/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php
@@ -36,6 +36,7 @@ class TraceableEventDispatcher implements EventDispatcherInterface, TraceableEve
private $wrappedListeners = array();
private $firstCalledEvent = array();
private $id;
+ private $lastEventId = 0;
/**
* Constructor.
@@ -123,7 +124,7 @@ class TraceableEventDispatcher implements EventDispatcherInterface, TraceableEve
$event = new Event();
}
- $this->id = spl_object_hash($event);
+ $this->id = $eventId = ++$this->lastEventId;
$this->preDispatch($eventName, $event);
@@ -138,7 +139,7 @@ class TraceableEventDispatcher implements EventDispatcherInterface, TraceableEve
$this->dispatcher->dispatch($eventName, $event);
// reset the id as another event might have been dispatched during the dispatching of this event
- $this->id = spl_object_hash($event);
+ $this->id = $eventId;
unset($this->firstCalledEvent[$eventName]);
diff --git a/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php b/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php
index 7649610f53..d30837d2c6 100644
--- a/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php
@@ -148,6 +148,22 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
$dispatcher->dispatch('foo');
}
+ public function testDispatchReusedEventNested()
+ {
+ $nestedCall = false;
+ $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch());
+ $dispatcher->addListener('foo', function (Event $e) use ($dispatcher) {
+ $dispatcher->dispatch('bar', $e);
+ });
+ $dispatcher->addListener('bar', function (Event $e) use (&$nestedCall) {
+ $nestedCall = true;
+ });
+
+ $this->assertFalse($nestedCall);
+ $dispatcher->dispatch('foo');
+ $this->assertTrue($nestedCall);
+ }
+
public function testStopwatchSections()
{
$dispatcher = new TraceableEventDispatcher(new EventDispatcher(), $stopwatch = new Stopwatch());
diff --git a/src/Symfony/Component/Intl/Util/SvnRepository.php b/src/Symfony/Component/Intl/Util/SvnRepository.php
index fb44e3c6c6..3fb3acb4fd 100644
--- a/src/Symfony/Component/Intl/Util/SvnRepository.php
+++ b/src/Symfony/Component/Intl/Util/SvnRepository.php
@@ -137,5 +137,4 @@ class SvnRepository
return $this->svnInfo;
}
-
}
diff --git a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php
index 42cd910893..4d19d2a2ec 100644
--- a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php
+++ b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php
@@ -92,6 +92,7 @@ EOF;
$properties[] = $route->getRequirements();
$properties[] = $compiledRoute->getTokens();
$properties[] = $compiledRoute->getHostTokens();
+ $properties[] = $route->getSchemes();
$routes .= sprintf(" '%s' => %s,\n", $name, str_replace("\n", '', var_export($properties, true)));
}
@@ -114,9 +115,9 @@ EOF;
throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', \$name));
}
- list(\$variables, \$defaults, \$requirements, \$tokens, \$hostTokens) = self::\$declaredRoutes[\$name];
+ list(\$variables, \$defaults, \$requirements, \$tokens, \$hostTokens, \$requiredSchemes) = self::\$declaredRoutes[\$name];
- return \$this->doGenerate(\$variables, \$defaults, \$requirements, \$tokens, \$parameters, \$name, \$referenceType, \$hostTokens);
+ return \$this->doGenerate(\$variables, \$defaults, \$requirements, \$tokens, \$parameters, \$name, \$referenceType, \$hostTokens, \$requiredSchemes);
}
EOF;
}
diff --git a/src/Symfony/Component/Routing/Generator/UrlGenerator.php b/src/Symfony/Component/Routing/Generator/UrlGenerator.php
index f224cb3f6d..59ce9e25a5 100644
--- a/src/Symfony/Component/Routing/Generator/UrlGenerator.php
+++ b/src/Symfony/Component/Routing/Generator/UrlGenerator.php
@@ -137,7 +137,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
// the Route has a cache of its own and is not recompiled as long as it does not get modified
$compiledRoute = $route->compile();
- return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens());
+ return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens(), $route->getSchemes());
}
/**
@@ -145,7 +145,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
* @throws InvalidParameterException When a parameter value for a placeholder is not correct because
* it does not match the requirement
*/
- protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens)
+ protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, array $requiredSchemes = array())
{
$variables = array_flip($variables);
$mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);
@@ -204,7 +204,24 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
$schemeAuthority = '';
if ($host = $this->context->getHost()) {
$scheme = $this->context->getScheme();
- if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme !== $req) {
+
+ if ($requiredSchemes) {
+ $schemeMatched = false;
+ foreach ($requiredSchemes as $requiredScheme) {
+ if ($scheme === $requiredScheme) {
+ $schemeMatched = true;
+
+ break;
+ }
+ }
+
+ if (!$schemeMatched) {
+ $referenceType = self::ABSOLUTE_URL;
+ $scheme = current($requiredSchemes);
+ }
+
+ } elseif (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme !== $req) {
+ // We do this for BC; to be removed if _scheme is not supported anymore
$referenceType = self::ABSOLUTE_URL;
$scheme = $req;
}
diff --git a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
index a412fa7806..2741502ad2 100644
--- a/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
+++ b/src/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php
@@ -288,14 +288,15 @@ EOF;
EOF;
}
- if ($scheme = $route->getRequirement('_scheme')) {
+ if ($schemes = $route->getSchemes()) {
if (!$supportsRedirections) {
- throw new \LogicException('The "_scheme" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
+ throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
}
-
+ $schemes = str_replace("\n", '', var_export(array_flip($schemes), true));
$code .= <<context->getScheme() !== '$scheme') {
- return \$this->redirect(\$pathinfo, '$name', '$scheme');
+ \$requiredSchemes = $schemes;
+ if (!isset(\$requiredSchemes[\$this->context->getScheme()])) {
+ return \$this->redirect(\$pathinfo, '$name', key(\$requiredSchemes));
}
@@ -313,8 +314,11 @@ EOF;
}
$vars[] = "array('_route' => '$name')";
- $code .= sprintf(" return \$this->mergeDefaults(array_replace(%s), %s);\n"
- , implode(', ', $vars), str_replace("\n", '', var_export($route->getDefaults(), true)));
+ $code .= sprintf(
+ " return \$this->mergeDefaults(array_replace(%s), %s);\n",
+ implode(', ', $vars),
+ str_replace("\n", '', var_export($route->getDefaults(), true))
+ );
} elseif ($route->getDefaults()) {
$code .= sprintf(" return %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true)));
diff --git a/src/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php b/src/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php
index 56fcb604cc..3d13181f46 100644
--- a/src/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php
+++ b/src/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php
@@ -56,9 +56,10 @@ abstract class RedirectableUrlMatcher extends UrlMatcher implements Redirectable
}
// check HTTP scheme requirement
- $scheme = $route->getRequirement('_scheme');
- if ($scheme && $this->context->getScheme() !== $scheme) {
- return array(self::ROUTE_MATCH, $this->redirect($pathinfo, $name, $scheme));
+ $scheme = $this->context->getScheme();
+ $schemes = $route->getSchemes();
+ if ($schemes && !$route->hasScheme($scheme)) {
+ return array(self::ROUTE_MATCH, $this->redirect($pathinfo, $name, current($schemes)));
}
return array(self::REQUIREMENT_MATCH, null);
diff --git a/src/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php b/src/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php
index e70063b7b9..b52b61908a 100644
--- a/src/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php
+++ b/src/Symfony/Component/Routing/Matcher/TraceableUrlMatcher.php
@@ -75,7 +75,7 @@ class TraceableUrlMatcher extends UrlMatcher
if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) {
$this->addTrace(sprintf('Host "%s" does not match the requirement ("%s")', $this->context->getHost(), $route->getHost()), self::ROUTE_ALMOST_MATCHES, $name, $route);
- return true;
+ continue;
}
// check HTTP method requirement
@@ -104,9 +104,11 @@ class TraceableUrlMatcher extends UrlMatcher
}
// check HTTP scheme requirement
- if ($scheme = $route->getRequirement('_scheme')) {
- if ($this->context->getScheme() !== $scheme) {
- $this->addTrace(sprintf('Scheme "%s" does not match the requirement ("%s"); the user will be redirected', $this->context->getScheme(), $scheme), self::ROUTE_ALMOST_MATCHES, $name, $route);
+ if ($requiredSchemes = $route->getSchemes()) {
+ $scheme = $this->context->getScheme();
+
+ if (!$route->hasScheme($scheme)) {
+ $this->addTrace(sprintf('Scheme "%s" does not match any of the required schemes ("%s"); the user will be redirected to first required scheme', $scheme, implode(', ', $requiredSchemes)), self::ROUTE_ALMOST_MATCHES, $name, $route);
return true;
}
diff --git a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php
index 559961597d..8d081a8629 100644
--- a/src/Symfony/Component/Routing/Matcher/UrlMatcher.php
+++ b/src/Symfony/Component/Routing/Matcher/UrlMatcher.php
@@ -205,12 +205,10 @@ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface
}
// check HTTP scheme requirement
- $scheme = $route->getRequirement('_scheme');
- if ($scheme && $scheme !== $this->context->getScheme()) {
- return array(self::REQUIREMENT_MISMATCH, null);
- }
+ $scheme = $this->context->getScheme();
+ $status = $route->getSchemes() && !$route->hasScheme($scheme) ? self::REQUIREMENT_MISMATCH : self::REQUIREMENT_MATCH;
- return array(self::REQUIREMENT_MATCH, null);
+ return array($status, null);
}
/**
diff --git a/src/Symfony/Component/Routing/Route.php b/src/Symfony/Component/Routing/Route.php
index aac654b01e..0800554146 100644
--- a/src/Symfony/Component/Routing/Route.php
+++ b/src/Symfony/Component/Routing/Route.php
@@ -247,6 +247,25 @@ class Route implements \Serializable
return $this;
}
+ /**
+ * Checks if a scheme requirement has been set.
+ *
+ * @param string $scheme
+ *
+ * @return Boolean true if the scheme requirement exists, otherwise false
+ */
+ public function hasScheme($scheme)
+ {
+ $scheme = strtolower($scheme);
+ foreach ($this->schemes as $requiredScheme) {
+ if ($scheme === $requiredScheme) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Returns the uppercased HTTP methods this route is restricted to.
* So an empty array means that any method is allowed.
diff --git a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php
index d7b99d67b9..acf1163c5b 100644
--- a/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php
+++ b/src/Symfony/Component/Routing/Tests/Fixtures/dumper/url_matcher2.php
@@ -321,8 +321,9 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
// secure
if ($pathinfo === '/secure') {
- if ($this->context->getScheme() !== 'https') {
- return $this->redirect($pathinfo, 'secure', 'https');
+ $requiredSchemes = array ( 'https' => 0,);
+ if (!isset($requiredSchemes[$this->context->getScheme()])) {
+ return $this->redirect($pathinfo, 'secure', key($requiredSchemes));
}
return array('_route' => 'secure');
@@ -330,8 +331,9 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
// nonsecure
if ($pathinfo === '/nonsecure') {
- if ($this->context->getScheme() !== 'http') {
- return $this->redirect($pathinfo, 'nonsecure', 'http');
+ $requiredSchemes = array ( 'http' => 0,);
+ if (!isset($requiredSchemes[$this->context->getScheme()])) {
+ return $this->redirect($pathinfo, 'nonsecure', key($requiredSchemes));
}
return array('_route' => 'nonsecure');
diff --git a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php
index ab5f4cdae0..78e3907fd5 100644
--- a/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php
+++ b/src/Symfony/Component/Routing/Tests/Generator/Dumper/PhpGeneratorDumperTest.php
@@ -114,4 +114,37 @@ class PhpGeneratorDumperTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($url, '/testing');
}
+
+ public function testDumpWithSchemeRequirement()
+ {
+ $this->routeCollection->add('Test1', new Route('/testing', array(), array(), array(), '', array('ftp', 'https')));
+ $this->routeCollection->add('Test2', new Route('/testing_bc', array(), array('_scheme' => 'https'))); // BC
+
+ file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(array('class' => 'SchemeUrlGenerator')));
+ include ($this->testTmpFilepath);
+
+ $projectUrlGenerator = new \SchemeUrlGenerator(new RequestContext('/app.php'));
+
+ $absoluteUrl = $projectUrlGenerator->generate('Test1', array(), true);
+ $absoluteUrlBC = $projectUrlGenerator->generate('Test2', array(), true);
+ $relativeUrl = $projectUrlGenerator->generate('Test1', array(), false);
+ $relativeUrlBC = $projectUrlGenerator->generate('Test2', array(), false);
+
+ $this->assertEquals($absoluteUrl, 'ftp://localhost/app.php/testing');
+ $this->assertEquals($absoluteUrlBC, 'https://localhost/app.php/testing_bc');
+ $this->assertEquals($relativeUrl, 'ftp://localhost/app.php/testing');
+ $this->assertEquals($relativeUrlBC, 'https://localhost/app.php/testing_bc');
+
+ $projectUrlGenerator = new \SchemeUrlGenerator(new RequestContext('/app.php', 'GET', 'localhost', 'https'));
+
+ $absoluteUrl = $projectUrlGenerator->generate('Test1', array(), true);
+ $absoluteUrlBC = $projectUrlGenerator->generate('Test2', array(), true);
+ $relativeUrl = $projectUrlGenerator->generate('Test1', array(), false);
+ $relativeUrlBC = $projectUrlGenerator->generate('Test2', array(), false);
+
+ $this->assertEquals($absoluteUrl, 'https://localhost/app.php/testing');
+ $this->assertEquals($absoluteUrlBC, 'https://localhost/app.php/testing_bc');
+ $this->assertEquals($relativeUrl, '/app.php/testing');
+ $this->assertEquals($relativeUrlBC, '/app.php/testing_bc');
+ }
}
diff --git a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
index 2ab21b2cdb..143e3448a2 100644
--- a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
+++ b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
@@ -244,20 +244,38 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase
public function testSchemeRequirementDoesNothingIfSameCurrentScheme()
{
- $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'http')));
+ $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'http'))); // BC
$this->assertEquals('/app.php/', $this->getGenerator($routes)->generate('test'));
- $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'https')));
+ $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'https'))); // BC
+ $this->assertEquals('/app.php/', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test'));
+
+ $routes = $this->getRoutes('test', new Route('/', array(), array(), array(), '', array('http')));
+ $this->assertEquals('/app.php/', $this->getGenerator($routes)->generate('test'));
+
+ $routes = $this->getRoutes('test', new Route('/', array(), array(), array(), '', array('https')));
$this->assertEquals('/app.php/', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test'));
}
public function testSchemeRequirementForcesAbsoluteUrl()
{
- $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'https')));
+ $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'https'))); // BC
$this->assertEquals('https://localhost/app.php/', $this->getGenerator($routes)->generate('test'));
- $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'http')));
+ $routes = $this->getRoutes('test', new Route('/', array(), array('_scheme' => 'http'))); // BC
$this->assertEquals('http://localhost/app.php/', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test'));
+
+ $routes = $this->getRoutes('test', new Route('/', array(), array(), array(), '', array('https')));
+ $this->assertEquals('https://localhost/app.php/', $this->getGenerator($routes)->generate('test'));
+
+ $routes = $this->getRoutes('test', new Route('/', array(), array(), array(), '', array('http')));
+ $this->assertEquals('http://localhost/app.php/', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test'));
+ }
+
+ public function testSchemeRequirementCreatesUrlForFirstRequiredScheme()
+ {
+ $routes = $this->getRoutes('test', new Route('/', array(), array(), array(), '', array('Ftp', 'https')));
+ $this->assertEquals('ftp://localhost/app.php/', $this->getGenerator($routes)->generate('test'));
}
public function testPathWithTwoStartingSlashes()
@@ -443,7 +461,7 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase
$this->assertNull($generator->generate('test', array('foo' => 'baz'), false));
}
- public function testGenerateNetworkPath()
+ public function testGenerateNetworkPathBC()
{
$routes = $this->getRoutes('test', new Route('/{name}', array(), array('_scheme' => 'http'), array(), '{locale}.example.com'));
@@ -461,13 +479,32 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase
);
}
+ public function testGenerateNetworkPath()
+ {
+ $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com', array('http')));
+
+ $this->assertSame('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test',
+ array('name' =>'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::NETWORK_PATH), 'network path with different host'
+ );
+ $this->assertSame('//fr.example.com/app.php/Fabien?query=string', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test',
+ array('name' =>'Fabien', 'locale' => 'fr', 'query' => 'string'), UrlGeneratorInterface::NETWORK_PATH), 'network path although host same as context'
+ );
+ $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, array('scheme' => 'https'))->generate('test',
+ array('name' =>'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::NETWORK_PATH), 'absolute URL because scheme requirement does not match context'
+ );
+ $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test',
+ array('name' =>'Fabien', 'locale' => 'fr'), UrlGeneratorInterface::ABSOLUTE_URL), 'absolute URL with same scheme because it is requested'
+ );
+ }
+
public function testGenerateRelativePath()
{
$routes = new RouteCollection();
$routes->add('article', new Route('/{author}/{article}/'));
$routes->add('comments', new Route('/{author}/{article}/comments'));
$routes->add('host', new Route('/{article}', array(), array(), array(), '{author}.example.com'));
- $routes->add('scheme', new Route('/{author}', array(), array('_scheme' => 'https')));
+ $routes->add('schemeBC', new Route('/{author}', array(), array('_scheme' => 'https'))); // BC
+ $routes->add('scheme', new Route('/{author}/blog', array(), array(), array(), '', array('https')));
$routes->add('unrelated', new Route('/about'));
$generator = $this->getGenerator($routes, array('host' => 'example.com', 'pathInfo' => '/fabien/symfony-is-great/'));
@@ -487,9 +524,12 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase
$this->assertSame('//bernhard.example.com/app.php/forms-are-great', $generator->generate('host',
array('author' =>'bernhard', 'article' => 'forms-are-great'), UrlGeneratorInterface::RELATIVE_PATH)
);
- $this->assertSame('https://example.com/app.php/bernhard', $generator->generate('scheme',
+ $this->assertSame('https://example.com/app.php/bernhard', $generator->generate('schemeBC',
array('author' =>'bernhard'), UrlGeneratorInterface::RELATIVE_PATH)
);
+ $this->assertSame('https://example.com/app.php/bernhard/blog', $generator->generate('scheme',
+ array('author' =>'bernhard'), UrlGeneratorInterface::RELATIVE_PATH)
+ );
$this->assertSame('../../about', $generator->generate('unrelated',
array(), UrlGeneratorInterface::RELATIVE_PATH)
);
diff --git a/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php
index 2ad4fc8725..5cbb605479 100644
--- a/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php
+++ b/src/Symfony/Component/Routing/Tests/Matcher/RedirectableUrlMatcherTest.php
@@ -41,7 +41,7 @@ class RedirectableUrlMatcherTest extends \PHPUnit_Framework_TestCase
$matcher->match('/foo');
}
- public function testSchemeRedirect()
+ public function testSchemeRedirectBC()
{
$coll = new RouteCollection();
$coll->add('foo', new Route('/foo', array(), array('_scheme' => 'https')));
@@ -55,4 +55,32 @@ class RedirectableUrlMatcherTest extends \PHPUnit_Framework_TestCase
;
$matcher->match('/foo');
}
+
+ public function testSchemeRedirectRedirectsToFirstScheme()
+ {
+ $coll = new RouteCollection();
+ $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('FTP', 'HTTPS')));
+
+ $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext()));
+ $matcher
+ ->expects($this->once())
+ ->method('redirect')
+ ->with('/foo', 'foo', 'ftp')
+ ->will($this->returnValue(array('_route' => 'foo')))
+ ;
+ $matcher->match('/foo');
+ }
+
+ public function testNoSchemaRedirectIfOnOfMultipleSchemesMatches()
+ {
+ $coll = new RouteCollection();
+ $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https', 'http')));
+
+ $matcher = $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', array($coll, new RequestContext()));
+ $matcher
+ ->expects($this->never())
+ ->method('redirect')
+ ;
+ $matcher->match('/foo');
+ }
}
diff --git a/src/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php
index de6395226f..969ab0a2f0 100644
--- a/src/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php
+++ b/src/Symfony/Component/Routing/Tests/Matcher/TraceableUrlMatcherTest.php
@@ -58,6 +58,37 @@ class TraceableUrlMatcherTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(0, 0, 0, 0, 0, 1), $this->getLevels($traces));
}
+ public function testMatchRouteOnMultipleHosts()
+ {
+ $routes = new RouteCollection();
+ $routes->add('first', new Route(
+ '/mypath/',
+ array('_controller' => 'MainBundle:Info:first'),
+ array(),
+ array(),
+ 'some.example.com'
+ ));
+
+ $routes->add('second', new Route(
+ '/mypath/',
+ array('_controller' => 'MainBundle:Info:second'),
+ array(),
+ array(),
+ 'another.example.com'
+ ));
+
+ $context = new RequestContext();
+ $context->setHost('baz');
+
+ $matcher = new TraceableUrlMatcher($routes, $context);
+
+ $traces = $matcher->getTraces('/mypath/');
+ $this->assertEquals(
+ array(TraceableUrlMatcher::ROUTE_ALMOST_MATCHES, TraceableUrlMatcher::ROUTE_ALMOST_MATCHES),
+ $this->getLevels($traces)
+ );
+ }
+
public function getLevels($traces)
{
$levels = array();
diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
index b31cada8c0..b03b0c3aa0 100644
--- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
+++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php
@@ -313,13 +313,23 @@ class UrlMatcherTest extends \PHPUnit_Framework_TestCase
/**
* @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
*/
- public function testSchemeRequirement()
+ public function testSchemeRequirementBC()
{
$coll = new RouteCollection();
$coll->add('foo', new Route('/foo', array(), array('_scheme' => 'https')));
$matcher = new UrlMatcher($coll, new RequestContext());
$matcher->match('/foo');
}
+ /**
+ * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
+ */
+ public function testSchemeRequirement()
+ {
+ $coll = new RouteCollection();
+ $coll->add('foo', new Route('/foo', array(), array(), array(), '', array('https')));
+ $matcher = new UrlMatcher($coll, new RequestContext());
+ $matcher->match('/foo');
+ }
/**
* @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
diff --git a/src/Symfony/Component/Routing/Tests/RouteTest.php b/src/Symfony/Component/Routing/Tests/RouteTest.php
index d63bcbacf6..b097e979f4 100644
--- a/src/Symfony/Component/Routing/Tests/RouteTest.php
+++ b/src/Symfony/Component/Routing/Tests/RouteTest.php
@@ -142,10 +142,15 @@ class RouteTest extends \PHPUnit_Framework_TestCase
{
$route = new Route('/');
$this->assertEquals(array(), $route->getSchemes(), 'schemes is initialized with array()');
+ $this->assertFalse($route->hasScheme('http'));
$route->setSchemes('hTTp');
$this->assertEquals(array('http'), $route->getSchemes(), '->setSchemes() accepts a single scheme string and lowercases it');
+ $this->assertTrue($route->hasScheme('htTp'));
+ $this->assertFalse($route->hasScheme('httpS'));
$route->setSchemes(array('HttpS', 'hTTp'));
$this->assertEquals(array('https', 'http'), $route->getSchemes(), '->setSchemes() accepts an array of schemes and lowercases them');
+ $this->assertTrue($route->hasScheme('htTp'));
+ $this->assertTrue($route->hasScheme('httpS'));
}
public function testSchemeIsBC()
@@ -154,6 +159,9 @@ class RouteTest extends \PHPUnit_Framework_TestCase
$route->setRequirement('_scheme', 'http|https');
$this->assertEquals('http|https', $route->getRequirement('_scheme'));
$this->assertEquals(array('http', 'https'), $route->getSchemes());
+ $this->assertTrue($route->hasScheme('https'));
+ $this->assertTrue($route->hasScheme('http'));
+ $this->assertFalse($route->hasScheme('ftp'));
$route->setSchemes(array('hTTp'));
$this->assertEquals('http', $route->getRequirement('_scheme'));
$route->setSchemes(array());
diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php
index e62f73c33c..59510eec1c 100644
--- a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php
+++ b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php
@@ -142,7 +142,14 @@ abstract class AbstractToken implements TokenInterface
*/
public function serialize()
{
- return serialize(array($this->user, $this->authenticated, $this->roles, $this->attributes));
+ return serialize(
+ array(
+ is_object($this->user) ? clone $this->user : $this->user,
+ $this->authenticated,
+ $this->roles,
+ $this->attributes
+ )
+ );
}
/**
diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php
index 928ee40c8b..098017e767 100644
--- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php
+++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php
@@ -11,7 +11,9 @@
namespace Symfony\Component\Security\Core\Tests\Authentication\Token;
+use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
use Symfony\Component\Security\Core\Role\Role;
+use Symfony\Component\Security\Core\Role\SwitchUserRole;
class TestUser
{
@@ -28,6 +30,31 @@ class TestUser
}
}
+class ConcreteToken extends AbstractToken
+{
+ private $credentials = 'credentials_value';
+
+ public function __construct($user, array $roles = array())
+ {
+ parent::__construct($roles);
+
+ $this->setUser($user);
+ }
+
+ public function serialize()
+ {
+ return serialize(array($this->credentials, parent::serialize()));
+ }
+
+ public function unserialize($serialized)
+ {
+ list($this->credentials, $parentStr) = unserialize($serialized);
+ parent::unserialize($parentStr);
+ }
+
+ public function getCredentials() {}
+}
+
class AbstractTokenTest extends \PHPUnit_Framework_TestCase
{
public function testGetUsername()
@@ -71,6 +98,20 @@ class AbstractTokenTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($token->getAttributes(), $uToken->getAttributes());
}
+ public function testSerializeParent()
+ {
+ $user = new TestUser('fabien');
+ $token = new ConcreteToken($user, array('ROLE_FOO'));
+
+ $parentToken = new ConcreteToken($user, array(new SwitchUserRole('ROLE_PREVIOUS', $token)));
+ $uToken = unserialize(serialize($parentToken));
+
+ $this->assertEquals(
+ current($parentToken->getRoles())->getSource()->getUser(),
+ current($uToken->getRoles())->getSource()->getUser()
+ );
+ }
+
/**
* @covers Symfony\Component\Security\Core\Authentication\Token\AbstractToken::__construct
*/
diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php
index 2db79f304b..05e260e74c 100644
--- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php
@@ -155,10 +155,11 @@ class ContextListener implements ListenerInterface
foreach ($this->userProviders as $provider) {
try {
- $token->setUser($provider->refreshUser($user));
+ $refreshedUser = $provider->refreshUser($user);
+ $token->setUser($refreshedUser);
if (null !== $this->logger) {
- $this->logger->debug(sprintf('Username "%s" was reloaded from user provider.', $user->getUsername()));
+ $this->logger->debug(sprintf('Username "%s" was reloaded from user provider.', $refreshedUser->getUsername()));
}
return $token;
@@ -166,7 +167,7 @@ class ContextListener implements ListenerInterface
// let's try the next user provider
} catch (UsernameNotFoundException $notFound) {
if (null !== $this->logger) {
- $this->logger->warning(sprintf('Username "%s" could not be found.', $user->getUsername()));
+ $this->logger->warning(sprintf('Username "%s" could not be found.', $notFound->getUsername()));
}
return null;
diff --git a/src/Symfony/Component/Validator/Exception/UnexpectedTypeException.php b/src/Symfony/Component/Validator/Exception/UnexpectedTypeException.php
index 573fecd2e8..49d8cc2082 100644
--- a/src/Symfony/Component/Validator/Exception/UnexpectedTypeException.php
+++ b/src/Symfony/Component/Validator/Exception/UnexpectedTypeException.php
@@ -15,6 +15,6 @@ class UnexpectedTypeException extends ValidatorException
{
public function __construct($value, $expectedType)
{
- parent::__construct(sprintf('Expected argument of type %s, %s given', $expectedType, gettype($value)));
+ parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value)));
}
}
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf
index 22e3e9f30c..6310279cae 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.cs.xlf
@@ -92,11 +92,11 @@
- Tato hodnota nesmí být null.
+ Tato hodnota nesmí být prázdná.
- Tato hodnota musí být null.
+ Tato hodnota musí být prázdná.
diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf
index 2b57a2b7ec..30642148e6 100644
--- a/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf
+++ b/src/Symfony/Component/Validator/Resources/translations/validators.es.xlf
@@ -242,6 +242,42 @@
Este valor no es un ISSN válido.
+
+
+ Este valor no es una divisa válida.
+
+
+
+ Este valor debería ser igual que {{ compared_value }}.
+
+
+
+ Este valor debería ser mayor que {{ compared_value }}.
+
+
+
+ Este valor debería ser mayor o igual que {{ compared_value }}.
+
+
+
+ Este valor debería ser idéntico a {{ compared_value_type }} {{ compared_value }}.
+
+
+
+ Este valor debería ser menor que {{ compared_value }}.
+
+
+
+ Este valor debería ser menor o igual que {{ compared_value }}.
+
+
+
+ Este valor debería ser distinto de {{ compared_value }}.
+
+
+
+ Este valor no debería ser idéntico a {{ compared_value_type }} {{ compared_value }}.
+
- Balio honek faltsua izan beharko luke (false).
+ Balio hau faltsua izan beharko litzateke.
- Balio honek egiazkoa izan beharko luke (true).
+ Balio hau egia izan beharko litzateke.
- Balio honek {{ type }} motakoa izan beharko luke.
+ Balio hau {{ type }} motakoa izan beharko litzateke.
- Balio honek hutsik egon beharko luke.
+ Balio hau hutsik egon beharko litzateke.
@@ -24,15 +24,15 @@
- Gutxienez {{ limit }} aukera hautatu behar dituzu.
+ Gutxienez aukera {{ limit }} hautatu behar duzu.|Gutxienez {{ limit }} aukera hautatu behar dituzu.
- Gehienez {{ limit }} aukera hautatu behar dituzu.
+ Gehienez aukera {{ limit }} hautatu behar duzu.|Gehienez {{ limit }} aukera hautatu behar dituzu.
- Emandako balio bat edo gehiago ez dira egokiak.
+ Emandako balioetatik gutxienez bat ez da egokia.
@@ -48,11 +48,11 @@
- Balio hau ez da data eta ordu egoki bat.
+ Balio hau ez da data-ordu egoki bat.
- Balio hau ez da helbide elektroniko egoki bat.
+ Balio hau ez da posta elektroniko egoki bat.
@@ -60,11 +60,11 @@
- Fitxategia ez dago irakurgai.
+ Fitxategia ez da irakurgarria.
- Fitxategia handiegia da ({{ size }} {{ suffix }}). Baimendutako tamainu handiena {{ limit }} {{ suffix }} da.
+ Fitxategia handiegia da ({{ size }} {{ suffix }}). Baimendutako tamaina handiena {{ limit }} {{ suffix }} da.
@@ -72,31 +72,31 @@
- Balio honek {{ limit }} edo gutxiago izan beharko luke.
+ Balio hau gehienez {{ limit }} izan beharko litzateke.
- Balio hau luzeegia da. {{ limit }} karaktere edo gutxiago eduki beharko lituzke.
+ Balio hau luzeegia da. Gehienez karaktere {{ limit }} eduki beharko luke.|Balio hau luzeegia da. Gehienez {{ limit }} karaktere eduki beharko lituzke.
- Balio honek {{ limit }} edo handiago izan beharko luke.
+ Balio hau gutxienez {{ limit }} izan beharko litzateke.
- Balio hau motzegia da. {{ limit }} karaktere edo gehiago eduki beharko lituzke.
+ Balio hau motzegia da. Karaktere {{ limit }} gutxienez eduki beharko luke.|Balio hau motzegia da. Gutxienez {{ limit }} karaktere eduki beharko lituzke.
- Balio honek ez luke hutsik egon behar.
+ Balio hau ez litzateke hutsik egon behar.
- Balio honek ez luke nulua izan behar.
+ Balio hau ez litzateke nulua izan behar.
- Balio honek nulua izan beharko luke.
+ Balio hau nulua izan beharko litzateke.
@@ -108,7 +108,7 @@
- Balio hau ez da URL egoki bat.
+ Balio hau ez da baliabideen kokatzaile uniforme (URL) egoki bat.
@@ -116,7 +116,7 @@
- Fitxategia handiegia da. Baimendutako tamainu handiena {{ limit }} {{ suffix }} da.
+ Fitxategia handiegia da. Baimendutako tamaina handiena {{ limit }} {{ suffix }} da.
@@ -128,7 +128,7 @@
- Balio honek zenbaki egoki bat izan beharko luke.
+ Balio hau zenbaki egoki bat izan beharko litzateke.
@@ -144,7 +144,7 @@
- Balio hau ez da lokalizazio egoki bat.
+ Balio hau ez da kokapen egoki bat.
@@ -160,7 +160,7 @@
- Irudiaren zabalera haundiegia da ({{ width }}px). Onartutako gehienezko zabalera {{ max_width }}px dira.
+ Irudiaren zabalera handiegia da ({{ width }}px). Onartutako gehienezko zabalera {{ max_width }}px dira.
@@ -168,7 +168,7 @@
- Irudiaren altuera haundiegia da ({{ height }}px). Onartutako gehienezko altuera {{ max_height }}px dira.
+ Irudiaren altuera handiegia da ({{ height }}px). Onartutako gehienezko altuera {{ max_height }}px dira.
@@ -176,15 +176,15 @@
- Balio honek uneko erabiltzailearen pasahitza izan beharko luke.
+ Balio hau uneko erabiltzailearen pasahitza izan beharko litzateke.
- Balio honek zehazki {{ limit }} karaktere izan beharko lituzke.
+ Balio honek zehazki karaktere {{ limit }} izan beharko luke.|Balio honek zehazki {{ limit }} karaktere izan beharko lituzke.
- Fitxategia partzialki bakarrik igo da.
+ Fitxategiaren zati bat bakarrik igo da.
@@ -204,27 +204,27 @@
- Bilduma honek {{ limit }} elementu edo gehiago eduki beharko lituzke.
+ Bilduma honek gutxienez elementu {{ limit }} eduki beharko luke.|Bilduma honek gutxienez {{ limit }} elementu eduki beharko lituzke.
- Bilduma honek {{ limit }} elementu edo gutxiago eduki beharko lituzke.
+ Bilduma honek gehienez elementu {{ limit }} eduki beharko luke.|Bilduma honek gehienez {{ limit }} elementu eduki beharko lituzke.
- Bilduma honek zehazki {{ limit }} elementu eduki beharko lituzke.
+ Bilduma honek zehazki elementu {{ limit }} eduki beharko luke.|Bilduma honek zehazki {{ limit }} elementu eduki beharko lituzke.
- Txartelaren zenbaki baliogabea.
+ Txartel zenbaki baliogabea.
- Onartzen ez den txartel mota edo txartelaren zenbaki baliogabea.
+ Txartel mota onartezina edo txartel zenbaki baliogabea.
- Hau ez da onartutako International Bank Account Number (IBAN) bat.
+ Hau ez da baliozko banku internazionaleko kontu zenbaki (IBAN) bat.
@@ -244,39 +244,39 @@
- Balio hau ez da baliozko moneta.
+ Balio hau ez da baliozko moneta bat.
- Balio honek {{ compared_value }}-(r)en berdina izan behar du.
+ Balio hau {{ compared_value }}-(r)en berbera izan beharko litzateke.
- Balio honek {{ compared_value }} baino handiagoa izan behar du.
+ Balio hau {{ compared_value }} baino handiagoa izan beharko litzateke.
- Balio honek {{ compared_value }}-(r)en berdina edo handiagoa izan beharko luke.
+ Balio hau {{ compared_value }}-(r)en berdina edota handiagoa izan beharko litzateke.
- Balio honek berbera izan behar du {{ compared_value_type }} {{ compared_value }}.
+ Balio hau {{ compared_value_type }} {{ compared_value }}-(r)en berbera izan beharko litzateke.
- Balio honek {{ compared_value }} baino txikiago izan behar du.
+ Balio hau {{ compared_value }} baino txikiagoa izan beharko litzateke.
- Balio honek {{ compared_value }}-(r)en berdina edo txikiago izan behar du.
+ Balio hau {{ compared_value }}-(r)en berdina edota txikiagoa izan beharko litzateke.
- Balio honek {{ compared_value }}-(r)en berdina izan behar du.
+ Balio hau ez litzateke {{ compared_value }}-(r)en berdina izan behar.
- Balio honek ez du berbera izan behar {{ compared_value_type }} {{ compared_value }}.
+ Balio hau ez litzateke {{ compared_value_type }} {{ compared_value }}-(r)en berbera izan behar.