Pool.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. <?php
  2. namespace GuzzleHttp;
  3. use GuzzleHttp\Promise\EachPromise;
  4. use GuzzleHttp\Promise\PromiseInterface;
  5. use GuzzleHttp\Promise\PromisorInterface;
  6. use Psr\Http\Message\RequestInterface;
  7. /**
  8. * Sends an iterator of requests concurrently using a capped pool size.
  9. *
  10. * The pool will read from an iterator until it is cancelled or until the
  11. * iterator is consumed. When a request is yielded, the request is sent after
  12. * applying the "request_options" request options (if provided in the ctor).
  13. *
  14. * When a function is yielded by the iterator, the function is provided the
  15. * "request_options" array that should be merged on top of any existing
  16. * options, and the function MUST then return a wait-able promise.
  17. */
  18. class Pool implements PromisorInterface
  19. {
  20. /** @var EachPromise */
  21. private $each;
  22. /**
  23. * @param ClientInterface $client Client used to send the requests.
  24. * @param array|\Iterator $requests Requests or functions that return
  25. * requests to send concurrently.
  26. * @param array $config Associative array of options
  27. * - concurrency: (int) Maximum number of requests to send concurrently
  28. * - options: Array of request options to apply to each request.
  29. * - fulfilled: (callable) Function to invoke when a request completes.
  30. * - rejected: (callable) Function to invoke when a request is rejected.
  31. */
  32. public function __construct(
  33. ClientInterface $client,
  34. $requests,
  35. array $config = []
  36. ) {
  37. // Backwards compatibility.
  38. if (isset($config['pool_size'])) {
  39. $config['concurrency'] = $config['pool_size'];
  40. } elseif (!isset($config['concurrency'])) {
  41. $config['concurrency'] = 25;
  42. }
  43. if (isset($config['options'])) {
  44. $opts = $config['options'];
  45. unset($config['options']);
  46. } else {
  47. $opts = [];
  48. }
  49. $iterable = \GuzzleHttp\Promise\iter_for($requests);
  50. $requests = function () use ($iterable, $client, $opts) {
  51. foreach ($iterable as $key => $rfn) {
  52. if ($rfn instanceof RequestInterface) {
  53. yield $key => $client->sendAsync($rfn, $opts);
  54. } elseif (is_callable($rfn)) {
  55. yield $key => $rfn($opts);
  56. } else {
  57. throw new \InvalidArgumentException('Each value yielded by '
  58. . 'the iterator must be a Psr7\Http\Message\RequestInterface '
  59. . 'or a callable that returns a promise that fulfills '
  60. . 'with a Psr7\Message\Http\ResponseInterface object.');
  61. }
  62. }
  63. };
  64. $this->each = new EachPromise($requests(), $config);
  65. }
  66. /**
  67. * Get promise
  68. *
  69. * @return PromiseInterface
  70. */
  71. public function promise()
  72. {
  73. return $this->each->promise();
  74. }
  75. /**
  76. * Sends multiple requests concurrently and returns an array of responses
  77. * and exceptions that uses the same ordering as the provided requests.
  78. *
  79. * IMPORTANT: This method keeps every request and response in memory, and
  80. * as such, is NOT recommended when sending a large number or an
  81. * indeterminate number of requests concurrently.
  82. *
  83. * @param ClientInterface $client Client used to send the requests
  84. * @param array|\Iterator $requests Requests to send concurrently.
  85. * @param array $options Passes through the options available in
  86. * {@see GuzzleHttp\Pool::__construct}
  87. *
  88. * @return array Returns an array containing the response or an exception
  89. * in the same order that the requests were sent.
  90. * @throws \InvalidArgumentException if the event format is incorrect.
  91. */
  92. public static function batch(
  93. ClientInterface $client,
  94. $requests,
  95. array $options = []
  96. ) {
  97. $res = [];
  98. self::cmpCallback($options, 'fulfilled', $res);
  99. self::cmpCallback($options, 'rejected', $res);
  100. $pool = new static($client, $requests, $options);
  101. $pool->promise()->wait();
  102. ksort($res);
  103. return $res;
  104. }
  105. /**
  106. * Execute callback(s)
  107. *
  108. * @return void
  109. */
  110. private static function cmpCallback(array &$options, $name, array &$results)
  111. {
  112. if (!isset($options[$name])) {
  113. $options[$name] = function ($v, $k) use (&$results) {
  114. $results[$k] = $v;
  115. };
  116. } else {
  117. $currentFn = $options[$name];
  118. $options[$name] = function ($v, $k) use (&$results, $currentFn) {
  119. $currentFn($v, $k);
  120. $results[$k] = $v;
  121. };
  122. }
  123. }
  124. }