MockHttpClient.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  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. private $responseFactory;
  27. private $requestsCount = 0;
  28. private $defaultOptions = [];
  29. /**
  30. * @param callable|callable[]|ResponseInterface|ResponseInterface[]|iterable|null $responseFactory
  31. */
  32. public function __construct($responseFactory = null, ?string $baseUri = 'https://example.com')
  33. {
  34. $this->setResponseFactory($responseFactory);
  35. $this->defaultOptions['base_uri'] = $baseUri;
  36. }
  37. /**
  38. * @param callable|callable[]|ResponseInterface|ResponseInterface[]|iterable|null $responseFactory
  39. */
  40. public function setResponseFactory($responseFactory): void
  41. {
  42. if ($responseFactory instanceof ResponseInterface) {
  43. $responseFactory = [$responseFactory];
  44. }
  45. if (!$responseFactory instanceof \Iterator && null !== $responseFactory && !\is_callable($responseFactory)) {
  46. $responseFactory = (static function () use ($responseFactory) {
  47. yield from $responseFactory;
  48. })();
  49. }
  50. $this->responseFactory = $responseFactory;
  51. }
  52. /**
  53. * {@inheritdoc}
  54. */
  55. public function request(string $method, string $url, array $options = []): ResponseInterface
  56. {
  57. [$url, $options] = $this->prepareRequest($method, $url, $options, $this->defaultOptions, true);
  58. $url = implode('', $url);
  59. if (null === $this->responseFactory) {
  60. $response = new MockResponse();
  61. } elseif (\is_callable($this->responseFactory)) {
  62. $response = ($this->responseFactory)($method, $url, $options);
  63. } elseif (!$this->responseFactory->valid()) {
  64. throw new TransportException('The response factory iterator passed to MockHttpClient is empty.');
  65. } else {
  66. $responseFactory = $this->responseFactory->current();
  67. $response = \is_callable($responseFactory) ? $responseFactory($method, $url, $options) : $responseFactory;
  68. $this->responseFactory->next();
  69. }
  70. ++$this->requestsCount;
  71. if (!$response instanceof ResponseInterface) {
  72. throw new TransportException(sprintf('The response factory passed to MockHttpClient must return/yield an instance of ResponseInterface, "%s" given.', \is_object($response) ? \get_class($response) : \gettype($response)));
  73. }
  74. return MockResponse::fromRequest($method, $url, $options, $response);
  75. }
  76. /**
  77. * {@inheritdoc}
  78. */
  79. public function stream($responses, float $timeout = null): ResponseStreamInterface
  80. {
  81. if ($responses instanceof ResponseInterface) {
  82. $responses = [$responses];
  83. } elseif (!is_iterable($responses)) {
  84. throw new \TypeError(sprintf('"%s()" expects parameter 1 to be an iterable of MockResponse objects, "%s" given.', __METHOD__, get_debug_type($responses)));
  85. }
  86. return new ResponseStream(MockResponse::stream($responses, $timeout));
  87. }
  88. public function getRequestsCount(): int
  89. {
  90. return $this->requestsCount;
  91. }
  92. /**
  93. * {@inheritdoc}
  94. */
  95. public function withOptions(array $options): self
  96. {
  97. $clone = clone $this;
  98. $clone->defaultOptions = self::mergeDefaultOptions($options, $this->defaultOptions, true);
  99. return $clone;
  100. }
  101. public function reset()
  102. {
  103. $this->requestsCount = 0;
  104. }
  105. }