throw new Exception( "Cannot find CA bundle: " . $this->caBundlePath );
}
}
- static $opts = array(
+ static $opts = [
'connTimeout', 'reqTimeout', 'usePipelining', 'maxConnsPerHost', 'proxy', 'userAgent'
- );
+ ];
foreach ( $opts as $key ) {
if ( isset( $options[$key] ) ) {
$this->$key = $options[$key];
* - reqTimeout : post-connection timeout per request (seconds)
* @return array Response array for request
*/
- final public function run( array $req, array $opts = array() ) {
- $req = $this->runMulti( array( $req ), $opts );
- return $req[0]['response'];
+ final public function run( array $req, array $opts = [] ) {
+ return $this->runMulti( [ $req ], $opts )[0]['response'];
}
/**
* @return array $reqs With response array populated for each
* @throws Exception
*/
- public function runMulti( array $reqs, array $opts = array() ) {
+ public function runMulti( array $reqs, array $opts = [] ) {
$chm = $this->getCurlMulti();
// Normalize $reqs and add all of the required cURL handles...
- $handles = array();
+ $handles = [];
foreach ( $reqs as $index => &$req ) {
- $req['response'] = array(
+ $req['response'] = [
'code' => 0,
'reason' => '',
- 'headers' => array(),
+ 'headers' => [],
'body' => '',
'error' => ''
- );
+ ];
if ( isset( $req[0] ) ) {
$req['method'] = $req[0]; // short-form
unset( $req[0] );
} elseif ( !isset( $req['url'] ) ) {
throw new Exception( "Request has no 'url' field set." );
}
- $req['query'] = isset( $req['query'] ) ? $req['query'] : array();
- $headers = array(); // normalized headers
+ $req['query'] = isset( $req['query'] ) ? $req['query'] : [];
+ $headers = []; // normalized headers
if ( isset( $req['headers'] ) ) {
foreach ( $req['headers'] as $name => $value ) {
$headers[strtolower( $name )] = $value;
// @TODO: use a per-host rolling handle window (e.g. CURLMOPT_MAX_HOST_CONNECTIONS)
$batches = array_chunk( $indexes, $this->maxConnsPerHost );
- $infos = array();
+ $infos = [];
foreach ( $batches as $batch ) {
// Attach all cURL handles for this batch
* @return resource
* @throws Exception
*/
- protected function getCurlHandle( array &$req, array $opts = array() ) {
+ protected function getCurlHandle( array &$req, array $opts = [] ) {
$ch = curl_init();
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT,
$url = $req['url'];
// PHP_QUERY_RFC3986 is PHP 5.4+ only
$query = str_replace(
- array( '+', '%7E' ),
- array( '%20', '~' ),
+ [ '+', '%7E' ],
+ [ '%20', '~' ],
http_build_query( $req['query'], '', '&' )
);
if ( $query != '' ) {
);
} elseif ( $req['method'] === 'POST' ) {
curl_setopt( $ch, CURLOPT_POST, 1 );
+ // Don't interpret POST parameters starting with '@' as file uploads, because this
+ // makes it impossible to POST plain values starting with '@' (and causes security
+ // issues potentially exposing the contents of local files).
+ // The PHP manual says this option was introduced in PHP 5.5 defaults to true in PHP 5.6,
+ // but we support lower versions, and the option doesn't exist in HHVM 5.6.99.
+ if ( defined( 'CURLOPT_SAFE_UPLOAD' ) ) {
+ curl_setopt( $ch, CURLOPT_SAFE_UPLOAD, true );
+ } elseif ( is_array( $req['body'] ) ) {
+ // In PHP 5.2 and later, '@' is interpreted as a file upload if POSTFIELDS
+ // is an array, but not if it's a string. So convert $req['body'] to a string
+ // for safety.
+ $req['body'] = wfArrayToCgi( $req['body'] );
+ }
curl_setopt( $ch, CURLOPT_POSTFIELDS, $req['body'] );
} else {
if ( is_resource( $req['body'] ) || $req['body'] !== '' ) {
$req['headers']['user-agent'] = $this->userAgent;
}
- $headers = array();
+ $headers = [];
foreach ( $req['headers'] as $name => $value ) {
if ( strpos( $name, ': ' ) ) {
throw new Exception( "Headers cannot have ':' in the name." );
curl_setopt( $ch, CURLOPT_HEADERFUNCTION,
function ( $ch, $header ) use ( &$req ) {
$length = strlen( $header );
- $matches = array();
+ $matches = [];
if ( preg_match( "/^(HTTP\/1\.[01]) (\d{3}) (.*)/", $header, $matches ) ) {
$req['response']['code'] = (int)$matches[2];
$req['response']['reason'] = trim( $matches[3] );