Merge branch '3.4' into 4.1
* 3.4: [DoctrineBridge] support __toString as documented for UniqueEntityValidator [travis] enable Redis cluster [Cache] enable Memcached::OPT_TCP_NODELAY to fix perf of misses fix data mapper return type in docblock fix type error handling when writing values
This commit is contained in:
commit
51525e63f8
|
@ -41,8 +41,15 @@ services:
|
|||
- mongodb
|
||||
- redis-server
|
||||
- rabbitmq
|
||||
- docker
|
||||
|
||||
before_install:
|
||||
- |
|
||||
# Start Redis cluster
|
||||
docker pull grokzen/redis-cluster:4.0.8
|
||||
docker run -d -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 --name redis-cluster grokzen/redis-cluster:4.0.8
|
||||
export REDIS_CLUSTER_HOSTS='localhost:7000 localhost:7001 localhost:7002 localhost:7003 localhost:7004 localhost:7005'
|
||||
|
||||
- |
|
||||
# General configuration
|
||||
set -e
|
||||
|
|
|
@ -482,7 +482,7 @@ class UniqueEntityValidatorTest extends ConstraintValidatorTestCase
|
|||
|
||||
$this->buildViolation('myMessage')
|
||||
->atPath('property.path.single')
|
||||
->setParameter('{{ value }}', 'object("Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity") identified by (id => 1)')
|
||||
->setParameter('{{ value }}', 'foo')
|
||||
->setInvalidValue($entity1)
|
||||
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
|
||||
->setCause(array($associated, $associated2))
|
||||
|
|
|
@ -186,6 +186,10 @@ class UniqueEntityValidator extends ConstraintValidator
|
|||
return $this->formatValue($value, self::PRETTY_DATE);
|
||||
}
|
||||
|
||||
if (\method_exists($value, '__toString')) {
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
if ($class->getName() !== $idClass = \get_class($value)) {
|
||||
// non unique value might be a composite PK that consists of other entity objects
|
||||
if ($em->getMetadataFactory()->hasMetadataFor($idClass)) {
|
||||
|
|
|
@ -89,6 +89,7 @@ class MemcachedAdapterTest extends AdapterTestCase
|
|||
|
||||
$this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION));
|
||||
$this->assertSame(1, $client->getOption(\Memcached::OPT_BINARY_PROTOCOL));
|
||||
$this->assertSame(1, $client->getOption(\Memcached::OPT_TCP_NODELAY));
|
||||
$this->assertSame(1, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE));
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Tests\Adapter;
|
||||
|
||||
class RedisClusterAdapterTest extends AbstractRedisAdapterTest
|
||||
{
|
||||
public static function setupBeforeClass()
|
||||
{
|
||||
if (!class_exists('RedisCluster')) {
|
||||
self::markTestSkipped('The RedisCluster class is required.');
|
||||
}
|
||||
if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) {
|
||||
self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.');
|
||||
}
|
||||
|
||||
self::$redis = new \RedisCluster(null, explode(' ', $hosts));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Tests\Simple;
|
||||
|
||||
class RedisClusterCacheTest extends AbstractRedisCacheTest
|
||||
{
|
||||
public static function setupBeforeClass()
|
||||
{
|
||||
if (!class_exists('RedisCluster')) {
|
||||
self::markTestSkipped('The RedisCluster class is required.');
|
||||
}
|
||||
if (!$hosts = getenv('REDIS_CLUSTER_HOSTS')) {
|
||||
self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.');
|
||||
}
|
||||
|
||||
self::$redis = new \RedisCluster(null, explode(' ', $hosts));
|
||||
}
|
||||
}
|
|
@ -134,6 +134,7 @@ trait MemcachedTrait
|
|||
$options = array_change_key_case($options, CASE_UPPER);
|
||||
$client->setOption(\Memcached::OPT_BINARY_PROTOCOL, true);
|
||||
$client->setOption(\Memcached::OPT_NO_BLOCK, true);
|
||||
$client->setOption(\Memcached::OPT_TCP_NODELAY, true);
|
||||
if (!array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) {
|
||||
$client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ interface FormConfigInterface
|
|||
/**
|
||||
* Returns the data mapper of the form.
|
||||
*
|
||||
* @return DataMapperInterface The data mapper
|
||||
* @return DataMapperInterface|null The data mapper
|
||||
*/
|
||||
public function getDataMapper();
|
||||
|
||||
|
|
|
@ -54,8 +54,10 @@ class RedisStore implements StoreInterface
|
|||
$script = '
|
||||
if redis.call("GET", KEYS[1]) == ARGV[1] then
|
||||
return redis.call("PEXPIRE", KEYS[1], ARGV[2])
|
||||
elseif redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2]) then
|
||||
return 1
|
||||
else
|
||||
return redis.call("set", KEYS[1], ARGV[1], "NX", "PX", ARGV[2])
|
||||
return 0
|
||||
end
|
||||
';
|
||||
|
||||
|
@ -140,7 +142,7 @@ class RedisStore implements StoreInterface
|
|||
return \call_user_func_array(array($this->redis, 'eval'), array_merge(array($script, 1, $resource), $args));
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('%s() expects been initialized with a Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, \is_object($this->redis) ? \get_class($this->redis) : \gettype($this->redis)));
|
||||
throw new InvalidArgumentException(sprintf('%s() expects being initialized with a Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, \is_object($this->redis) ? \get_class($this->redis) : \gettype($this->redis)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Lock\Tests\Store;
|
||||
|
||||
/**
|
||||
* @author Jérémy Derussé <jeremy@derusse.com>
|
||||
*
|
||||
* @requires extension redis
|
||||
*/
|
||||
class RedisClusterStoreTest extends AbstractRedisStoreTest
|
||||
{
|
||||
public static function setupBeforeClass()
|
||||
{
|
||||
if (!class_exists('RedisCluster')) {
|
||||
self::markTestSkipped('The RedisCluster class is required.');
|
||||
}
|
||||
if (!getenv('REDIS_CLUSTER_HOSTS')) {
|
||||
self::markTestSkipped('REDIS_CLUSTER_HOSTS env var is not defined.');
|
||||
}
|
||||
}
|
||||
|
||||
protected function getRedisConnection()
|
||||
{
|
||||
return new \RedisCluster(null, explode(' ', getenv('REDIS_CLUSTER_HOSTS')));
|
||||
}
|
||||
}
|
|
@ -160,7 +160,12 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||
|
||||
private static function throwInvalidArgumentException($message, $trace, $i)
|
||||
{
|
||||
if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file'] && isset($trace[$i]['args'][0])) {
|
||||
// the type mismatch is not caused by invalid arguments (but e.g. by an incompatible return type hint of the writer method)
|
||||
if (0 !== strpos($message, 'Argument ')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($trace[$i]['file']) && __FILE__ === $trace[$i]['file'] && array_key_exists(0, $trace[$i]['args'])) {
|
||||
$pos = strpos($message, $delim = 'must be of the type ') ?: (strpos($message, $delim = 'must be an instance of ') ?: strpos($message, $delim = 'must implement interface '));
|
||||
$pos += \strlen($delim);
|
||||
$type = $trace[$i]['args'][0];
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ReturnTyped
|
||||
{
|
||||
public function getFoos(): array
|
||||
{
|
||||
return 'It doesn\'t respect the return type on purpose';
|
||||
}
|
||||
|
||||
public function addFoo(\DateTime $dateTime)
|
||||
{
|
||||
}
|
||||
|
||||
public function removeFoo(\DateTime $dateTime)
|
||||
{
|
||||
}
|
||||
|
||||
public function setName($name): self
|
||||
{
|
||||
return 'This does not respect the return type on purpose.';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\PropertyAccess\Tests\Fixtures;
|
||||
|
||||
class TestClassTypeErrorInsideCall
|
||||
{
|
||||
public function expectsDateTime(\DateTime $date)
|
||||
{
|
||||
}
|
||||
|
||||
public function getProperty()
|
||||
{
|
||||
}
|
||||
|
||||
public function setProperty($property)
|
||||
{
|
||||
$this->expectsDateTime(null); // throws TypeError
|
||||
}
|
||||
}
|
|
@ -15,11 +15,13 @@ use PHPUnit\Framework\TestCase;
|
|||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessor;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\ReturnTyped;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClass;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassIsWritable;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicCall;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicGet;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassSetValue;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassTypeErrorInsideCall;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\Ticket5775Object;
|
||||
use Symfony\Component\PropertyAccess\Tests\Fixtures\TypeHinted;
|
||||
|
||||
|
@ -538,6 +540,17 @@ class PropertyAccessorTest extends TestCase
|
|||
$this->propertyAccessor->setValue($object, 'date', 'This is a string, \DateTime expected.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException
|
||||
* @expectedExceptionMessage Expected argument of type "DateTime", "NULL" given
|
||||
*/
|
||||
public function testThrowTypeErrorWithNullArgument()
|
||||
{
|
||||
$object = new TypeHinted();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'date', null);
|
||||
}
|
||||
|
||||
public function testSetTypeHint()
|
||||
{
|
||||
$date = new \DateTime();
|
||||
|
@ -632,4 +645,34 @@ class PropertyAccessorTest extends TestCase
|
|||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \TypeError
|
||||
*/
|
||||
public function testThrowTypeErrorInsideSetterCall()
|
||||
{
|
||||
$object = new TestClassTypeErrorInsideCall();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'property', 'foo');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \TypeError
|
||||
*/
|
||||
public function testDoNotDiscardReturnTypeError()
|
||||
{
|
||||
$object = new ReturnTyped();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'foos', array(new \DateTime()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \TypeError
|
||||
*/
|
||||
public function testDoNotDiscardReturnTypeErrorWhenWriterMethodIsMisconfigured()
|
||||
{
|
||||
$object = new ReturnTyped();
|
||||
|
||||
$this->propertyAccessor->setValue($object, 'name', 'foo');
|
||||
}
|
||||
}
|
||||
|
|
Reference in New Issue