2019-01-30 09:26:19 +00:00
< ? php
/*
* This file is part of the Symfony package .
*
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
*/
namespace Symfony\Component\HttpClient ;
use Psr\Log\LoggerInterface ;
use Symfony\Component\HttpClient\Response\MockResponse ;
use Symfony\Component\HttpClient\Response\ResponseStream ;
use Symfony\Component\HttpFoundation\Request ;
use Symfony\Component\HttpKernel\HttpCache\HttpCache ;
use Symfony\Component\HttpKernel\HttpCache\StoreInterface ;
use Symfony\Component\HttpKernel\HttpClientKernel ;
use Symfony\Contracts\HttpClient\HttpClientInterface ;
use Symfony\Contracts\HttpClient\ResponseInterface ;
use Symfony\Contracts\HttpClient\ResponseStreamInterface ;
/**
* Adds caching on top of an HTTP client .
*
* The implementation buffers responses in memory and doesn ' t stream directly from the network .
* You can disable / enable this layer by setting option " no_cache " under " extra " to true / false .
* By default , caching is enabled unless the " buffer " option is set to false .
*
* @ author Nicolas Grekas < p @ tchwork . com >
*/
class CachingHttpClient implements HttpClientInterface
{
use HttpClientTrait ;
private $client ;
private $cache ;
private $defaultOptions = self :: OPTIONS_DEFAULTS ;
public function __construct ( HttpClientInterface $client , StoreInterface $store , array $defaultOptions = [], LoggerInterface $logger = null )
{
if ( ! class_exists ( HttpClientKernel :: class )) {
2019-03-23 15:10:57 +00:00
throw new \LogicException ( sprintf ( 'Using "%s" requires that the HttpKernel component version 4.3 or higher is installed, try running "composer require symfony/http-kernel:^4.3".' , __CLASS__ ));
2019-01-30 09:26:19 +00:00
}
$this -> client = $client ;
2019-04-06 10:23:31 +01:00
$kernel = new HttpClientKernel ( $client );
2019-01-30 09:26:19 +00:00
$this -> cache = new HttpCache ( $kernel , $store , null , $defaultOptions );
unset ( $defaultOptions [ 'debug' ]);
unset ( $defaultOptions [ 'default_ttl' ]);
unset ( $defaultOptions [ 'private_headers' ]);
unset ( $defaultOptions [ 'allow_reload' ]);
unset ( $defaultOptions [ 'allow_revalidate' ]);
unset ( $defaultOptions [ 'stale_while_revalidate' ]);
unset ( $defaultOptions [ 'stale_if_error' ]);
if ( $defaultOptions ) {
[, $this -> defaultOptions ] = self :: prepareRequest ( null , null , $defaultOptions , $this -> defaultOptions );
}
}
/**
* { @ inheritdoc }
*/
public function request ( string $method , string $url , array $options = []) : ResponseInterface
{
[ $url , $options ] = $this -> prepareRequest ( $method , $url , $options , $this -> defaultOptions , true );
$url = implode ( '' , $url );
$options [ 'extra' ][ 'no_cache' ] = $options [ 'extra' ][ 'no_cache' ] ? ? ! $options [ 'buffer' ];
if ( $options [ 'extra' ][ 'no_cache' ] || ! empty ( $options [ 'body' ]) || ! \in_array ( $method , [ 'GET' , 'HEAD' , 'OPTIONS' ])) {
return $this -> client -> request ( $method , $url , $options );
}
$request = Request :: create ( $url , $method );
$request -> attributes -> set ( 'http_client_options' , $options );
foreach ( $options [ 'headers' ] as $name => $values ) {
if ( 'cookie' !== $name ) {
$request -> headers -> set ( $name , $values );
continue ;
}
foreach ( $values as $cookies ) {
foreach ( explode ( '; ' , $cookies ) as $cookie ) {
if ( '' !== $cookie ) {
$cookie = explode ( '=' , $cookie , 2 );
$request -> cookies -> set ( $cookie [ 0 ], $cookie [ 1 ] ? ? null );
}
}
}
}
$response = $this -> cache -> handle ( $request );
$response = new MockResponse ( $response -> getContent (), [
'http_code' => $response -> getStatusCode (),
2019-04-02 11:03:40 +01:00
'response_headers' => $response -> headers -> allPreserveCase (),
2019-01-30 09:26:19 +00:00
]);
return MockResponse :: fromRequest ( $method , $url , $options , $response );
}
/**
* { @ inheritdoc }
*/
public function stream ( $responses , float $timeout = null ) : ResponseStreamInterface
{
if ( $responses instanceof ResponseInterface ) {
$responses = [ $responses ];
} elseif ( ! \is_iterable ( $responses )) {
throw new \TypeError ( sprintf ( '%s() expects parameter 1 to be an iterable of ResponseInterface objects, %s given.' , __METHOD__ , \is_object ( $responses ) ? \get_class ( $responses ) : \gettype ( $responses )));
}
$mockResponses = [];
$clientResponses = [];
foreach ( $responses as $response ) {
if ( $response instanceof MockResponse ) {
$mockResponses [] = $response ;
} else {
$clientResponses [] = $response ;
}
}
if ( ! $mockResponses ) {
return $this -> client -> stream ( $clientResponses , $timeout );
}
if ( ! $clientResponses ) {
return new ResponseStream ( MockResponse :: stream ( $mockResponses , $timeout ));
}
return new ResponseStream (( function () use ( $mockResponses , $clientResponses , $timeout ) {
yield from MockResponse :: stream ( $mockResponses , $timeout );
yield $this -> client -> stream ( $clientResponses , $timeout );
})());
}
}