scratch – Blame information for rev 87

Subversion Repositories:
Rev:
Rev Author Line No. Line
87 office 1 <?php
2  
3 namespace GuzzleHttp\Adapter\Curl;
4  
5 use GuzzleHttp\Adapter\AdapterInterface;
6 use GuzzleHttp\Adapter\TransactionInterface;
7 use GuzzleHttp\Event\RequestEvents;
8 use GuzzleHttp\Exception\AdapterException;
9 use GuzzleHttp\Message\MessageFactoryInterface;
10  
11 /**
12 * HTTP adapter that uses cURL easy handles as a transport layer.
13 *
14 * Requires PHP 5.5+
15 *
16 * When using the CurlAdapter, custom curl options can be specified as an
17 * associative array of curl option constants mapping to values in the
18 * **curl** key of a request's configuration options.
19 */
20 class CurlAdapter implements AdapterInterface
21 {
22 /** @var CurlFactory */
23 private $curlFactory;
24  
25 /** @var MessageFactoryInterface */
26 private $messageFactory;
27  
28 /** @var array Array of curl easy handles */
29 private $handles = [];
30  
31 /** @var array Array of owned curl easy handles */
32 private $ownedHandles = [];
33  
34 /** @var int Total number of idle handles to keep in cache */
35 private $maxHandles;
36  
37 /**
38 * Accepts an associative array of options:
39 *
40 * - handle_factory: Optional callable factory used to create cURL handles.
41 * The callable is invoked with the following arguments:
42 * TransactionInterface, MessageFactoryInterface, and an optional cURL
43 * handle to modify. The factory method must then return a cURL resource.
44 * - max_handles: Maximum number of idle handles (defaults to 5).
45 *
46 * @param MessageFactoryInterface $messageFactory
47 * @param array $options Array of options to use with the adapter
48 */
49 public function __construct(
50 MessageFactoryInterface $messageFactory,
51 array $options = []
52 ) {
53 $this->handles = $this->ownedHandles = [];
54 $this->messageFactory = $messageFactory;
55 $this->curlFactory = isset($options['handle_factory'])
56 ? $options['handle_factory']
57 : new CurlFactory();
58 $this->maxHandles = isset($options['max_handles'])
59 ? $options['max_handles']
60 : 5;
61 }
62  
63 public function __destruct()
64 {
65 foreach ($this->handles as $handle) {
66 if (is_resource($handle)) {
67 curl_close($handle);
68 }
69 }
70 }
71  
72 public function send(TransactionInterface $transaction)
73 {
74 RequestEvents::emitBefore($transaction);
75 if ($response = $transaction->getResponse()) {
76 return $response;
77 }
78  
79 $factory = $this->curlFactory;
80 $handle = $factory(
81 $transaction,
82 $this->messageFactory,
83 $this->checkoutEasyHandle()
84 );
85  
86 curl_exec($handle);
87 $info = curl_getinfo($handle);
88 $info['curl_result'] = curl_errno($handle);
89  
90 if ($info['curl_result']) {
91 $this->handleError($transaction, $info, $handle);
92 } else {
93 $this->releaseEasyHandle($handle);
94 if ($body = $transaction->getResponse()->getBody()) {
95 $body->seek(0);
96 }
97 RequestEvents::emitComplete($transaction, $info);
98 }
99  
100 return $transaction->getResponse();
101 }
102  
103 private function handleError(
104 TransactionInterface $transaction,
105 $info,
106 $handle
107 ) {
108 $error = curl_error($handle);
109 $this->releaseEasyHandle($handle);
110 RequestEvents::emitError(
111 $transaction,
112 new AdapterException("cURL error {$info['curl_result']}: {$error}"),
113 $info
114 );
115 }
116  
117 private function checkoutEasyHandle()
118 {
119 // Find an unused handle in the cache
120 if (false !== ($key = array_search(false, $this->ownedHandles, true))) {
121 $this->ownedHandles[$key] = true;
122 return $this->handles[$key];
123 }
124  
125 // Add a new handle
126 $handle = curl_init();
127 $id = (int) $handle;
128 $this->handles[$id] = $handle;
129 $this->ownedHandles[$id] = true;
130  
131 return $handle;
132 }
133  
134 private function releaseEasyHandle($handle)
135 {
136 $id = (int) $handle;
137 if (count($this->ownedHandles) > $this->maxHandles) {
138 curl_close($this->handles[$id]);
139 unset($this->handles[$id], $this->ownedHandles[$id]);
140 } else {
141 // curl_reset doesn't clear these out for some reason
142 curl_setopt_array($handle, [
143 CURLOPT_HEADERFUNCTION => null,
144 CURLOPT_WRITEFUNCTION => null,
145 CURLOPT_READFUNCTION => null,
146 CURLOPT_PROGRESSFUNCTION => null
147 ]);
148 curl_reset($handle);
149 $this->ownedHandles[$id] = false;
150 }
151 }
152 }