Passed
Push — master ( 232102...169a1f )
by Radu
01:29
created

CurlBrowser::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 2
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace WebServCo\Framework;
3
4
use WebServCo\Framework\Http;
5
use WebServCo\Framework\Exceptions\ApplicationException;
6
7
final class CurlBrowser implements
8
    \WebServCo\Framework\Interfaces\HttpBrowserInterface
9
{
10
    protected $debug = true;
11
    protected $skipSslVerification = false;
12
    protected $requestHeaders = [];
13
14
    protected $method;
15
    protected $postData;
16
17
    protected $curl;
18
    protected $debugStderr;
19
    protected $debugOutput;
20
    protected $debugInfo;
21
    protected $response;
22
    protected $responseHeaders;
23
24
    protected $logger;
25
26
    public function __construct($logDir, \WebServCo\Framework\Interfaces\RequestInterface $requestInterface)
27
    {
28
        $this->logger = new \WebServCo\Framework\FileLogger(
29
            'CurlBrowser',
30
            $logDir,
31
            $requestInterface
32
        );
33
        $this->logger->clear();
34
    }
35
36
    public function setDebug(bool $debug)
37
    {
38
        $this->debug = $debug;
39
    }
40
41
    public function setSkipSSlVerification(bool $skipSslVerification)
42
    {
43
        $this->skipSslVerification = $skipSslVerification;
44
    }
45
46
    public function setRequestHeaders(array $requestHeaders)
47
    {
48
        $this->requestHeaders = $requestHeaders;
49
    }
50
51
    public function getResponseHeaders()
52
    {
53
        return $this->responseHeaders;
54
    }
55
56
    public function get($url)
57
    {
58
        $this->setMethod(Http::METHOD_GET);
59
        return $this->retrieve($url);
60
    }
61
62
    public function head($url)
63
    {
64
        $this->setMethod(Http::METHOD_HEAD);
65
        return $this->retrieve($url);
66
    }
67
68
    public function post($url, $postData = [])
69
    {
70
        $this->setMethod(Http::METHOD_POST);
71
        $this->setPostData($postData);
72
        return $this->retrieve($url);
73
    }
74
75
    protected function setMethod($method)
76
    {
77
        if (!in_array($method, Http::getMethods())) {
78
            throw new ApplicationException('Unsupported method');
79
        }
80
        $this->method = $method;
81
        return true;
82
    }
83
84
    protected function setPostData(array $postData)
85
    {
86
        $this->postData = $postData;
87
    }
88
89
    protected function debugInit()
90
    {
91
        if ($this->debug) {
92
            ob_start();
93
            $this->debugStderr = fopen('php://output', 'w');
94
            return true;
95
        }
96
        return false;
97
    }
98
99
    protected function debugDo()
100
    {
101
        if ($this->debug) {
102
            //curl_setopt($this->curl, CURLINFO_HEADER_OUT, 1); /* verbose not working if this is enabled */
103
            curl_setopt($this->curl, CURLOPT_VERBOSE, 1);
104
            curl_setopt($this->curl, CURLOPT_STDERR, $this->debugStderr);
105
            return false;
106
        }
107
        return false;
108
    }
109
110
    protected function debugFinish()
111
    {
112
        if ($this->debug) {
113
            fclose($this->debugStderr);
114
            $this->debugOutput = ob_get_clean();
115
116
            $this->logger->debug('CURL INFO:', $this->debugInfo);
117
            $this->logger->debug('CURL VERBOSE:', $this->debugOutput);
118
            $this->logger->debug('CURL RESPONSE:', $this->response);
119
120
            return true;
121
        }
122
        return false;
123
    }
124
125
    protected function getHttpCode()
126
    {
127
        return isset($this->debugInfo['http_code']) ? $this->debugInfo['http_code']: false;
128
    }
129
130
    protected function parseResponseHeaders($headerString)
131
    {
132
        $headers = [];
133
        $lines = explode("\r\n", $headerString);
134
        foreach ($lines as $index => $line) {
135
            if (0 === $index) {
136
                continue; /* we'll get the status code elsewhere */
137
            }
138
            list($key, $value) = explode(': ', $line);
139
            $headers[$key] = $value;
140
        }
141
        return $headers;
142
    }
143
144
    protected function parseRequestHeaders($headers)
145
    {
146
        $data = [];
147
        foreach ($headers as $k => $v) {
148
            $data[] = sprintf('%s: %s', $k, $v);
149
        }
150
        return $data;
151
    }
152
153
    protected function retrieve($url)
154
    {
155
        $this->debugInit();
156
        $this->responseHeaders = [];
157
158
        $this->curl = curl_init();
159
        curl_setopt_array(
160
            $this->curl,
161
            [
162
                CURLOPT_RETURNTRANSFER => true, /* return instead of outputting */
163
                CURLOPT_URL => $url,
164
                CURLOPT_HEADER => true, /* include the header in the output */
165
                CURLOPT_FOLLOWLOCATION => true, /* follow redirects */
166
            ]
167
        );
168
        if ($this->skipSslVerification) {
169
            // stop cURL from verifying the peer's certificate
170
            curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false);
171
            // don't check the existence of a common name in the SSL peer certificate
172
            curl_setopt($this->curl, CURLOPT_SSL_VERIFYHOST, 0);
173
        }
174
        if (!empty($this->requestHeaders)) {
175
            curl_setopt(
176
                $this->curl,
177
                CURLOPT_HTTPHEADER,
178
                $this->parseRequestHeaders($this->requestHeaders)
179
            );
180
        }
181
182
        $this->debugDo();
183
184
        switch ($this->method) {
185
            case Http::METHOD_POST:
186
                curl_setopt($this->curl, CURLOPT_POST, true);
187
                if (!empty($this->postData)) {
188
                    curl_setopt($this->curl, CURLOPT_POSTFIELDS, $this->postData);
189
                }
190
                break;
191
            case Http::METHOD_HEAD:
192
                curl_setopt($this->curl, CURLOPT_NOBODY, true);
193
                break;
194
        }
195
196
        $this->response = curl_exec($this->curl);
197
        if (false === $this->response) {
198
            throw new ApplicationException(curl_error($this->curl));
199
        }
200
201
        $this->debugInfo = curl_getinfo($this->curl);
202
203
        $httpCode = $this->getHttpCode();
204
205
        curl_close($this->curl);
206
207
        /**
208
         * For redirects, the response will contain evey header/body pair.
209
         * The last header/body will be at the end of the response.
210
         */
211
        $responseParts = explode("\r\n\r\n", $this->response);
212
        $body = trim(array_pop($responseParts));
213
214
        foreach ($responseParts as $item) {
215
            $this->responseHeaders[] = $this->parseResponseHeaders($item);
216
        }
217
218
        $this->debugFinish();
219
220
        return new \WebServCo\Framework\HttpResponse(
221
            $body,
222
            $httpCode,
223
            end($this->responseHeaders)
224
        );
225
    }
226
}
227