MockHttpClient.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpClient;
  11. use Symfony\Component\HttpClient\Exception\TransportException;
  12. use Symfony\Component\HttpClient\Response\MockResponse;
  13. use Symfony\Component\HttpClient\Response\ResponseStream;
  14. use Symfony\Contracts\HttpClient\HttpClientInterface;
  15. use Symfony\Contracts\HttpClient\ResponseInterface;
  16. use Symfony\Contracts\HttpClient\ResponseStreamInterface;
  17. use Symfony\Contracts\Service\ResetInterface;
  18. /**
  19. * A test-friendly HttpClient that doesn't make actual HTTP requests.
  20. *
  21. * @author Nicolas Grekas <p@tchwork.com>
  22. */
  23. class MockHttpClient implements HttpClientInterface, ResetInterface
  24. {
  25. use HttpClientTrait;
  26. /**
  27. * @var \Symfony\Contracts\HttpClient\ResponseInterface|\Closure|mixed[]|null
  28. */
  29. private $responseFactory;
  30. /**
  31. * @var int
  32. */
  33. private $requestsCount = 0;
  34. /**
  35. * @var mixed[]
  36. */
  37. private $defaultOptions = [];
  38. /**
  39. * @param callable|mixed[]|\Symfony\Contracts\HttpClient\ResponseInterface $responseFactory
  40. * @param string|null $baseUri
  41. */
  42. public function __construct($responseFactory = null, $baseUri = 'https://example.com')
  43. {
  44. $this->setResponseFactory($responseFactory);
  45. $this->defaultOptions['base_uri'] = $baseUri;
  46. }
  47. /**
  48. * @param callable|callable[]|ResponseInterface|ResponseInterface[]|iterable|null $responseFactory
  49. */
  50. public function setResponseFactory($responseFactory)
  51. {
  52. if ($responseFactory instanceof ResponseInterface) {
  53. $responseFactory = [$responseFactory];
  54. }
  55. if (!$responseFactory instanceof \Iterator && null !== $responseFactory && !\is_callable($responseFactory)) {
  56. $responseFactory = (static function () use ($responseFactory) {
  57. yield from $responseFactory;
  58. })();
  59. }
  60. $this->responseFactory = !\is_callable($responseFactory) ? $responseFactory : \Closure::fromCallable($responseFactory);
  61. }
  62. /**
  63. * @param string $method
  64. * @param string $url
  65. * @param mixed[] $options
  66. */
  67. public function request($method, $url, $options = [])
  68. {
  69. [$url, $options] = $this->prepareRequest($method, $url, $options, $this->defaultOptions, true);
  70. $url = implode('', $url);
  71. if (null === $this->responseFactory) {
  72. $response = new MockResponse();
  73. } elseif (\is_callable($this->responseFactory)) {
  74. $response = ($this->responseFactory)($method, $url, $options);
  75. } elseif (!$this->responseFactory->valid()) {
  76. throw new TransportException($this->requestsCount ? 'No more response left in the response factory iterator passed to MockHttpClient: the number of requests exceeds the number of responses.' : 'The response factory iterator passed to MockHttpClient is empty.');
  77. } else {
  78. $responseFactory = $this->responseFactory->current();
  79. $response = \is_callable($responseFactory) ? $responseFactory($method, $url, $options) : $responseFactory;
  80. $this->responseFactory->next();
  81. }
  82. ++$this->requestsCount;
  83. if (!$response instanceof ResponseInterface) {
  84. throw new TransportException(sprintf('The response factory passed to MockHttpClient must return/yield an instance of ResponseInterface, "%s" given.', get_debug_type($response)));
  85. }
  86. return MockResponse::fromRequest($method, $url, $options, $response);
  87. }
  88. /**
  89. * @param \Symfony\Contracts\HttpClient\ResponseInterface|mixed[] $responses
  90. * @param float|null $timeout
  91. */
  92. public function stream($responses, $timeout = null)
  93. {
  94. if ($responses instanceof ResponseInterface) {
  95. $responses = [$responses];
  96. }
  97. return new ResponseStream(MockResponse::stream($responses, $timeout));
  98. }
  99. public function getRequestsCount()
  100. {
  101. return $this->requestsCount;
  102. }
  103. /**
  104. * @return $this
  105. * @param mixed[] $options
  106. */
  107. public function withOptions($options)
  108. {
  109. $clone = clone $this;
  110. $clone->defaultOptions = self::mergeDefaultOptions($options, $this->defaultOptions, true);
  111. return $clone;
  112. }
  113. /**
  114. * @return void
  115. */
  116. public function reset()
  117. {
  118. $this->requestsCount = 0;
  119. }
  120. }