GuzzleHttpHandlerCurlMultiHandler

Returns an asynchronous response using curl_multi_* functions.

Defined (1)

The class is defined in the following location(s).

/lib/Azure/GuzzleHttp/Handler/CurlMultiHandler.php  
  1. class CurlMultiHandler 
  2. /** @var CurlFactoryInterface */ 
  3. private $factory; 
  4. private $selectTimeout; 
  5. private $active; 
  6. private $handles = []; 
  7. private $delays = []; 
  8.  
  9. /** 
  10. * This handler accepts the following options: 
  11. * - handle_factory: An optional factory used to create curl handles 
  12. * - select_timeout: Optional timeout (in seconds) to block before timing 
  13. * out while selecting curl handles. Defaults to 1 second. 
  14. * @param array $options 
  15. */ 
  16. public function __construct(array $options = []) 
  17. $this->factory = isset($options['handle_factory']) 
  18. ? $options['handle_factory'] : new CurlFactory(50); 
  19. $this->selectTimeout = isset($options['select_timeout']) 
  20. ? $options['select_timeout'] : 1; 
  21.  
  22. public function __get($name) 
  23. if ($name === '_mh') { 
  24. return $this->_mh = curl_multi_init(); 
  25.  
  26. throw new \BadMethodCallException(); 
  27.  
  28. public function __destruct() 
  29. if (isset($this->_mh)) { 
  30. curl_multi_close($this->_mh); 
  31. unset($this->_mh); 
  32.  
  33. public function __invoke(RequestInterface $request, array $options) 
  34. $easy = $this->factory->create($request, $options); 
  35. $id = (int) $easy->handle; 
  36.  
  37. $promise = new Promise( 
  38. [$this, 'execute'],  
  39. function () use ($id) { return $this->cancel($id); } 
  40. ); 
  41.  
  42. $this->addRequest(['easy' => $easy, 'deferred' => $promise]); 
  43.  
  44. return $promise; 
  45.  
  46. /** 
  47. * Ticks the curl event loop. 
  48. */ 
  49. public function tick() 
  50. // Add any delayed handles if needed. 
  51. if ($this->delays) { 
  52. $currentTime = microtime(true); 
  53. foreach ($this->delays as $id => $delay) { 
  54. if ($currentTime >= $delay) { 
  55. unset($this->delays[$id]); 
  56. curl_multi_add_handle( 
  57. $this->_mh,  
  58. $this->handles[$id]['easy']->handle 
  59. ); 
  60.  
  61. // Step through the task queue which may add additional requests. 
  62. P\queue()->run(); 
  63.  
  64. if ($this->active && 
  65. curl_multi_select($this->_mh, $this->selectTimeout) === -1 
  66. ) { 
  67. // Perform a usleep if a select returns -1. 
  68. // See: https://bugs.php.net/bug.php?id=61141 
  69. usleep(250); 
  70.  
  71. while (curl_multi_exec($this->_mh, $this->active) === CURLM_CALL_MULTI_PERFORM); 
  72.  
  73. $this->processMessages(); 
  74.  
  75. /** 
  76. * Runs until all outstanding connections have completed. 
  77. */ 
  78. public function execute() 
  79. $queue = P\queue(); 
  80.  
  81. while ($this->handles || !$queue->isEmpty()) { 
  82. // If there are no transfers, then sleep for the next delay 
  83. if (!$this->active && $this->delays) { 
  84. usleep($this->timeToNext()); 
  85. $this->tick(); 
  86.  
  87. private function addRequest(array $entry) 
  88. $easy = $entry['easy']; 
  89. $id = (int) $easy->handle; 
  90. $this->handles[$id] = $entry; 
  91. if (empty($easy->options['delay'])) { 
  92. curl_multi_add_handle($this->_mh, $easy->handle); 
  93. } else { 
  94. $this->delays[$id] = microtime(true) + ($easy->options['delay'] / 1000); 
  95.  
  96. /** 
  97. * Cancels a handle from sending and removes references to it. 
  98. * @param int $id Handle ID to cancel and remove. 
  99. * @return bool True on success, false on failure. 
  100. */ 
  101. private function cancel($id) 
  102. // Cannot cancel if it has been processed. 
  103. if (!isset($this->handles[$id])) { 
  104. return false; 
  105.  
  106. $handle = $this->handles[$id]['easy']->handle; 
  107. unset($this->delays[$id], $this->handles[$id]); 
  108. curl_multi_remove_handle($this->_mh, $handle); 
  109. curl_close($handle); 
  110.  
  111. return true; 
  112.  
  113. private function processMessages() 
  114. while ($done = curl_multi_info_read($this->_mh)) { 
  115. $id = (int) $done['handle']; 
  116. curl_multi_remove_handle($this->_mh, $done['handle']); 
  117.  
  118. if (!isset($this->handles[$id])) { 
  119. // Probably was cancelled. 
  120. continue; 
  121.  
  122. $entry = $this->handles[$id]; 
  123. unset($this->handles[$id], $this->delays[$id]); 
  124. $entry['easy']->errno = $done['result']; 
  125. $entry['deferred']->resolve( 
  126. CurlFactory::finish( 
  127. $this,  
  128. $entry['easy'],  
  129. $this->factory 
  130. ); 
  131.  
  132. private function timeToNext() 
  133. $currentTime = microtime(true); 
  134. $nextTime = PHP_INT_MAX; 
  135. foreach ($this->delays as $time) { 
  136. if ($time < $nextTime) { 
  137. $nextTime = $time; 
  138.  
  139. return max(0, $nextTime - $currentTime) * 1000000;