1<?php
2
3namespace OAuth\Common\Http\Client;
4
5use OAuth\Common\Http\Exception\TokenResponseException;
6use OAuth\Common\Http\Uri\UriInterface;
7
8/**
9 * Client implementation for cURL
10 */
11class CurlClient extends AbstractClient
12{
13    /**
14     * If true, explicitly sets cURL to use SSL version 3. Use this if cURL
15     * compiles with GnuTLS SSL.
16     *
17     * @var bool
18     */
19    private $forceSSL3 = false;
20
21    /**
22     * Additional parameters (as `key => value` pairs) to be passed to `curl_setopt`
23     *
24     * @var array
25     */
26    private $parameters = array();
27
28    /**
29     * Additional `curl_setopt` parameters
30     *
31     * @param array $parameters
32     */
33    public function setCurlParameters(array $parameters)
34    {
35        $this->parameters = $parameters;
36    }
37
38    /**
39     * @param bool $force
40     *
41     * @return CurlClient
42     */
43    public function setForceSSL3($force)
44    {
45        $this->forceSSL3 = $force;
46
47        return $this;
48    }
49
50    /**
51     * Any implementing HTTP providers should send a request to the provided endpoint with the parameters.
52     * They should return, in string form, the response body and throw an exception on error.
53     *
54     * @param UriInterface $endpoint
55     * @param mixed        $requestBody
56     * @param array        $extraHeaders
57     * @param string       $method
58     *
59     * @return string
60     *
61     * @throws TokenResponseException
62     * @throws \InvalidArgumentException
63     */
64    public function retrieveResponse(
65        UriInterface $endpoint,
66        $requestBody,
67        array $extraHeaders = array(),
68        $method = 'POST'
69    ) {
70        // Normalize method name
71        $method = strtoupper($method);
72
73        $this->normalizeHeaders($extraHeaders);
74
75        if ($method === 'GET' && !empty($requestBody)) {
76            throw new \InvalidArgumentException('No body expected for "GET" request.');
77        }
78
79        if (!isset($extraHeaders['Content-Type']) && $method === 'POST' && is_array($requestBody)) {
80            $extraHeaders['Content-Type'] = 'Content-Type: application/x-www-form-urlencoded';
81        }
82
83        $extraHeaders['Host']       = 'Host: '.$endpoint->getHost();
84        $extraHeaders['Connection'] = 'Connection: close';
85
86        $ch = curl_init();
87
88        curl_setopt($ch, CURLOPT_URL, $endpoint->getAbsoluteUri());
89
90        if ($method === 'POST' || $method === 'PUT') {
91            if ($requestBody && is_array($requestBody)) {
92                $requestBody = http_build_query($requestBody, '', '&');
93            }
94
95            if ($method === 'PUT') {
96                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
97            } else {
98                curl_setopt($ch, CURLOPT_POST, true);
99            }
100
101            curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
102        } else {
103            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
104        }
105
106        if ($this->maxRedirects > 0) {
107            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
108            curl_setopt($ch, CURLOPT_MAXREDIRS, $this->maxRedirects);
109        }
110
111        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
112        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
113        curl_setopt($ch, CURLOPT_HEADER, false);
114        curl_setopt($ch, CURLOPT_HTTPHEADER, $extraHeaders);
115        curl_setopt($ch, CURLOPT_USERAGENT, $this->userAgent);
116
117        foreach ($this->parameters as $key => $value) {
118            curl_setopt($ch, $key, $value);
119        }
120
121        if ($this->forceSSL3) {
122            curl_setopt($ch, CURLOPT_SSLVERSION, 3);
123        }
124
125        $response     = curl_exec($ch);
126        $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
127
128        if (false === $response) {
129            $errNo  = curl_errno($ch);
130            $errStr = curl_error($ch);
131            curl_close($ch);
132            if (empty($errStr)) {
133                throw new TokenResponseException('Failed to request resource.', $responseCode);
134            }
135            throw new TokenResponseException('cURL Error # '.$errNo.': '.$errStr, $responseCode);
136        }
137
138        curl_close($ch);
139
140        return $response;
141    }
142}
143