diff --git a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php index 665869ba9a..ddfacebfa4 100644 --- a/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php +++ b/src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php @@ -122,7 +122,7 @@ class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface } $instance['event'] = array($instance['event']); - if (isset($instance['lazy']) && $instance['lazy']) { + if ($lazy = !empty($instance['lazy'])) { $this->container->getDefinition($id)->setPublic(true); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php index 9c6ab53a82..3ee9f086ae 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php +++ b/src/Symfony/Bundle/FrameworkBundle/Command/CachePoolClearCommand.php @@ -33,7 +33,7 @@ final class CachePoolClearCommand extends ContainerAwareCommand $this ->setName('cache:pool:clear') ->setDefinition(array( - new InputArgument('pools', InputArgument::IS_ARRAY, 'A list of cache pools or cache pool clearers'), + new InputArgument('pools', InputArgument::IS_ARRAY | InputArgument::REQUIRED, 'A list of cache pools or cache pool clearers'), )) ->setDescription('Clears cache pools') ->setHelp(<<<'EOF' diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 115f24c664..3fc032531d 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -570,7 +570,7 @@ class SecurityExtension extends Extension // Parses a tag and returns the id for the related user provider service private function createUserDaoProvider($name, $provider, ContainerBuilder $container) { - $name = $this->getUserProviderId(strtolower($name)); + $name = $this->getUserProviderId($name); // Doctrine Entity and In-memory DAO provider are managed by factories foreach ($this->userProviderFactories as $factory) { @@ -594,7 +594,7 @@ class SecurityExtension extends Extension if (isset($provider['chain'])) { $providers = array(); foreach ($provider['chain']['providers'] as $providerName) { - $providers[] = new Reference($this->getUserProviderId(strtolower($providerName))); + $providers[] = new Reference($this->getUserProviderId($providerName)); } $container @@ -609,7 +609,7 @@ class SecurityExtension extends Extension private function getUserProviderId($name) { - return 'security.user.provider.concrete.'.$name; + return 'security.user.provider.concrete.'.strtolower($name); } private function createExceptionListener($container, $config, $id, $defaultEntryPoint, $stateless) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig index f5ff17680a..31881953d8 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/translation.html.twig @@ -8,7 +8,7 @@ {{ include('@WebProfiler/Icon/translation.svg') }} {% set status_color = collector.countMissings ? 'red' : collector.countFallbacks ? 'yellow' %} {% set error_count = collector.countMissings + collector.countFallbacks %} - {{ error_count ?: collector.countdefines }} + {{ error_count ?: collector.countDefines }} {% endset %} {% set text %} @@ -28,7 +28,7 @@
Defined messages - {{ collector.countdefines }} + {{ collector.countDefines }}
{% endset %} @@ -65,7 +65,7 @@
- {{ collector.countdefines }} + {{ collector.countDefines }} Defined messages
@@ -96,7 +96,7 @@
-

Defined {{ messages_defined|length }}

+

Defined {{ collector.countDefines }}

@@ -114,7 +114,7 @@

-

Fallback {{ messages_fallback|length }}

+

Fallback {{ collector.countFallbacks }}

@@ -133,7 +133,7 @@

-

Missing {{ messages_missing|length }}

+

Missing {{ collector.countMissings }}

@@ -169,10 +169,10 @@ {% for message in messages %} {{ message.locale }} - {{ message.domain }} + {{ message.domain }} {{ message.count }} - {{ message.id }} + {{ message.id }} {% if message.transChoiceNumber is not null %} (pluralization is used) @@ -188,7 +188,7 @@

{% endif %} - {{ message.translation }} + {{ message.translation }} {% endfor %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index 3c4a6d441e..83f9ea3c8b 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -204,6 +204,9 @@ table tbody ul { .nowrap { white-space: pre; } +.prewrap { + white-space: pre-wrap; +} .newline { display: block; } diff --git a/src/Symfony/Component/Filesystem/Filesystem.php b/src/Symfony/Component/Filesystem/Filesystem.php index 1d8034fbbb..a550e8da14 100644 --- a/src/Symfony/Component/Filesystem/Filesystem.php +++ b/src/Symfony/Component/Filesystem/Filesystem.php @@ -446,6 +446,31 @@ class Filesystem $startPathArr = explode('/', trim($startPath, '/')); $endPathArr = explode('/', trim($endPath, '/')); + if ('/' !== $startPath[0]) { + array_shift($startPathArr); + } + + if ('/' !== $endPath[0]) { + array_shift($endPathArr); + } + + $normalizePathArray = function ($pathSegments) { + $result = array(); + + foreach ($pathSegments as $segment) { + if ('..' === $segment) { + array_pop($result); + } else { + $result[] = $segment; + } + } + + return $result; + }; + + $startPathArr = $normalizePathArray($startPathArr); + $endPathArr = $normalizePathArray($endPathArr); + // Find for which directory the common path stops $index = 0; while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) { diff --git a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php index e13d59f37e..1d4b9377b0 100644 --- a/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php +++ b/src/Symfony/Component/Filesystem/Tests/FilesystemTest.php @@ -1117,6 +1117,16 @@ class FilesystemTest extends FilesystemTestCase array('/a/aab/bb/', '/b/aab', '../../a/aab/bb/'), array('/aab/bb', '/aa', '../aab/bb/'), array('/aab', '/aa', '../aab/'), + array('/aa/bb/cc', '/aa/dd/..', 'bb/cc/'), + array('/aa/../bb/cc', '/aa/dd/..', '../bb/cc/'), + array('/aa/bb/../../cc', '/aa/../dd/..', 'cc/'), + array('/../aa/bb/cc', '/aa/dd/..', 'bb/cc/'), + array('/../../aa/../bb/cc', '/aa/dd/..', '../bb/cc/'), + array('C:/aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'), + array('c:/aa/../bb/cc', 'c:/aa/dd/..', '../bb/cc/'), + array('C:/aa/bb/../../cc', 'C:/aa/../dd/..', 'cc/'), + array('C:/../aa/bb/cc', 'C:/aa/dd/..', 'bb/cc/'), + array('C:/../../aa/../bb/cc', 'C:/aa/dd/..', '../bb/cc/'), ); if ('\\' === DIRECTORY_SEPARATOR) { diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index 73eed913ca..8b81e935cc 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -201,6 +201,28 @@ class Response $this->setContent($content); $this->setStatusCode($status); $this->setProtocolVersion('1.0'); + + // Deprecations + $class = get_class($this); + if ($this instanceof \PHPUnit_Framework_MockObject_MockObject || $this instanceof \Prophecy\Doubler\DoubleInterface) { + $class = get_parent_class($class); + } + if (isset(self::$deprecationsTriggered[$class])) { + return; + } + + self::$deprecationsTriggered[$class] = true; + foreach (self::$deprecatedMethods as $method) { + $r = new \ReflectionMethod($class, $method); + if (__CLASS__ !== $r->getDeclaringClass()->getName()) { + @trigger_error(sprintf('Extending %s::%s() in %s is deprecated since version 3.2 and won\'t be supported anymore in 4.0 as it will be final.', __CLASS__, $method, $class), E_USER_DEPRECATED); + } + } + + /* RFC2616 - 14.18 says all Responses need to have a Date */ + if (!$this->headers->has('Date')) { + $this->setDate(new \DateTime(null, new \DateTimeZone('UTC'))); + } } /** @@ -329,6 +351,7 @@ class Response return $this; } + /* RFC2616 - 14.18 says all Responses need to have a Date */ if (!$this->headers->has('Date')) { $this->setDate(\DateTime::createFromFormat('U', time())); } @@ -642,6 +665,11 @@ class Response */ public function getDate() { + /* + RFC2616 - 14.18 says all Responses need to have a Date. + Make sure we provide one even if it the header + has been removed in the meantime. + */ if (!$this->headers->has('Date')) { $this->setDate(\DateTime::createFromFormat('U', time())); } diff --git a/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php index c26cc1334d..348fd23018 100644 --- a/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php +++ b/src/Symfony/Component/HttpFoundation/Session/Storage/MockArraySessionStorage.php @@ -58,9 +58,9 @@ class MockArraySessionStorage implements SessionStorageInterface protected $metadataBag; /** - * @var array + * @var array|SessionBagInterface[] */ - protected $bags; + protected $bags = array(); /** * Constructor. diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index c08a4fe31a..d8beb5f406 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -276,8 +276,10 @@ class ResponseTest extends ResponseTestCase $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the date when the header has been modified'); $response = new Response('', 200); + $now = $this->createDateTimeNow(); $response->headers->remove('Date'); - $this->assertInstanceOf('\DateTime', $response->getDate()); + $date = $response->getDate(); + $this->assertEquals($now->getTimestamp(), $date->getTimestamp(), '->getDate() returns the current Date when the header has previously been removed'); } public function testGetMaxAge() diff --git a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockArraySessionStorageTest.php b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockArraySessionStorageTest.php index 99da778b5f..82df5543eb 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockArraySessionStorageTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/Session/Storage/MockArraySessionStorageTest.php @@ -97,6 +97,30 @@ class MockArraySessionStorageTest extends TestCase $this->assertNotEquals('', $this->storage->getId()); } + public function testClearClearsBags() + { + $this->storage->clear(); + + $this->assertSame(array(), $this->storage->getBag('attributes')->all()); + $this->assertSame(array(), $this->storage->getBag('flashes')->peekAll()); + } + + public function testClearStartsSession() + { + $this->storage->clear(); + + $this->assertTrue($this->storage->isStarted()); + } + + public function testClearWithNoBagsStartsSession() + { + $storage = new MockArraySessionStorage(); + + $storage->clear(); + + $this->assertTrue($storage->isStarted()); + } + /** * @expectedException \RuntimeException */ diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php index 2e9c0f34b4..0eee6d94a4 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php @@ -150,7 +150,7 @@ abstract class AbstractToken implements TokenInterface array( is_object($this->user) ? clone $this->user : $this->user, $this->authenticated, - $this->roles, + array_map(function ($role) { return clone $role; }, $this->roles), $this->attributes, ) ); diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php index 50990b7e46..da6136f221 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Provider/UserAuthenticationProviderTest.php @@ -221,7 +221,7 @@ class UserAuthenticationProviderTest extends TestCase $this->assertInstanceOf('Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken', $authToken); $this->assertSame($user, $authToken->getUser()); $this->assertContains(new Role('ROLE_FOO'), $authToken->getRoles(), '', false, false); - $this->assertContains($switchUserRole, $authToken->getRoles()); + $this->assertContains($switchUserRole, $authToken->getRoles(), '', false, false); $this->assertEquals('foo', $authToken->getCredentials()); $this->assertEquals(array('foo' => 'bar'), $authToken->getAttributes(), '->authenticate() copies token 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 57ebf184f2..4cdf982676 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\AbstractToken; use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Role\SwitchUserRole; +use Symfony\Component\Security\Core\User\User; class TestUser { @@ -89,7 +90,7 @@ class AbstractTokenTest extends TestCase public function testSerialize() { - $token = $this->getToken(array('ROLE_FOO')); + $token = $this->getToken(array('ROLE_FOO', new Role('ROLE_BAR'))); $token->setAttributes(array('foo' => 'bar')); $uToken = unserialize(serialize($token)); @@ -98,6 +99,19 @@ class AbstractTokenTest extends TestCase $this->assertEquals($token->getAttributes(), $uToken->getAttributes()); } + public function testSerializeWithRoleObjects() + { + $user = new User('name', 'password', array(new Role('ROLE_FOO'), new Role('ROLE_BAR'))); + $token = new ConcreteToken($user, $user->getRoles()); + + $serialized = serialize($token); + $unserialized = unserialize($serialized); + + $roles = $unserialized->getRoles(); + + $this->assertEquals($roles, $user->getRoles()); + } + public function testSerializeParent() { $user = new TestUser('fabien'); diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php index 7c998f530d..e580e06b01 100644 --- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php +++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php @@ -33,7 +33,9 @@ class UrlValidator extends ConstraintValidator \] # an IPv6 address ) (:[0-9]+)? # a port (optional) - (/?|/\S+|\?\S*|\#\S*) # a /, nothing, a / with something, a query or a fragment + (?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%%[0-9A-Fa-f]{2})* )* # a path + (?:\? (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a query (optional) + (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a fragment (optional) $~ixu'; /** diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php index 37dcd63453..c48d45a2e5 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php @@ -123,6 +123,7 @@ class UrlValidatorTest extends ConstraintValidatorTestCase array('http://symfony.com#'), array('http://symfony.com#fragment'), array('http://symfony.com/#fragment'), + array('http://symfony.com/#one_more%20test'), ); } @@ -163,6 +164,9 @@ class UrlValidatorTest extends ConstraintValidatorTestCase array('http://:password@@symfony.com'), array('http://username:passwordsymfony.com'), array('http://usern@me:password@symfony.com'), + array('http://example.com/exploit.html?'), + array('http://example.com/exploit.html?hel lo'), + array('http://example.com/exploit.html?not_a%hex'), ); }