diff --git a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php index 5e1b0276fc..fa499557d4 100644 --- a/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ProxyAdapter.php @@ -256,7 +256,7 @@ class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterfa } } - private function getId($key) + private function getId($key): string { CacheItem::validateKey($key); diff --git a/src/Symfony/Component/Cache/CacheItem.php b/src/Symfony/Component/Cache/CacheItem.php index f419464735..5d089f0bda 100644 --- a/src/Symfony/Component/Cache/CacheItem.php +++ b/src/Symfony/Component/Cache/CacheItem.php @@ -173,7 +173,7 @@ final class CacheItem implements ItemInterface * * @internal */ - public static function log(LoggerInterface $logger = null, $message, $context = []) + public static function log(?LoggerInterface $logger, string $message, array $context = []) { if ($logger) { $logger->warning($message, $context); diff --git a/src/Symfony/Component/Cache/LockRegistry.php b/src/Symfony/Component/Cache/LockRegistry.php index 9993e8978e..cc4570407d 100644 --- a/src/Symfony/Component/Cache/LockRegistry.php +++ b/src/Symfony/Component/Cache/LockRegistry.php @@ -130,6 +130,8 @@ final class LockRegistry $logger && $logger->info('Item "{key}" not found while lock was released, now retrying', ['key' => $item->getKey()]); } } + + return null; } private static function open(int $key) diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AbstractRedisAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/AbstractRedisAdapterTest.php index 9b77e926d4..6a686a9481 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/AbstractRedisAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/AbstractRedisAdapterTest.php @@ -24,7 +24,7 @@ abstract class AbstractRedisAdapterTest extends AdapterTestCase 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); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php index a4673f12a2..10188607d1 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ApcuAdapterTest.php @@ -23,7 +23,7 @@ class ApcuAdapterTest extends AdapterTestCase '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)) { $this->markTestSkipped('APCu extension is required.'); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php index 81f8e14702..ff37479cc1 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php @@ -25,7 +25,7 @@ class ArrayAdapterTest extends AdapterTestCase '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); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php index effc0e84f7..ffd598bc04 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ChainAdapterTest.php @@ -11,7 +11,6 @@ namespace Symfony\Component\Cache\Tests\Adapter; -use PHPUnit\Framework\MockObject\MockObject; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; @@ -26,7 +25,7 @@ use Symfony\Component\Cache\Tests\Fixtures\ExternalAdapter; */ 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) { return new ChainAdapter([new FilesystemAdapter('', $defaultLifetime)], $defaultLifetime); @@ -70,14 +69,9 @@ class ChainAdapterTest extends AdapterTestCase $this->assertFalse($cache->prune()); } - /** - * @return MockObject|PruneableCacheInterface - */ - private function getPruneableMock(): object + private function getPruneableMock(): AdapterInterface { - $pruneable = $this - ->getMockBuilder(PruneableCacheInterface::class) - ->getMock(); + $pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]); $pruneable ->expects($this->atLeastOnce()) @@ -87,14 +81,9 @@ class ChainAdapterTest extends AdapterTestCase return $pruneable; } - /** - * @return MockObject|PruneableCacheInterface - */ - private function getFailingPruneableMock(): object + private function getFailingPruneableMock(): AdapterInterface { - $pruneable = $this - ->getMockBuilder(PruneableCacheInterface::class) - ->getMock(); + $pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]); $pruneable ->expects($this->atLeastOnce()) @@ -104,17 +93,8 @@ class ChainAdapterTest extends AdapterTestCase return $pruneable; } - /** - * @return MockObject|AdapterInterface - */ - private function getNonPruneableMock(): object + private function getNonPruneableMock(): AdapterInterface { - return $this - ->getMockBuilder(AdapterInterface::class) - ->getMock(); + return $this->createMock(AdapterInterface::class); } } - -interface PruneableCacheInterface extends PruneableInterface, AdapterInterface -{ -} diff --git a/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php index dda3019c69..310aa4387a 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php @@ -27,7 +27,7 @@ class DoctrineAdapterTest extends AdapterTestCase '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); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/FilesystemAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemAdapterTest.php index 5dbedcd444..54264eeac5 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/FilesystemAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemAdapterTest.php @@ -19,7 +19,7 @@ use Symfony\Component\Cache\Adapter\FilesystemAdapter; */ class FilesystemAdapterTest extends AdapterTestCase { - public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface + public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface { return new FilesystemAdapter('', $defaultLifetime); } @@ -29,7 +29,7 @@ class FilesystemAdapterTest extends AdapterTestCase self::rmdir(sys_get_temp_dir().'/symfony-cache'); } - public static function rmdir($dir) + public static function rmdir(string $dir) { if (!file_exists($dir)) { return; @@ -51,7 +51,7 @@ class FilesystemAdapterTest extends AdapterTestCase rmdir($dir); } - protected function isPruned(CacheItemPoolInterface $cache, $name) + protected function isPruned(CacheItemPoolInterface $cache, string $name): bool { $getFileMethod = (new \ReflectionObject($cache))->getMethod('getFile'); $getFileMethod->setAccessible(true); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTagAwareAdapterTest.php index 76c9a5817c..a9f3407eae 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTagAwareAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTagAwareAdapterTest.php @@ -22,7 +22,7 @@ class FilesystemTagAwareAdapterTest extends FilesystemAdapterTest { use TagAwareTestTrait; - public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface + public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface { return new FilesystemTagAwareAdapter('', $defaultLifetime); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php index 724aa9451c..cc4d160aef 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/MaxIdLengthAdapterTest.php @@ -80,7 +80,7 @@ abstract class MaxIdLengthAdapter extends AbstractAdapter { protected $maxIdLength = 50; - public function __construct($ns) + public function __construct(string $ns) { parent::__construct($ns); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php index 8238eaea05..9a60642e80 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php @@ -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; @@ -73,7 +73,7 @@ class MemcachedAdapterTest extends AdapterTestCase MemcachedAdapter::createConnection([], [$name => $value]); } - public function provideBadOptions() + public function provideBadOptions(): array { return [ ['foo', 'bar'], @@ -109,7 +109,7 @@ class MemcachedAdapterTest extends AdapterTestCase /** * @dataProvider provideServersSetting */ - public function testServersSetting($dsn, $host, $port) + public function testServersSetting(string $dsn, string $host, int $port) { $client1 = MemcachedAdapter::createConnection($dsn); $client2 = MemcachedAdapter::createConnection([$dsn]); @@ -125,7 +125,7 @@ class MemcachedAdapterTest extends AdapterTestCase $this->assertSame([$expect], array_map($f, $client3->getServerList())); } - public function provideServersSetting() + public function provideServersSetting(): iterable { yield [ 'memcached://127.0.0.1/50', @@ -166,7 +166,7 @@ class MemcachedAdapterTest extends AdapterTestCase /** * @dataProvider provideDsnWithOptions */ - public function testDsnWithOptions($dsn, array $options, array $expectedOptions) + public function testDsnWithOptions(string $dsn, array $options, array $expectedOptions) { $client = MemcachedAdapter::createConnection($dsn, $options); @@ -175,7 +175,7 @@ class MemcachedAdapterTest extends AdapterTestCase } } - public function provideDsnWithOptions() + public function provideDsnWithOptions(): iterable { if (!class_exists('\Memcached')) { self::markTestSkipped('Extension memcached required.'); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php index 460ca0bc82..a4edc7a608 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/NamespacedProxyAdapterTest.php @@ -21,7 +21,7 @@ use Symfony\Component\Cache\Adapter\ProxyAdapter; */ 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) { return new ProxyAdapter(new FilesystemAdapter(), 'foo', $defaultLifetime); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PdoAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PdoAdapterTest.php index 4a09e85179..ec9e00d3c9 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PdoAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PdoAdapterTest.php @@ -41,7 +41,7 @@ class PdoAdapterTest extends AdapterTestCase @unlink(self::$dbFile); } - public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface + public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface { return new PdoAdapter('sqlite:'.self::$dbFile, 'ns', $defaultLifetime); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php index d4fa558411..6ad568d6cc 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PdoDbalAdapterTest.php @@ -41,7 +41,7 @@ class PdoDbalAdapterTest extends AdapterTestCase @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); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php index ed5023b078..4a5aa82d59 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php @@ -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) { return new PhpArrayAdapter(self::$file, new FilesystemAdapter()); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterWithFallbackTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterWithFallbackTest.php index 32f52bae9b..694b63d89f 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterWithFallbackTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterWithFallbackTest.php @@ -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)); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PhpFilesAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PhpFilesAdapterTest.php index 3e63617447..d204ef8d29 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PhpFilesAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PhpFilesAdapterTest.php @@ -33,7 +33,7 @@ class PhpFilesAdapterTest extends AdapterTestCase 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->setAccessible(true); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareAdapterTest.php index 390a73da5f..eedd3903a8 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareAdapterTest.php @@ -25,7 +25,7 @@ class PredisTagAwareAdapterTest extends PredisAdapterTest $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); $adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareClusterAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareClusterAdapterTest.php index 8339367593..77d51a9033 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareClusterAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareClusterAdapterTest.php @@ -25,7 +25,7 @@ class PredisTagAwareClusterAdapterTest extends PredisClusterAdapterTest $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); $adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareRedisClusterAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareRedisClusterAdapterTest.php index 813d524715..8357fffe39 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareRedisClusterAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/PredisTagAwareRedisClusterAdapterTest.php @@ -25,7 +25,7 @@ class PredisTagAwareRedisClusterAdapterTest extends PredisRedisClusterAdapterTes $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); $adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php index 6436428e74..0fbe94aac8 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/ProxyAdapterTest.php @@ -29,7 +29,7 @@ class ProxyAdapterTest extends AdapterTestCase '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) { return new ProxyAdapter(new FilesystemAdapter(), '', $defaultLifetime); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/Psr16AdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/Psr16AdapterTest.php index 7d5f242398..bdd5d04c56 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/Psr16AdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/Psr16AdapterTest.php @@ -27,7 +27,7 @@ class Psr16AdapterTest extends AdapterTestCase '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); } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php index 216d1373db..c785137241 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php @@ -24,7 +24,7 @@ class RedisAdapterTest extends AbstractRedisAdapterTest 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); $this->assertInstanceOf(RedisProxy::class, self::$redis); @@ -35,7 +35,7 @@ class RedisAdapterTest extends AbstractRedisAdapterTest /** * @dataProvider provideValidSchemes */ - public function testCreateConnection($dsnScheme) + public function testCreateConnection(string $dsnScheme) { $redis = RedisAdapter::createConnection($dsnScheme.':?host[h1]&host[h2]&host[/foo:]'); $this->assertInstanceOf(\RedisArray::class, $redis); @@ -65,14 +65,14 @@ class RedisAdapterTest extends AbstractRedisAdapterTest /** * @dataProvider provideFailedCreateConnection */ - public function testFailedCreateConnection($dsn) + public function testFailedCreateConnection(string $dsn) { $this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException'); $this->expectExceptionMessage('Redis connection failed'); RedisAdapter::createConnection($dsn); } - public function provideFailedCreateConnection() + public function provideFailedCreateConnection(): array { return [ ['redis://localhost:1234'], @@ -84,14 +84,14 @@ class RedisAdapterTest extends AbstractRedisAdapterTest /** * @dataProvider provideInvalidCreateConnection */ - public function testInvalidCreateConnection($dsn) + public function testInvalidCreateConnection(string $dsn) { $this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException'); $this->expectExceptionMessage('Invalid Redis DSN'); RedisAdapter::createConnection($dsn); } - public function provideValidSchemes() + public function provideValidSchemes(): array { return [ ['redis'], @@ -99,7 +99,7 @@ class RedisAdapterTest extends AbstractRedisAdapterTest ]; } - public function provideInvalidCreateConnection() + public function provideInvalidCreateConnection(): array { return [ ['foo://localhost'], diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisClusterAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisClusterAdapterTest.php index 821259b78f..d1dfe34fe8 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/RedisClusterAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisClusterAdapterTest.php @@ -30,7 +30,7 @@ class RedisClusterAdapterTest extends AbstractRedisAdapterTest 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); $adapter = new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); @@ -41,14 +41,14 @@ class RedisClusterAdapterTest extends AbstractRedisAdapterTest /** * @dataProvider provideFailedCreateConnection */ - public function testFailedCreateConnection($dsn) + public function testFailedCreateConnection(string $dsn) { $this->expectException('Symfony\Component\Cache\Exception\InvalidArgumentException'); $this->expectExceptionMessage('Redis connection failed'); RedisAdapter::createConnection($dsn); } - public function provideFailedCreateConnection() + public function provideFailedCreateConnection(): array { return [ ['redis://localhost:1234?redis_cluster=1'], diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareAdapterTest.php index b88af7b733..5f8eef7c56 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareAdapterTest.php @@ -26,7 +26,7 @@ class RedisTagAwareAdapterTest extends RedisAdapterTest $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); $adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareArrayAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareArrayAdapterTest.php index 5fcf781cd9..8f9f87c8fe 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareArrayAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareArrayAdapterTest.php @@ -25,7 +25,7 @@ class RedisTagAwareArrayAdapterTest extends RedisArrayAdapterTest $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); $adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareClusterAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareClusterAdapterTest.php index 3deb0c264a..d179abde1e 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareClusterAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/RedisTagAwareClusterAdapterTest.php @@ -26,7 +26,7 @@ class RedisTagAwareClusterAdapterTest extends RedisClusterAdapterTest $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); $adapter = new RedisTagAwareAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime); diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php index 00d2927801..a945267f4d 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAdapterTest.php @@ -16,6 +16,7 @@ use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\Cache\Adapter\TagAwareAdapter; +use Symfony\Component\Cache\PruneableInterface; use Symfony\Component\Cache\Tests\Traits\TagAwareTestTrait; /** @@ -66,14 +67,9 @@ class TagAwareAdapterTest extends AdapterTestCase $this->assertFalse($cache->prune()); } - /** - * @return MockObject|PruneableCacheInterface - */ - private function getPruneableMock(): object + private function getPruneableMock(): AdapterInterface { - $pruneable = $this - ->getMockBuilder(PruneableCacheInterface::class) - ->getMock(); + $pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]); $pruneable ->expects($this->atLeastOnce()) @@ -83,14 +79,9 @@ class TagAwareAdapterTest extends AdapterTestCase return $pruneable; } - /** - * @return MockObject|PruneableCacheInterface - */ - private function getFailingPruneableMock(): object + private function getFailingPruneableMock(): AdapterInterface { - $pruneable = $this - ->getMockBuilder(PruneableCacheInterface::class) - ->getMock(); + $pruneable = $this->createMock([PruneableInterface::class, AdapterInterface::class]); $pruneable ->expects($this->atLeastOnce()) @@ -100,13 +91,8 @@ class TagAwareAdapterTest extends AdapterTestCase return $pruneable; } - /** - * @return MockObject|AdapterInterface - */ - private function getNonPruneableMock(): object + private function getNonPruneableMock(): AdapterInterface { - return $this - ->getMockBuilder(AdapterInterface::class) - ->getMock(); + return $this->createMock(AdapterInterface::class); } } diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAndProxyAdapterIntegrationTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAndProxyAdapterIntegrationTest.php index b11c1f2870..e53b407028 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAndProxyAdapterIntegrationTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TagAwareAndProxyAdapterIntegrationTest.php @@ -26,7 +26,7 @@ class TagAwareAndProxyAdapterIntegrationTest extends TestCase $this->assertSame('bar', $cache->getItem('foo')->get()); } - public function dataProvider() + public function dataProvider(): array { return [ [new ArrayAdapter()], diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php index 3d531f5caf..3a573dc431 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php @@ -24,7 +24,7 @@ class TraceableAdapterTest extends AdapterTestCase 'testPrune' => 'TraceableAdapter just proxies', ]; - public function createCachePool($defaultLifetime = 0): CacheItemPoolInterface + public function createCachePool(int $defaultLifetime = 0): CacheItemPoolInterface { return new TraceableAdapter(new FilesystemAdapter('', $defaultLifetime)); } diff --git a/src/Symfony/Component/Cache/Tests/CacheItemTest.php b/src/Symfony/Component/Cache/Tests/CacheItemTest.php index b36b6343a8..3b756f571f 100644 --- a/src/Symfony/Component/Cache/Tests/CacheItemTest.php +++ b/src/Symfony/Component/Cache/Tests/CacheItemTest.php @@ -31,7 +31,7 @@ class CacheItemTest extends TestCase CacheItem::validateKey($key); } - public function provideInvalidKey() + public function provideInvalidKey(): array { return [ [''], diff --git a/src/Symfony/Component/Cache/Tests/Psr16CacheTest.php b/src/Symfony/Component/Cache/Tests/Psr16CacheTest.php index f3a027cbfb..094be128bb 100644 --- a/src/Symfony/Component/Cache/Tests/Psr16CacheTest.php +++ b/src/Symfony/Component/Cache/Tests/Psr16CacheTest.php @@ -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)); } @@ -146,7 +146,7 @@ class Psr16CacheTest extends SimpleCacheTest $cache->clear(); } - protected function isPruned($cache, $name) + protected function isPruned(CacheInterface $cache, string $name): bool { if (Psr16Cache::class !== \get_class($cache)) { $this->fail('Test classes for pruneable caches must implement `isPruned($cache, $name)` method.'); diff --git a/src/Symfony/Component/Cache/Tests/Traits/PdoPruneableTrait.php b/src/Symfony/Component/Cache/Tests/Traits/PdoPruneableTrait.php index 3b1e1128ba..d9c3b91568 100644 --- a/src/Symfony/Component/Cache/Tests/Traits/PdoPruneableTrait.php +++ b/src/Symfony/Component/Cache/Tests/Traits/PdoPruneableTrait.php @@ -13,7 +13,7 @@ namespace Symfony\Component\Cache\Tests\Traits; trait PdoPruneableTrait { - protected function isPruned($cache, $name) + protected function isPruned($cache, string $name): bool { $o = new \ReflectionObject($cache); diff --git a/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php b/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php index c27213e03b..c981530e9e 100644 --- a/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php +++ b/src/Symfony/Component/Cache/Tests/Traits/TagAwareTestTrait.php @@ -16,7 +16,7 @@ use Symfony\Component\Cache\CacheItem; /** * 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 { diff --git a/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php b/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php index 8f026f750d..5385c073cc 100644 --- a/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php +++ b/src/Symfony/Component/Cache/Traits/AbstractAdapterTrait.php @@ -313,7 +313,7 @@ trait AbstractAdapterTrait } } - private function generateItems(iterable $items, array &$keys) + private function generateItems(iterable $items, array &$keys): iterable { $f = $this->createCacheItem; diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index bdbed9d09f..306d6410d6 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -423,7 +423,7 @@ trait RedisTrait return $failed; } - private function pipeline(\Closure $generator) + private function pipeline(\Closure $generator): \Generator { $ids = []; diff --git a/src/Symfony/Component/HttpClient/CHANGELOG.md b/src/Symfony/Component/HttpClient/CHANGELOG.md index 9fcfa7ee9a..973bb6107b 100644 --- a/src/Symfony/Component/HttpClient/CHANGELOG.md +++ b/src/Symfony/Component/HttpClient/CHANGELOG.md @@ -11,6 +11,7 @@ CHANGELOG * added `$response->toStream()` to cast responses to regular PHP streams * made `Psr18Client` implement relevant PSR-17 factories and have streaming responses * added `TraceableHttpClient`, `HttpClientDataCollector` and `HttpClientPass` to integrate with the web profiler + * allow enabling buffering conditionally with a Closure 4.3.0 ----- diff --git a/src/Symfony/Component/HttpClient/CachingHttpClient.php b/src/Symfony/Component/HttpClient/CachingHttpClient.php index 65426ef647..3d7a1232f6 100644 --- a/src/Symfony/Component/HttpClient/CachingHttpClient.php +++ b/src/Symfony/Component/HttpClient/CachingHttpClient.php @@ -68,9 +68,8 @@ class CachingHttpClient implements HttpClientInterface { [$url, $options] = $this->prepareRequest($method, $url, $options, $this->defaultOptions, true); $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); } diff --git a/src/Symfony/Component/HttpClient/CurlHttpClient.php b/src/Symfony/Component/HttpClient/CurlHttpClient.php index 96b2ba8bb4..f77ba80828 100644 --- a/src/Symfony/Component/HttpClient/CurlHttpClient.php +++ b/src/Symfony/Component/HttpClient/CurlHttpClient.php @@ -37,7 +37,9 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface use HttpClientTrait; 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 // 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.'); } + $this->defaultOptions['buffer'] = \Closure::fromCallable([__CLASS__, 'shouldBuffer']); + 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(); diff --git a/src/Symfony/Component/HttpClient/HttpClientTrait.php b/src/Symfony/Component/HttpClient/HttpClientTrait.php index c5c1cdb25f..b42d2369c3 100644 --- a/src/Symfony/Component/HttpClient/HttpClientTrait.php +++ b/src/Symfony/Component/HttpClient/HttpClientTrait.php @@ -503,4 +503,15 @@ trait HttpClientTrait 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); + } } diff --git a/src/Symfony/Component/HttpClient/NativeHttpClient.php b/src/Symfony/Component/HttpClient/NativeHttpClient.php index 85aafdb589..6c3245ba08 100644 --- a/src/Symfony/Component/HttpClient/NativeHttpClient.php +++ b/src/Symfony/Component/HttpClient/NativeHttpClient.php @@ -35,7 +35,9 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac use HttpClientTrait; 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 */ private $multi; @@ -48,8 +50,10 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac */ public function __construct(array $defaultOptions = [], int $maxHostConnections = 6) { + $this->defaultOptions['buffer'] = \Closure::fromCallable([__CLASS__, 'shouldBuffer']); + 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(); diff --git a/src/Symfony/Component/HttpClient/Response/CurlResponse.php b/src/Symfony/Component/HttpClient/Response/CurlResponse.php index 0b044630f6..a6ebfec61f 100644 --- a/src/Symfony/Component/HttpClient/Response/CurlResponse.php +++ b/src/Symfony/Component/HttpClient/Response/CurlResponse.php @@ -64,18 +64,18 @@ final class CurlResponse implements ResponseInterface } if (null === $content = &$this->content) { - $content = ($options['buffer'] ?? true) ? fopen('php://temp', 'w+') : null; + $content = true === $options['buffer'] ? fopen('php://temp', 'w+') : null; } else { // Move the pushed response to the activity list if (ftell($content)) { rewind($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 { - return self::parseHeaderLine($ch, $data, $info, $headers, $options, $multi, $id, $location, $resolveRedirect, $logger); + 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, $content); }); if (null === $options) { @@ -278,7 +278,7 @@ final class CurlResponse implements ResponseInterface /** * 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)) { return \strlen($data); // Ignore HTTP trailers @@ -349,6 +349,10 @@ final class CurlResponse implements ResponseInterface return 0; } + if ($options['buffer'] instanceof \Closure && !$content && $options['buffer']($headers)) { + $content = fopen('php://temp', 'w+'); + } + curl_setopt($ch, CURLOPT_PRIVATE, 'content'); } elseif (null !== $info['redirect_url'] && $logger) { $logger->info(sprintf('Redirecting: "%s %s"', $info['http_code'], $info['redirect_url'])); diff --git a/src/Symfony/Component/HttpClient/Response/MockResponse.php b/src/Symfony/Component/HttpClient/Response/MockResponse.php index fa8abebea4..2e9cbc555b 100644 --- a/src/Symfony/Component/HttpClient/Response/MockResponse.php +++ b/src/Symfony/Component/HttpClient/Response/MockResponse.php @@ -104,7 +104,12 @@ class MockResponse implements ResponseInterface $response = new self([]); $response->requestOptions = $options; $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) { if (null !== $response->info['error']) { throw new TransportException($response->info['error']); diff --git a/src/Symfony/Component/HttpClient/Response/NativeResponse.php b/src/Symfony/Component/HttpClient/Response/NativeResponse.php index 7aa2d8022d..e0fb09327b 100644 --- a/src/Symfony/Component/HttpClient/Response/NativeResponse.php +++ b/src/Symfony/Component/HttpClient/Response/NativeResponse.php @@ -35,6 +35,7 @@ final class NativeResponse implements ResponseInterface private $inflate; private $multi; private $debugBuffer; + private $shouldBuffer; /** * @internal @@ -50,7 +51,8 @@ final class NativeResponse implements ResponseInterface $this->info = &$info; $this->resolveRedirect = $resolveRedirect; $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 $this->buffer = fopen('php://temp', 'w+'); @@ -92,6 +94,8 @@ final class NativeResponse implements ResponseInterface public function __destruct() { + $this->shouldBuffer = null; + try { $this->doDestruct(); } finally { @@ -152,6 +156,10 @@ final class NativeResponse implements ResponseInterface stream_set_blocking($h, false); $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'])) { $this->info['peer_certificate_chain'] = $context['ssl']['peer_certificate_chain']; } diff --git a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php index 4be2706003..c51b3d3c05 100644 --- a/src/Symfony/Component/HttpClient/Response/ResponseTrait.php +++ b/src/Symfony/Component/HttpClient/Response/ResponseTrait.php @@ -117,7 +117,7 @@ trait ResponseTrait } 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; diff --git a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php index 1e7fe180a3..fbe68d8c80 100644 --- a/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php +++ b/src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php @@ -11,6 +11,7 @@ namespace Symfony\Component\HttpClient\Tests; +use Symfony\Component\HttpClient\Exception\TransportException; use Symfony\Contracts\HttpClient\Test\HttpClientTestCase as BaseHttpClientTestCase; abstract class HttpClientTestCase extends BaseHttpClientTestCase @@ -37,4 +38,21 @@ abstract class HttpClientTestCase extends BaseHttpClientTestCase $this->assertSame('', fread($stream, 1)); $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(); + } }