MultiCurlRequest   A
last analyzed

Complexity

Total Complexity 31

Size/Duplication

Total Lines 225
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 31
lcom 1
cbo 2
dl 0
loc 225
ccs 79
cts 79
cp 1
rs 9.8
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A ping() 0 14 3
A addHttpRequest() 0 3 1
A setCallback() 0 3 1
A setOption() 0 15 2
A getOption() 0 3 2
A getLastTransferInfo() 0 3 1
A getLastError() 0 3 2
A getLastErrorCode() 0 3 1
A execute() 0 16 2
C doMakeMultiRequest() 0 33 8
B doCheckRequestResponse() 0 21 5
A __destruct() 0 3 1
1
<?php
2
3
namespace Onoi\HttpRequest;
4
5
use InvalidArgumentException;
6
use Closure;
7
8
/**
9
 * @license GNU GPL v2+
10
 * @since 1.0
11
 *
12
 * @author mwjames
13
 */
14
class MultiCurlRequest implements HttpRequest {
15
16
	/**
17
	 * @var resource
18
	 */
19
	private $handle;
20
21
	/**
22
	 * @var array
23
	 */
24
	protected $options = array();
25
26
	/**
27
	 * @var CurlRequest[]
28
	 */
29
	private $httpRequests = array();
30
31
	/**
32
	 * @var integer
33
	 */
34
	private $lastErrorCode = 0;
35
36
	/**
37
	 * @since 1.0
38
	 *
39
	 * @param resource $handle
40
	 */
41 12
	public function __construct( $handle ) {
42
43 12
		if ( get_resource_type( $handle ) !== 'curl_multi' ) {
44 1
			throw new InvalidArgumentException( "Expected a cURL multi resource type" );
45
		}
46
47 11
		$this->handle = $handle;
48 11
	}
49
50
	/**
51
	 * @since 1.0
52
	 *
53
	 * @return boolean
54
	 */
55 3
	public function ping() {
56
57 3
		$canConnect = false;
58
59 3
		foreach ( $this->httpRequests as $httpRequest ) {
60 2
			if ( !$httpRequest->ping() ) {
61 1
				return false;
62
			}
63
64 1
			$canConnect = true;
65 2
		}
66
67 2
		return $canConnect;
68
	}
69
70
	/**
71
	 * @since 1.0
72
	 *
73
	 * @param CurlRequest $httpRequest
74
	 */
75 8
	public function addHttpRequest( CurlRequest $httpRequest ) {
76 8
		$this->httpRequests[] = $httpRequest;
77 8
	}
78
79
	/**
80
	 * @since 1.0
81
	 * @deprecated since 1.1, use ONOI_HTTP_REQUEST_ON_COMPLETED_CALLBACK instead
82
	 *
83
	 * @param Closure $callback
84
	 */
85 1
	public function setCallback( Closure $callback ) {
86 1
		$this->setOption( ONOI_HTTP_REQUEST_ON_COMPLETED_CALLBACK, $callback );
87 1
	}
88
89
	/**
90
	 * @since 1.0
91
	 *
92
	 * @param string $name
93
	 * @param mixed $value
94
	 */
95 4
	public function setOption( $name, $value ) {
96
97 4
		$this->options[$name] = $value;
98
99
		// Internal ONOI options are not further relayed
100 4
		if ( strpos( $name, 'ONOI_HTTP_REQUEST' ) !== false ) {
101 3
			return;
102
		}
103
104 1
		curl_multi_setopt(
105 1
			$this->handle,
106 1
			$name,
107
			$value
108 1
		);
109 1
	}
110
111
	/**
112
	 * @since 1.0
113
	 *
114
	 * @param string $name
115
	 *
116
	 * @return mixed|null
117
	 */
118 7
	public function getOption( $name ) {
119 7
		return isset( $this->options[$name] ) ? $this->options[$name] : null;
120
	}
121
122
	/**
123
	 * @since 1.0
124
	 *
125
	 * @param string|null $name
126
	 *
127
	 * @return mixed
128
	 */
129 1
	public function getLastTransferInfo( $name = null ) {
130 1
		return curl_multi_info_read( $this->handle );
131
	}
132
133
	/**
134
	 * @note PHP 5.5.0
135
	 *
136
	 * @since 1.0
137
	 *
138
	 * @return string
139
	 */
140 1
	public function getLastError() {
141 1
		return function_exists( 'curl_multi_strerror' ) ?  curl_multi_strerror( $this->lastErrorCode ) : '';
142
	}
143
144
	/**
145
	 * @since 1.0
146
	 *
147
	 * @return integer
148
	 */
149 1
	public function getLastErrorCode() {
150 1
		return $this->lastErrorCode;
151
	}
152
153
	/**
154
	 * @since 1.0
155
	 *
156
	 * @return array|null
157
	 */
158 6
	public function execute() {
159
160
		// Register all handles and monitor that
161
		// all requests are executed
162 6
		$handleExecutionCounter = array();
163
164 6
		foreach( $this->httpRequests as $httpRequest ) {
165 6
			$httpRequest->setOption( CURLOPT_RETURNTRANSFER, true );
166 6
			$handleExecutionCounter[(int)  $httpRequest() ] = true;
167 6
			curl_multi_add_handle( $this->handle, $httpRequest() );
168 6
		}
169
170 6
		$response = $this->doMakeMultiRequest( $handleExecutionCounter );
171
172 6
		return $response;
173
	}
174
175 6
	private function doMakeMultiRequest( $handleExecutionCounter ) {
176
177 6
		$active = null;
178 6
		$responses = array();
179
180
		// http://php.net/manual/en/function.curl-multi-init.php
181
		// https://gist.github.com/Xeoncross/2362936
182
		do {
183 6
			$this->lastErrorCode = curl_multi_exec( $this->handle, $active );
184
185
			// Wait for activity on any curl-connection
186 6
			if ( curl_multi_select( $this->handle ) == -1 ) {
187 6
				usleep( 100 );
188 6
			}
189
190 6
		} while ( $this->lastErrorCode == CURLM_CALL_MULTI_PERFORM );
191
192 6
		while ( ( $active && $this->lastErrorCode == CURLM_OK ) || $handleExecutionCounter !== array() ) {
193
194 6
			$requestResponse = $this->doCheckRequestResponse( $handleExecutionCounter );
195
196 6
			if ( $requestResponse !== null ) {
197 6
				$responses[] = $requestResponse;
198 6
			}
199
200
			// Continue to exec until curl is ready
201
			do {
202 6
				$this->lastErrorCode = curl_multi_exec( $this->handle, $active );
203 6
			} while ( $this->lastErrorCode == CURLM_CALL_MULTI_PERFORM );
204 6
		}
205
206 6
		return $responses;
207
	}
208
209 6
	private function doCheckRequestResponse( &$handleExecutionCounter ) {
210
211 6
		$requestResponse = null;
212
213 6
		if ( ( $state = curl_multi_info_read( $this->handle ) ) && $state["msg"] == CURLMSG_DONE ) {
214
215 6
			$requestResponse = new RequestResponse( array(
216 6
				'contents' => curl_multi_getcontent( $state['handle'] ),
217 6
				'info'     => curl_getinfo( $state['handle'] )
218 6
			) );
219
220 6
			unset( $handleExecutionCounter[(int) $state['handle']] );
221 6
			curl_multi_remove_handle( $this->handle, $state['handle'] );
222 6
		}
223
224 6
		if ( is_callable( $this->getOption( ONOI_HTTP_REQUEST_ON_COMPLETED_CALLBACK ) ) && $requestResponse !== null ) {
225 3
			call_user_func_array( $this->getOption( ONOI_HTTP_REQUEST_ON_COMPLETED_CALLBACK ), array( $requestResponse ) );
226 3
		}
227
228 6
		return $requestResponse;
229
	}
230
231
	/**
232
	 * @since 1.0
233
	 */
234 11
	public function __destruct() {
235 11
		curl_multi_close( $this->handle );
236 11
	}
237
238
}
239