MockHandler.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <?php
  2. namespace GuzzleHttp\Handler;
  3. use GuzzleHttp\Exception\RequestException;
  4. use GuzzleHttp\HandlerStack;
  5. use GuzzleHttp\Promise\PromiseInterface;
  6. use GuzzleHttp\Promise\RejectedPromise;
  7. use GuzzleHttp\TransferStats;
  8. use Psr\Http\Message\RequestInterface;
  9. use Psr\Http\Message\ResponseInterface;
  10. /**
  11. * Handler that returns responses or throw exceptions from a queue.
  12. */
  13. class MockHandler implements \Countable
  14. {
  15. private $queue = [];
  16. private $lastRequest;
  17. private $lastOptions;
  18. private $onFulfilled;
  19. private $onRejected;
  20. /**
  21. * Creates a new MockHandler that uses the default handler stack list of
  22. * middlewares.
  23. *
  24. * @param array $queue Array of responses, callables, or exceptions.
  25. * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
  26. * @param callable $onRejected Callback to invoke when the return value is rejected.
  27. *
  28. * @return HandlerStack
  29. */
  30. public static function createWithMiddleware(
  31. array $queue = null,
  32. callable $onFulfilled = null,
  33. callable $onRejected = null
  34. ) {
  35. return HandlerStack::create(new self($queue, $onFulfilled, $onRejected));
  36. }
  37. /**
  38. * The passed in value must be an array of
  39. * {@see Psr7\Http\Message\ResponseInterface} objects, Exceptions,
  40. * callables, or Promises.
  41. *
  42. * @param array $queue
  43. * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
  44. * @param callable $onRejected Callback to invoke when the return value is rejected.
  45. */
  46. public function __construct(
  47. array $queue = null,
  48. callable $onFulfilled = null,
  49. callable $onRejected = null
  50. ) {
  51. $this->onFulfilled = $onFulfilled;
  52. $this->onRejected = $onRejected;
  53. if ($queue) {
  54. call_user_func_array([$this, 'append'], $queue);
  55. }
  56. }
  57. public function __invoke(RequestInterface $request, array $options)
  58. {
  59. if (!$this->queue) {
  60. throw new \OutOfBoundsException('Mock queue is empty');
  61. }
  62. if (isset($options['delay']) && is_numeric($options['delay'])) {
  63. usleep($options['delay'] * 1000);
  64. }
  65. $this->lastRequest = $request;
  66. $this->lastOptions = $options;
  67. $response = array_shift($this->queue);
  68. if (isset($options['on_headers'])) {
  69. if (!is_callable($options['on_headers'])) {
  70. throw new \InvalidArgumentException('on_headers must be callable');
  71. }
  72. try {
  73. $options['on_headers']($response);
  74. } catch (\Exception $e) {
  75. $msg = 'An error was encountered during the on_headers event';
  76. $response = new RequestException($msg, $request, $response, $e);
  77. }
  78. }
  79. if (is_callable($response)) {
  80. $response = call_user_func($response, $request, $options);
  81. }
  82. $response = $response instanceof \Exception
  83. ? \GuzzleHttp\Promise\rejection_for($response)
  84. : \GuzzleHttp\Promise\promise_for($response);
  85. return $response->then(
  86. function ($value) use ($request, $options) {
  87. $this->invokeStats($request, $options, $value);
  88. if ($this->onFulfilled) {
  89. call_user_func($this->onFulfilled, $value);
  90. }
  91. if (isset($options['sink'])) {
  92. $contents = (string) $value->getBody();
  93. $sink = $options['sink'];
  94. if (is_resource($sink)) {
  95. fwrite($sink, $contents);
  96. } elseif (is_string($sink)) {
  97. file_put_contents($sink, $contents);
  98. } elseif ($sink instanceof \Psr\Http\Message\StreamInterface) {
  99. $sink->write($contents);
  100. }
  101. }
  102. return $value;
  103. },
  104. function ($reason) use ($request, $options) {
  105. $this->invokeStats($request, $options, null, $reason);
  106. if ($this->onRejected) {
  107. call_user_func($this->onRejected, $reason);
  108. }
  109. return \GuzzleHttp\Promise\rejection_for($reason);
  110. }
  111. );
  112. }
  113. /**
  114. * Adds one or more variadic requests, exceptions, callables, or promises
  115. * to the queue.
  116. */
  117. public function append()
  118. {
  119. foreach (func_get_args() as $value) {
  120. if ($value instanceof ResponseInterface
  121. || $value instanceof \Exception
  122. || $value instanceof PromiseInterface
  123. || is_callable($value)
  124. ) {
  125. $this->queue[] = $value;
  126. } else {
  127. throw new \InvalidArgumentException('Expected a response or '
  128. . 'exception. Found ' . \GuzzleHttp\describe_type($value));
  129. }
  130. }
  131. }
  132. /**
  133. * Get the last received request.
  134. *
  135. * @return RequestInterface
  136. */
  137. public function getLastRequest()
  138. {
  139. return $this->lastRequest;
  140. }
  141. /**
  142. * Get the last received request options.
  143. *
  144. * @return array
  145. */
  146. public function getLastOptions()
  147. {
  148. return $this->lastOptions;
  149. }
  150. /**
  151. * Returns the number of remaining items in the queue.
  152. *
  153. * @return int
  154. */
  155. public function count()
  156. {
  157. return count($this->queue);
  158. }
  159. public function reset()
  160. {
  161. $this->queue = [];
  162. }
  163. private function invokeStats(
  164. RequestInterface $request,
  165. array $options,
  166. ResponseInterface $response = null,
  167. $reason = null
  168. ) {
  169. if (isset($options['on_stats'])) {
  170. $transferTime = isset($options['transfer_time']) ? $options['transfer_time'] : 0;
  171. $stats = new TransferStats($request, $response, $transferTime, $reason);
  172. call_user_func($options['on_stats'], $stats);
  173. }
  174. }
  175. }