Merge branch '4.4'
* 4.4: [Cache] Add types to constructors and private/final/internal methods. [HttpClient] Allow enabling buffering conditionally with a Closure
This commit is contained in:
commit
41b9d81292
@ -256,7 +256,7 @@ class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterfa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getId($key)
|
private function getId($key): string
|
||||||
{
|
{
|
||||||
CacheItem::validateKey($key);
|
CacheItem::validateKey($key);
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ final class CacheItem implements ItemInterface
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
public static function log(LoggerInterface $logger = null, $message, $context = [])
|
public static function log(?LoggerInterface $logger, string $message, array $context = [])
|
||||||
{
|
{
|
||||||
if ($logger) {
|
if ($logger) {
|
||||||
$logger->warning($message, $context);
|
$logger->warning($message, $context);
|
||||||
|
@ -130,6 +130,8 @@ final class LockRegistry
|
|||||||
$logger && $logger->info('Item "{key}" not found while lock was released, now retrying', ['key' => $item->getKey()]);
|
$logger && $logger->info('Item "{key}" not found while lock was released, now retrying', ['key' => $item->getKey()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function open(int $key)
|
private static function open(int $key)
|
||||||
|
@ -24,7 +24,7 @@ abstract class AbstractRedisAdapterTest extends AdapterTestCase
|
|||||||
|
|
||||||
protected static $redis;
|
protected static $redis;
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
return new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
return new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class ApcuAdapterTest extends AdapterTestCase
|
|||||||
'testDefaultLifeTime' => 'Testing expiration slows down the test suite',
|
'testDefaultLifeTime' => 'Testing expiration slows down the test suite',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
if (!\function_exists('apcu_fetch') || !filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN)) {
|
if (!\function_exists('apcu_fetch') || !filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN)) {
|
||||||
$this->markTestSkipped('APCu extension is required.');
|
$this->markTestSkipped('APCu extension is required.');
|
||||||
|
@ -25,7 +25,7 @@ class ArrayAdapterTest extends AdapterTestCase
|
|||||||
'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayAdapter is not.',
|
'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayAdapter is not.',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
return new ArrayAdapter($defaultLifetime);
|
return new ArrayAdapter($defaultLifetime);
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Cache\Tests\Adapter;
|
namespace Symfony\Component\Cache\Tests\Adapter;
|
||||||
|
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
|
||||||
use Psr\Cache\CacheItemPoolInterface;
|
use Psr\Cache\CacheItemPoolInterface;
|
||||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||||
@ -26,7 +25,7 @@ use Symfony\Component\Cache\Tests\Fixtures\ExternalAdapter;
|
|||||||
*/
|
*/
|
||||||
class ChainAdapterTest extends AdapterTestCase
|
class ChainAdapterTest extends AdapterTestCase
|
||||||
{
|
{
|
||||||
public function createCachePool($defaultLifetime = 0, $testMethod = null): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0, string $testMethod = null): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
if ('testGetMetadata' === $testMethod) {
|
if ('testGetMetadata' === $testMethod) {
|
||||||
return new ChainAdapter([new FilesystemAdapter('', $defaultLifetime)], $defaultLifetime);
|
return new ChainAdapter([new FilesystemAdapter('', $defaultLifetime)], $defaultLifetime);
|
||||||
@ -70,14 +69,9 @@ class ChainAdapterTest extends AdapterTestCase
|
|||||||
$this->assertFalse($cache->prune());
|
$this->assertFalse($cache->prune());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function getPruneableMock(): AdapterInterface
|
||||||
* @return MockObject|PruneableCacheInterface
|
|
||||||
*/
|
|
||||||
private function getPruneableMock(): object
|
|
||||||
{
|
{
|
||||||
$pruneable = $this
|
$pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]);
|
||||||
->getMockBuilder(PruneableCacheInterface::class)
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$pruneable
|
$pruneable
|
||||||
->expects($this->atLeastOnce())
|
->expects($this->atLeastOnce())
|
||||||
@ -87,14 +81,9 @@ class ChainAdapterTest extends AdapterTestCase
|
|||||||
return $pruneable;
|
return $pruneable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function getFailingPruneableMock(): AdapterInterface
|
||||||
* @return MockObject|PruneableCacheInterface
|
|
||||||
*/
|
|
||||||
private function getFailingPruneableMock(): object
|
|
||||||
{
|
{
|
||||||
$pruneable = $this
|
$pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]);
|
||||||
->getMockBuilder(PruneableCacheInterface::class)
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$pruneable
|
$pruneable
|
||||||
->expects($this->atLeastOnce())
|
->expects($this->atLeastOnce())
|
||||||
@ -104,17 +93,8 @@ class ChainAdapterTest extends AdapterTestCase
|
|||||||
return $pruneable;
|
return $pruneable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function getNonPruneableMock(): AdapterInterface
|
||||||
* @return MockObject|AdapterInterface
|
|
||||||
*/
|
|
||||||
private function getNonPruneableMock(): object
|
|
||||||
{
|
{
|
||||||
return $this
|
return $this->createMock(AdapterInterface::class);
|
||||||
->getMockBuilder(AdapterInterface::class)
|
|
||||||
->getMock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PruneableCacheInterface extends PruneableInterface, AdapterInterface
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
@ -27,7 +27,7 @@ class DoctrineAdapterTest extends AdapterTestCase
|
|||||||
'testClearPrefix' => 'Doctrine cannot clear by prefix',
|
'testClearPrefix' => 'Doctrine cannot clear by prefix',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
return new DoctrineAdapter(new ArrayCache($defaultLifetime), '', $defaultLifetime);
|
return new DoctrineAdapter(new ArrayCache($defaultLifetime), '', $defaultLifetime);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
|||||||
*/
|
*/
|
||||||
class FilesystemAdapterTest extends AdapterTestCase
|
class FilesystemAdapterTest extends AdapterTestCase
|
||||||
{
|
{
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
return new FilesystemAdapter('', $defaultLifetime);
|
return new FilesystemAdapter('', $defaultLifetime);
|
||||||
}
|
}
|
||||||
@ -29,7 +29,7 @@ class FilesystemAdapterTest extends AdapterTestCase
|
|||||||
self::rmdir(sys_get_temp_dir().'/symfony-cache');
|
self::rmdir(sys_get_temp_dir().'/symfony-cache');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function rmdir($dir)
|
public static function rmdir(string $dir)
|
||||||
{
|
{
|
||||||
if (!file_exists($dir)) {
|
if (!file_exists($dir)) {
|
||||||
return;
|
return;
|
||||||
@ -51,7 +51,7 @@ class FilesystemAdapterTest extends AdapterTestCase
|
|||||||
rmdir($dir);
|
rmdir($dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isPruned(CacheItemPoolInterface $cache, $name)
|
protected function isPruned(CacheItemPoolInterface $cache, string $name): bool
|
||||||
{
|
{
|
||||||
$getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile');
|
$getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile');
|
||||||
$getFileMethod->setAccessible(true);
|
$getFileMethod->setAccessible(true);
|
||||||
|
@ -22,7 +22,7 @@ class FilesystemTagAwareAdapterTest extends FilesystemAdapterTest
|
|||||||
{
|
{
|
||||||
use TagAwareTestTrait;
|
use TagAwareTestTrait;
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
return new FilesystemTagAwareAdapter('', $defaultLifetime);
|
return new FilesystemTagAwareAdapter('', $defaultLifetime);
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ abstract class MaxIdLengthAdapter extends AbstractAdapter
|
|||||||
{
|
{
|
||||||
protected $maxIdLength = 50;
|
protected $maxIdLength = 50;
|
||||||
|
|
||||||
public function __construct($ns)
|
public function __construct(string $ns)
|
||||||
{
|
{
|
||||||
parent::__construct($ns);
|
parent::__construct($ns);
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ class MemcachedAdapterTest extends AdapterTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
$client = $defaultLifetime ? AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST')) : self::$client;
|
$client = $defaultLifetime ? AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST')) : self::$client;
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ class MemcachedAdapterTest extends AdapterTestCase
|
|||||||
MemcachedAdapter::createConnection([], [$name => $value]);
|
MemcachedAdapter::createConnection([], [$name => $value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideBadOptions()
|
public function provideBadOptions(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['foo', 'bar'],
|
['foo', 'bar'],
|
||||||
@ -109,7 +109,7 @@ class MemcachedAdapterTest extends AdapterTestCase
|
|||||||
/**
|
/**
|
||||||
* @dataProvider provideServersSetting
|
* @dataProvider provideServersSetting
|
||||||
*/
|
*/
|
||||||
public function testServersSetting($dsn, $host, $port)
|
public function testServersSetting(string $dsn, string $host, int $port)
|
||||||
{
|
{
|
||||||
$client1 = MemcachedAdapter::createConnection($dsn);
|
$client1 = MemcachedAdapter::createConnection($dsn);
|
||||||
$client2 = MemcachedAdapter::createConnection([$dsn]);
|
$client2 = MemcachedAdapter::createConnection([$dsn]);
|
||||||
@ -125,7 +125,7 @@ class MemcachedAdapterTest extends AdapterTestCase
|
|||||||
$this->assertSame([$expect], array_map($f, $client3->getServerList()));
|
$this->assertSame([$expect], array_map($f, $client3->getServerList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideServersSetting()
|
public function provideServersSetting(): iterable
|
||||||
{
|
{
|
||||||
yield [
|
yield [
|
||||||
'memcached://127.0.0.1/50',
|
'memcached://127.0.0.1/50',
|
||||||
@ -166,7 +166,7 @@ class MemcachedAdapterTest extends AdapterTestCase
|
|||||||
/**
|
/**
|
||||||
* @dataProvider provideDsnWithOptions
|
* @dataProvider provideDsnWithOptions
|
||||||
*/
|
*/
|
||||||
public function testDsnWithOptions($dsn, array $options, array $expectedOptions)
|
public function testDsnWithOptions(string $dsn, array $options, array $expectedOptions)
|
||||||
{
|
{
|
||||||
$client = MemcachedAdapter::createConnection($dsn, $options);
|
$client = MemcachedAdapter::createConnection($dsn, $options);
|
||||||
|
|
||||||
@ -175,7 +175,7 @@ class MemcachedAdapterTest extends AdapterTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideDsnWithOptions()
|
public function provideDsnWithOptions(): iterable
|
||||||
{
|
{
|
||||||
if (!class_exists('\Memcached')) {
|
if (!class_exists('\Memcached')) {
|
||||||
self::markTestSkipped('Extension memcached required.');
|
self::markTestSkipped('Extension memcached required.');
|
||||||
|
@ -21,7 +21,7 @@ use Symfony\Component\Cache\Adapter\ProxyAdapter;
|
|||||||
*/
|
*/
|
||||||
class NamespacedProxyAdapterTest extends ProxyAdapterTest
|
class NamespacedProxyAdapterTest extends ProxyAdapterTest
|
||||||
{
|
{
|
||||||
public function createCachePool($defaultLifetime = 0, $testMethod = null): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0, string $testMethod = null): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
if ('testGetMetadata' === $testMethod) {
|
if ('testGetMetadata' === $testMethod) {
|
||||||
return new ProxyAdapter(new FilesystemAdapter(), 'foo', $defaultLifetime);
|
return new ProxyAdapter(new FilesystemAdapter(), 'foo', $defaultLifetime);
|
||||||
|
@ -41,7 +41,7 @@ class PdoAdapterTest extends AdapterTestCase
|
|||||||
@unlink(self::$dbFile);
|
@unlink(self::$dbFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
return new PdoAdapter('sqlite:'.self::$dbFile, 'ns', $defaultLifetime);
|
return new PdoAdapter('sqlite:'.self::$dbFile, 'ns', $defaultLifetime);
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ class PdoDbalAdapterTest extends AdapterTestCase
|
|||||||
@unlink(self::$dbFile);
|
@unlink(self::$dbFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
return new PdoAdapter(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]), '', $defaultLifetime);
|
return new PdoAdapter(DriverManager::getConnection(['driver' => 'pdo_sqlite', 'path' => self::$dbFile]), '', $defaultLifetime);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ class PhpArrayAdapterTest extends AdapterTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0, $testMethod = null): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0, string $testMethod = null): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
if ('testGetMetadata' === $testMethod || 'testClearPrefix' === $testMethod) {
|
if ('testGetMetadata' === $testMethod || 'testClearPrefix' === $testMethod) {
|
||||||
return new PhpArrayAdapter(self::$file, new FilesystemAdapter());
|
return new PhpArrayAdapter(self::$file, new FilesystemAdapter());
|
||||||
|
@ -43,7 +43,7 @@ class PhpArrayAdapterWithFallbackTest extends AdapterTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
return new PhpArrayAdapter(self::$file, new FilesystemAdapter('php-array-fallback', $defaultLifetime));
|
return new PhpArrayAdapter(self::$file, new FilesystemAdapter('php-array-fallback', $defaultLifetime));
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ class PhpFilesAdapterTest extends AdapterTestCase
|
|||||||
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
|
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isPruned(CacheItemPoolInterface $cache, $name)
|
protected function isPruned(CacheItemPoolInterface $cache, string $name): bool
|
||||||
{
|
{
|
||||||
$getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile');
|
$getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile');
|
||||||
$getFileMethod->setAccessible(true);
|
$getFileMethod->setAccessible(true);
|
||||||
|
@ -25,7 +25,7 @@ class PredisTagAwareAdapterTest extends PredisAdapterTest
|
|||||||
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
$this->assertInstanceOf(\Predis\Client::class, self::$redis);
|
$this->assertInstanceOf(\Predis\Client::class, self::$redis);
|
||||||
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
||||||
|
@ -25,7 +25,7 @@ class PredisTagAwareClusterAdapterTest extends PredisClusterAdapterTest
|
|||||||
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
$this->assertInstanceOf(\Predis\Client::class, self::$redis);
|
$this->assertInstanceOf(\Predis\Client::class, self::$redis);
|
||||||
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
||||||
|
@ -25,7 +25,7 @@ class PredisTagAwareRedisClusterAdapterTest extends PredisRedisClusterAdapterTes
|
|||||||
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
$this->assertInstanceOf(\Predis\Client::class, self::$redis);
|
$this->assertInstanceOf(\Predis\Client::class, self::$redis);
|
||||||
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
||||||
|
@ -29,7 +29,7 @@ class ProxyAdapterTest extends AdapterTestCase
|
|||||||
'testPrune' => 'ProxyAdapter just proxies',
|
'testPrune' => 'ProxyAdapter just proxies',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0, $testMethod = null): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0, string $testMethod = null): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
if ('testGetMetadata' === $testMethod) {
|
if ('testGetMetadata' === $testMethod) {
|
||||||
return new ProxyAdapter(new FilesystemAdapter(), '', $defaultLifetime);
|
return new ProxyAdapter(new FilesystemAdapter(), '', $defaultLifetime);
|
||||||
|
@ -27,7 +27,7 @@ class Psr16AdapterTest extends AdapterTestCase
|
|||||||
'testClearPrefix' => 'SimpleCache cannot clear by prefix',
|
'testClearPrefix' => 'SimpleCache cannot clear by prefix',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
return new Psr16Adapter(new Psr16Cache(new FilesystemAdapter()), '', $defaultLifetime);
|
return new Psr16Adapter(new Psr16Cache(new FilesystemAdapter()), '', $defaultLifetime);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ class RedisAdapterTest extends AbstractRedisAdapterTest
|
|||||||
self::$redis = AbstractAdapter::createConnection('redis://'.getenv('REDIS_HOST'), ['lazy' => true]);
|
self::$redis = AbstractAdapter::createConnection('redis://'.getenv('REDIS_HOST'), ['lazy' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
$adapter = parent::createCachePool($defaultLifetime);
|
$adapter = parent::createCachePool($defaultLifetime);
|
||||||
$this->assertInstanceOf(RedisProxy::class, self::$redis);
|
$this->assertInstanceOf(RedisProxy::class, self::$redis);
|
||||||
@ -35,7 +35,7 @@ class RedisAdapterTest extends AbstractRedisAdapterTest
|
|||||||
/**
|
/**
|
||||||
* @dataProvider provideValidSchemes
|
* @dataProvider provideValidSchemes
|
||||||
*/
|
*/
|
||||||
public function testCreateConnection($dsnScheme)
|
public function testCreateConnection(string $dsnScheme)
|
||||||
{
|
{
|
||||||
$redis = RedisAdapter::createConnection($dsnScheme.':?host[h1]&host[h2]&host[/foo:]');
|
$redis = RedisAdapter::createConnection($dsnScheme.':?host[h1]&host[h2]&host[/foo:]');
|
||||||
$this->assertInstanceOf(\RedisArray::class, $redis);
|
$this->assertInstanceOf(\RedisArray::class, $redis);
|
||||||
@ -65,14 +65,14 @@ class RedisAdapterTest extends AbstractRedisAdapterTest
|
|||||||
/**
|
/**
|
||||||
* @dataProvider provideFailedCreateConnection
|
* @dataProvider provideFailedCreateConnection
|
||||||
*/
|
*/
|
||||||
public function testFailedCreateConnection($dsn)
|
public function testFailedCreateConnection(string $dsn)
|
||||||
{
|
{
|
||||||
$this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException');
|
$this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException');
|
||||||
$this->expectExceptionMessage('Redis connection failed');
|
$this->expectExceptionMessage('Redis connection failed');
|
||||||
RedisAdapter::createConnection($dsn);
|
RedisAdapter::createConnection($dsn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideFailedCreateConnection()
|
public function provideFailedCreateConnection(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['redis://localhost:1234'],
|
['redis://localhost:1234'],
|
||||||
@ -84,14 +84,14 @@ class RedisAdapterTest extends AbstractRedisAdapterTest
|
|||||||
/**
|
/**
|
||||||
* @dataProvider provideInvalidCreateConnection
|
* @dataProvider provideInvalidCreateConnection
|
||||||
*/
|
*/
|
||||||
public function testInvalidCreateConnection($dsn)
|
public function testInvalidCreateConnection(string $dsn)
|
||||||
{
|
{
|
||||||
$this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException');
|
$this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException');
|
||||||
$this->expectExceptionMessage('Invalid Redis DSN');
|
$this->expectExceptionMessage('Invalid Redis DSN');
|
||||||
RedisAdapter::createConnection($dsn);
|
RedisAdapter::createConnection($dsn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideValidSchemes()
|
public function provideValidSchemes(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['redis'],
|
['redis'],
|
||||||
@ -99,7 +99,7 @@ class RedisAdapterTest extends AbstractRedisAdapterTest
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideInvalidCreateConnection()
|
public function provideInvalidCreateConnection(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['foo://localhost'],
|
['foo://localhost'],
|
||||||
|
@ -30,7 +30,7 @@ class RedisClusterAdapterTest extends AbstractRedisAdapterTest
|
|||||||
self::$redis = AbstractAdapter::createConnection('redis:?host['.str_replace(' ', ']&host[', $hosts).']', ['lazy' => true, 'redis_cluster' => true]);
|
self::$redis = AbstractAdapter::createConnection('redis:?host['.str_replace(' ', ']&host[', $hosts).']', ['lazy' => true, 'redis_cluster' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
$this->assertInstanceOf(RedisClusterProxy::class, self::$redis);
|
$this->assertInstanceOf(RedisClusterProxy::class, self::$redis);
|
||||||
$adapter = new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
$adapter = new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
||||||
@ -41,14 +41,14 @@ class RedisClusterAdapterTest extends AbstractRedisAdapterTest
|
|||||||
/**
|
/**
|
||||||
* @dataProvider provideFailedCreateConnection
|
* @dataProvider provideFailedCreateConnection
|
||||||
*/
|
*/
|
||||||
public function testFailedCreateConnection($dsn)
|
public function testFailedCreateConnection(string $dsn)
|
||||||
{
|
{
|
||||||
$this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException');
|
$this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException');
|
||||||
$this->expectExceptionMessage('Redis connection failed');
|
$this->expectExceptionMessage('Redis connection failed');
|
||||||
RedisAdapter::createConnection($dsn);
|
RedisAdapter::createConnection($dsn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideFailedCreateConnection()
|
public function provideFailedCreateConnection(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
['redis://localhost:1234?redis_cluster=1'],
|
['redis://localhost:1234?redis_cluster=1'],
|
||||||
|
@ -26,7 +26,7 @@ class RedisTagAwareAdapterTest extends RedisAdapterTest
|
|||||||
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
$this->assertInstanceOf(RedisProxy::class, self::$redis);
|
$this->assertInstanceOf(RedisProxy::class, self::$redis);
|
||||||
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
||||||
|
@ -25,7 +25,7 @@ class RedisTagAwareArrayAdapterTest extends RedisArrayAdapterTest
|
|||||||
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
$this->assertInstanceOf(\RedisArray::class, self::$redis);
|
$this->assertInstanceOf(\RedisArray::class, self::$redis);
|
||||||
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
||||||
|
@ -26,7 +26,7 @@ class RedisTagAwareClusterAdapterTest extends RedisClusterAdapterTest
|
|||||||
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
$this->skippedTests['testTagItemExpiry'] = 'Testing expiration slows down the test suite';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
$this->assertInstanceOf(RedisClusterProxy::class, self::$redis);
|
$this->assertInstanceOf(RedisClusterProxy::class, self::$redis);
|
||||||
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
$adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
|
||||||
|
@ -16,6 +16,7 @@ use Psr\Cache\CacheItemPoolInterface;
|
|||||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||||
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
|
use Symfony\Component\Cache\Adapter\TagAwareAdapter;
|
||||||
|
use Symfony\Component\Cache\PruneableInterface;
|
||||||
use Symfony\Component\Cache\Tests\Traits\TagAwareTestTrait;
|
use Symfony\Component\Cache\Tests\Traits\TagAwareTestTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,14 +67,9 @@ class TagAwareAdapterTest extends AdapterTestCase
|
|||||||
$this->assertFalse($cache->prune());
|
$this->assertFalse($cache->prune());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function getPruneableMock(): AdapterInterface
|
||||||
* @return MockObject|PruneableCacheInterface
|
|
||||||
*/
|
|
||||||
private function getPruneableMock(): object
|
|
||||||
{
|
{
|
||||||
$pruneable = $this
|
$pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]);
|
||||||
->getMockBuilder(PruneableCacheInterface::class)
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$pruneable
|
$pruneable
|
||||||
->expects($this->atLeastOnce())
|
->expects($this->atLeastOnce())
|
||||||
@ -83,14 +79,9 @@ class TagAwareAdapterTest extends AdapterTestCase
|
|||||||
return $pruneable;
|
return $pruneable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function getFailingPruneableMock(): AdapterInterface
|
||||||
* @return MockObject|PruneableCacheInterface
|
|
||||||
*/
|
|
||||||
private function getFailingPruneableMock(): object
|
|
||||||
{
|
{
|
||||||
$pruneable = $this
|
$pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]);
|
||||||
->getMockBuilder(PruneableCacheInterface::class)
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$pruneable
|
$pruneable
|
||||||
->expects($this->atLeastOnce())
|
->expects($this->atLeastOnce())
|
||||||
@ -100,13 +91,8 @@ class TagAwareAdapterTest extends AdapterTestCase
|
|||||||
return $pruneable;
|
return $pruneable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function getNonPruneableMock(): AdapterInterface
|
||||||
* @return MockObject|AdapterInterface
|
|
||||||
*/
|
|
||||||
private function getNonPruneableMock(): object
|
|
||||||
{
|
{
|
||||||
return $this
|
return $this->createMock(AdapterInterface::class);
|
||||||
->getMockBuilder(AdapterInterface::class)
|
|
||||||
->getMock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class TagAwareAndProxyAdapterIntegrationTest extends TestCase
|
|||||||
$this->assertSame('bar', $cache->getItem('foo')->get());
|
$this->assertSame('bar', $cache->getItem('foo')->get());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataProvider()
|
public function dataProvider(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[new ArrayAdapter()],
|
[new ArrayAdapter()],
|
||||||
|
@ -24,7 +24,7 @@ class TraceableAdapterTest extends AdapterTestCase
|
|||||||
'testPrune' => 'TraceableAdapter just proxies',
|
'testPrune' => 'TraceableAdapter just proxies',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface
|
public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface
|
||||||
{
|
{
|
||||||
return new TraceableAdapter(new FilesystemAdapter('', $defaultLifetime));
|
return new TraceableAdapter(new FilesystemAdapter('', $defaultLifetime));
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ class CacheItemTest extends TestCase
|
|||||||
CacheItem::validateKey($key);
|
CacheItem::validateKey($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideInvalidKey()
|
public function provideInvalidKey(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[''],
|
[''],
|
||||||
|
@ -40,7 +40,7 @@ class Psr16CacheTest extends SimpleCacheTest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createSimpleCache($defaultLifetime = 0): CacheInterface
|
public function createSimpleCache(int $defaultLifetime = 0): CacheInterface
|
||||||
{
|
{
|
||||||
return new Psr16Cache(new FilesystemAdapter('', $defaultLifetime));
|
return new Psr16Cache(new FilesystemAdapter('', $defaultLifetime));
|
||||||
}
|
}
|
||||||
@ -146,7 +146,7 @@ class Psr16CacheTest extends SimpleCacheTest
|
|||||||
$cache->clear();
|
$cache->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isPruned($cache, $name)
|
protected function isPruned(CacheInterface $cache, string $name): bool
|
||||||
{
|
{
|
||||||
if (Psr16Cache::class !== \get_class($cache)) {
|
if (Psr16Cache::class !== \get_class($cache)) {
|
||||||
$this->fail('Test classes for pruneable caches must implement `isPruned($cache, $name)` method.');
|
$this->fail('Test classes for pruneable caches must implement `isPruned($cache, $name)` method.');
|
||||||
|
@ -13,7 +13,7 @@ namespace Symfony\Component\Cache\Tests\Traits;
|
|||||||
|
|
||||||
trait PdoPruneableTrait
|
trait PdoPruneableTrait
|
||||||
{
|
{
|
||||||
protected function isPruned($cache, $name)
|
protected function isPruned($cache, string $name): bool
|
||||||
{
|
{
|
||||||
$o = new \ReflectionObject($cache);
|
$o = new \ReflectionObject($cache);
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ use Symfony\Component\Cache\CacheItem;
|
|||||||
/**
|
/**
|
||||||
* Common assertions for TagAware adapters.
|
* Common assertions for TagAware adapters.
|
||||||
*
|
*
|
||||||
* @method \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface createCachePool() Must be implemented by TestCase
|
* @method \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface createCachePool(int $defaultLifetime = 0) Must be implemented by TestCase
|
||||||
*/
|
*/
|
||||||
trait TagAwareTestTrait
|
trait TagAwareTestTrait
|
||||||
{
|
{
|
||||||
|
@ -313,7 +313,7 @@ trait AbstractAdapterTrait
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateItems(iterable $items, array &$keys)
|
private function generateItems(iterable $items, array &$keys): iterable
|
||||||
{
|
{
|
||||||
$f = $this->createCacheItem;
|
$f = $this->createCacheItem;
|
||||||
|
|
||||||
|
@ -423,7 +423,7 @@ trait RedisTrait
|
|||||||
return $failed;
|
return $failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function pipeline(\Closure $generator)
|
private function pipeline(\Closure $generator): \Generator
|
||||||
{
|
{
|
||||||
$ids = [];
|
$ids = [];
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ CHANGELOG
|
|||||||
* added `$response->toStream()` to cast responses to regular PHP streams
|
* added `$response->toStream()` to cast responses to regular PHP streams
|
||||||
* made `Psr18Client` implement relevant PSR-17 factories and have streaming responses
|
* made `Psr18Client` implement relevant PSR-17 factories and have streaming responses
|
||||||
* added `TraceableHttpClient`, `HttpClientDataCollector` and `HttpClientPass` to integrate with the web profiler
|
* added `TraceableHttpClient`, `HttpClientDataCollector` and `HttpClientPass` to integrate with the web profiler
|
||||||
|
* allow enabling buffering conditionally with a Closure
|
||||||
|
|
||||||
4.3.0
|
4.3.0
|
||||||
-----
|
-----
|
||||||
|
@ -68,9 +68,8 @@ class CachingHttpClient implements HttpClientInterface
|
|||||||
{
|
{
|
||||||
[$url, $options] = $this->prepareRequest($method, $url, $options, $this->defaultOptions, true);
|
[$url, $options] = $this->prepareRequest($method, $url, $options, $this->defaultOptions, true);
|
||||||
$url = implode('', $url);
|
$url = implode('', $url);
|
||||||
$options['extra']['no_cache'] = $options['extra']['no_cache'] ?? !$options['buffer'];
|
|
||||||
|
|
||||||
if (!empty($options['body']) || $options['extra']['no_cache'] || !\in_array($method, ['GET', 'HEAD', 'OPTIONS'])) {
|
if (!empty($options['body']) || !empty($options['extra']['no_cache']) || !\in_array($method, ['GET', 'HEAD', 'OPTIONS'])) {
|
||||||
return $this->client->request($method, $url, $options);
|
return $this->client->request($method, $url, $options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,9 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface
|
|||||||
use HttpClientTrait;
|
use HttpClientTrait;
|
||||||
use LoggerAwareTrait;
|
use LoggerAwareTrait;
|
||||||
|
|
||||||
private $defaultOptions = self::OPTIONS_DEFAULTS + [
|
private $defaultOptions = [
|
||||||
|
'buffer' => null, // bool|\Closure - a boolean or a closure telling if the response should be buffered based on its headers
|
||||||
|
] + self::OPTIONS_DEFAULTS + [
|
||||||
'auth_ntlm' => null, // array|string - an array containing the username as first value, and optionally the
|
'auth_ntlm' => null, // array|string - an array containing the username as first value, and optionally the
|
||||||
// password as the second one; or string like username:password - enabling NTLM auth
|
// password as the second one; or string like username:password - enabling NTLM auth
|
||||||
];
|
];
|
||||||
@ -62,8 +64,10 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface
|
|||||||
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\CurlHttpClient" as the "curl" extension is not installed.');
|
throw new \LogicException('You cannot use the "Symfony\Component\HttpClient\CurlHttpClient" as the "curl" extension is not installed.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->defaultOptions['buffer'] = \Closure::fromCallable([__CLASS__, 'shouldBuffer']);
|
||||||
|
|
||||||
if ($defaultOptions) {
|
if ($defaultOptions) {
|
||||||
[, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, self::OPTIONS_DEFAULTS);
|
[, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, $this->defaultOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->multi = $multi = new CurlClientState();
|
$this->multi = $multi = new CurlClientState();
|
||||||
|
@ -503,4 +503,15 @@ trait HttpClientTrait
|
|||||||
|
|
||||||
return implode('&', $replace ? array_replace($query, $queryArray) : ($query + $queryArray));
|
return implode('&', $replace ? array_replace($query, $queryArray) : ($query + $queryArray));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function shouldBuffer(array $headers): bool
|
||||||
|
{
|
||||||
|
$contentType = $headers['content-type'][0] ?? null;
|
||||||
|
|
||||||
|
if (false !== $i = strpos($contentType, ';')) {
|
||||||
|
$contentType = substr($contentType, 0, $i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $contentType && preg_match('#^(?:text/|application/(?:.+\+)?(?:json|xml)$)#i', $contentType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,9 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
|
|||||||
use HttpClientTrait;
|
use HttpClientTrait;
|
||||||
use LoggerAwareTrait;
|
use LoggerAwareTrait;
|
||||||
|
|
||||||
private $defaultOptions = self::OPTIONS_DEFAULTS;
|
private $defaultOptions = [
|
||||||
|
'buffer' => null, // bool|\Closure - a boolean or a closure telling if the response should be buffered based on its headers
|
||||||
|
] + self::OPTIONS_DEFAULTS;
|
||||||
|
|
||||||
/** @var NativeClientState */
|
/** @var NativeClientState */
|
||||||
private $multi;
|
private $multi;
|
||||||
@ -48,8 +50,10 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
|
|||||||
*/
|
*/
|
||||||
public function __construct(array $defaultOptions = [], int $maxHostConnections = 6)
|
public function __construct(array $defaultOptions = [], int $maxHostConnections = 6)
|
||||||
{
|
{
|
||||||
|
$this->defaultOptions['buffer'] = \Closure::fromCallable([__CLASS__, 'shouldBuffer']);
|
||||||
|
|
||||||
if ($defaultOptions) {
|
if ($defaultOptions) {
|
||||||
[, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, self::OPTIONS_DEFAULTS);
|
[, $this->defaultOptions] = self::prepareRequest(null, null, $defaultOptions, $this->defaultOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->multi = new NativeClientState();
|
$this->multi = new NativeClientState();
|
||||||
|
@ -64,18 +64,18 @@ final class CurlResponse implements ResponseInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (null === $content = &$this->content) {
|
if (null === $content = &$this->content) {
|
||||||
$content = ($options['buffer'] ?? true) ? fopen('php://temp', 'w+') : null;
|
$content = true === $options['buffer'] ? fopen('php://temp', 'w+') : null;
|
||||||
} else {
|
} else {
|
||||||
// Move the pushed response to the activity list
|
// Move the pushed response to the activity list
|
||||||
if (ftell($content)) {
|
if (ftell($content)) {
|
||||||
rewind($content);
|
rewind($content);
|
||||||
$multi->handlesActivity[$id][] = stream_get_contents($content);
|
$multi->handlesActivity[$id][] = stream_get_contents($content);
|
||||||
}
|
}
|
||||||
$content = ($options['buffer'] ?? true) ? $content : null;
|
$content = true === $options['buffer'] ? $content : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_setopt($ch, CURLOPT_HEADERFUNCTION, static function ($ch, string $data) use (&$info, &$headers, $options, $multi, $id, &$location, $resolveRedirect, $logger): int {
|
curl_setopt($ch, CURLOPT_HEADERFUNCTION, static function ($ch, string $data) use (&$info, &$headers, $options, $multi, $id, &$location, $resolveRedirect, $logger, &$content): int {
|
||||||
return self::parseHeaderLine($ch, $data, $info, $headers, $options, $multi, $id, $location, $resolveRedirect, $logger);
|
return self::parseHeaderLine($ch, $data, $info, $headers, $options, $multi, $id, $location, $resolveRedirect, $logger, $content);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (null === $options) {
|
if (null === $options) {
|
||||||
@ -278,7 +278,7 @@ final class CurlResponse implements ResponseInterface
|
|||||||
/**
|
/**
|
||||||
* Parses header lines as curl yields them to us.
|
* Parses header lines as curl yields them to us.
|
||||||
*/
|
*/
|
||||||
private static function parseHeaderLine($ch, string $data, array &$info, array &$headers, ?array $options, CurlClientState $multi, int $id, ?string &$location, ?callable $resolveRedirect, ?LoggerInterface $logger): int
|
private static function parseHeaderLine($ch, string $data, array &$info, array &$headers, ?array $options, CurlClientState $multi, int $id, ?string &$location, ?callable $resolveRedirect, ?LoggerInterface $logger, &$content = null): int
|
||||||
{
|
{
|
||||||
if (!\in_array($waitFor = @curl_getinfo($ch, CURLINFO_PRIVATE), ['headers', 'destruct'], true)) {
|
if (!\in_array($waitFor = @curl_getinfo($ch, CURLINFO_PRIVATE), ['headers', 'destruct'], true)) {
|
||||||
return \strlen($data); // Ignore HTTP trailers
|
return \strlen($data); // Ignore HTTP trailers
|
||||||
@ -349,6 +349,10 @@ final class CurlResponse implements ResponseInterface
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($options['buffer'] instanceof \Closure && !$content && $options['buffer']($headers)) {
|
||||||
|
$content = fopen('php://temp', 'w+');
|
||||||
|
}
|
||||||
|
|
||||||
curl_setopt($ch, CURLOPT_PRIVATE, 'content');
|
curl_setopt($ch, CURLOPT_PRIVATE, 'content');
|
||||||
} elseif (null !== $info['redirect_url'] && $logger) {
|
} elseif (null !== $info['redirect_url'] && $logger) {
|
||||||
$logger->info(sprintf('Redirecting: "%s %s"', $info['http_code'], $info['redirect_url']));
|
$logger->info(sprintf('Redirecting: "%s %s"', $info['http_code'], $info['redirect_url']));
|
||||||
|
@ -104,7 +104,12 @@ class MockResponse implements ResponseInterface
|
|||||||
$response = new self([]);
|
$response = new self([]);
|
||||||
$response->requestOptions = $options;
|
$response->requestOptions = $options;
|
||||||
$response->id = ++self::$idSequence;
|
$response->id = ++self::$idSequence;
|
||||||
$response->content = ($options['buffer'] ?? true) ? fopen('php://temp', 'w+') : null;
|
|
||||||
|
if (($options['buffer'] ?? null) instanceof \Closure) {
|
||||||
|
$response->content = $options['buffer']($mock->getHeaders(false)) ? fopen('php://temp', 'w+') : null;
|
||||||
|
} else {
|
||||||
|
$response->content = true === ($options['buffer'] ?? true) ? fopen('php://temp', 'w+') : null;
|
||||||
|
}
|
||||||
$response->initializer = static function (self $response) {
|
$response->initializer = static function (self $response) {
|
||||||
if (null !== $response->info['error']) {
|
if (null !== $response->info['error']) {
|
||||||
throw new TransportException($response->info['error']);
|
throw new TransportException($response->info['error']);
|
||||||
|
@ -35,6 +35,7 @@ final class NativeResponse implements ResponseInterface
|
|||||||
private $inflate;
|
private $inflate;
|
||||||
private $multi;
|
private $multi;
|
||||||
private $debugBuffer;
|
private $debugBuffer;
|
||||||
|
private $shouldBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -50,7 +51,8 @@ final class NativeResponse implements ResponseInterface
|
|||||||
$this->info = &$info;
|
$this->info = &$info;
|
||||||
$this->resolveRedirect = $resolveRedirect;
|
$this->resolveRedirect = $resolveRedirect;
|
||||||
$this->onProgress = $onProgress;
|
$this->onProgress = $onProgress;
|
||||||
$this->content = $options['buffer'] ? fopen('php://temp', 'w+') : null;
|
$this->content = true === $options['buffer'] ? fopen('php://temp', 'w+') : null;
|
||||||
|
$this->shouldBuffer = $options['buffer'] instanceof \Closure ? $options['buffer'] : null;
|
||||||
|
|
||||||
// Temporary resources to dechunk/inflate the response stream
|
// Temporary resources to dechunk/inflate the response stream
|
||||||
$this->buffer = fopen('php://temp', 'w+');
|
$this->buffer = fopen('php://temp', 'w+');
|
||||||
@ -92,6 +94,8 @@ final class NativeResponse implements ResponseInterface
|
|||||||
|
|
||||||
public function __destruct()
|
public function __destruct()
|
||||||
{
|
{
|
||||||
|
$this->shouldBuffer = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->doDestruct();
|
$this->doDestruct();
|
||||||
} finally {
|
} finally {
|
||||||
@ -152,6 +156,10 @@ final class NativeResponse implements ResponseInterface
|
|||||||
stream_set_blocking($h, false);
|
stream_set_blocking($h, false);
|
||||||
$this->context = $this->resolveRedirect = null;
|
$this->context = $this->resolveRedirect = null;
|
||||||
|
|
||||||
|
if (null !== $this->shouldBuffer && null === $this->content && ($this->shouldBuffer)($this->headers)) {
|
||||||
|
$this->content = fopen('php://temp', 'w+');
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($context['ssl']['peer_certificate_chain'])) {
|
if (isset($context['ssl']['peer_certificate_chain'])) {
|
||||||
$this->info['peer_certificate_chain'] = $context['ssl']['peer_certificate_chain'];
|
$this->info['peer_certificate_chain'] = $context['ssl']['peer_certificate_chain'];
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ trait ResponseTrait
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (null === $content) {
|
if (null === $content) {
|
||||||
throw new TransportException('Cannot get the content of the response twice: the request was issued with option "buffer" set to false.');
|
throw new TransportException('Cannot get the content of the response twice: buffering is disabled.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $content;
|
return $content;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\HttpClient\Tests;
|
namespace Symfony\Component\HttpClient\Tests;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpClient\Exception\TransportException;
|
||||||
use Symfony\Contracts\HttpClient\Test\HttpClientTestCase as BaseHttpClientTestCase;
|
use Symfony\Contracts\HttpClient\Test\HttpClientTestCase as BaseHttpClientTestCase;
|
||||||
|
|
||||||
abstract class HttpClientTestCase extends BaseHttpClientTestCase
|
abstract class HttpClientTestCase extends BaseHttpClientTestCase
|
||||||
@ -37,4 +38,21 @@ abstract class HttpClientTestCase extends BaseHttpClientTestCase
|
|||||||
$this->assertSame('', fread($stream, 1));
|
$this->assertSame('', fread($stream, 1));
|
||||||
$this->assertTrue(feof($stream));
|
$this->assertTrue(feof($stream));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testConditionalBuffering()
|
||||||
|
{
|
||||||
|
$client = $this->getHttpClient(__FUNCTION__);
|
||||||
|
$response = $client->request('GET', 'http://localhost:8057');
|
||||||
|
$firstContent = $response->getContent();
|
||||||
|
$secondContent = $response->getContent();
|
||||||
|
|
||||||
|
$this->assertSame($firstContent, $secondContent);
|
||||||
|
|
||||||
|
$response = $client->request('GET', 'http://localhost:8057', ['buffer' => function () { return false; }]);
|
||||||
|
$response->getContent();
|
||||||
|
|
||||||
|
$this->expectException(TransportException::class);
|
||||||
|
$this->expectExceptionMessage('Cannot get the content of the response twice: buffering is disabled.');
|
||||||
|
$response->getContent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user