Passed
Branch master (776013)
by payever
03:51
created

CurlClient::execute()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 15
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
/**
3
 * This class represents Curl implementation of Client
4
 *
5
 * PHP version 5.4
6
 *
7
 * @category  Http
8
 * @package   Payever\Core
9
 * @author    payever GmbH <[email protected]>
10
 * @copyright 2017-2018 payever GmbH
11
 * @license   MIT <https://opensource.org/licenses/MIT>
12
 * @link      https://getpayever.com/developer/api-documentation/ Documentation
13
 */
14
15
namespace Payever\ExternalIntegration\Core\Http\Client;
16
17
use Payever\ExternalIntegration\Core\Base\HttpClientInterface;
18
use Payever\ExternalIntegration\Core\Base\RequestInterface;
19
use Payever\ExternalIntegration\Core\Exception\PayeverCommunicationException;
20
use Payever\ExternalIntegration\Core\Http\Request;
21
use Payever\ExternalIntegration\Core\Http\Response;
22
use Psr\Log\LoggerAwareInterface;
23
use Psr\Log\LoggerInterface;
24
25
/**
26
 * This class represents Curl implementation of Client
27
 *
28
 * PHP version 5.4
29
 *
30
 * @category  Http
31
 * @package   Payever\Core
32
 * @author    payever GmbH <[email protected]>
33
 * @copyright 2017-2018 payever GmbH
34
 * @license   MIT <https://opensource.org/licenses/MIT>
35
 * @link      https://getpayever.com/developer/api-documentation/ Documentation
36
 */
37
class CurlClient implements HttpClientInterface, LoggerAwareInterface
38
{
39
    /**
40
     * CurlClient constructor.
41
     *
42
     * @throws \RuntimeException when cURL extension is not enabled
43
     */
44
    public function __construct()
45
    {
46
        if (!extension_loaded('curl')) {
47
            throw new \RuntimeException('cURL PHP extension must be enabled in order to use this HTTP client');
48
        }
49
    }
50
51
    /** @var LoggerInterface */
52
    protected $logger;
53
54
    public function setLogger(LoggerInterface $logger)
55
    {
56
        $this->logger = $logger;
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     *
62
     * @param RequestInterface $request
63
     *
64
     * @throws \Exception
65
     */
66
    public function execute(RequestInterface $request)
67
    {
68
        try {
69
            return $this->executeRequest($request);
70
        } catch (\Exception $exception) {
71
            $this->logger->critical(
72
                sprintf(
73
                    'HTTP Request failed: %s %s',
74
                    $exception->getCode(),
75
                    $exception->getMessage()
76
                ),
77
                array('trace' => $exception->getTraceAsString())
78
            );
79
80
            throw $exception;
81
        }
82
    }
83
84
    /**
85
     * @param RequestInterface $request
86
     *
87
     * @return Response
88
     *
89
     * @throws \RuntimeException
90
     * @throws PayeverCommunicationException
91
     */
92
    protected function executeRequest(RequestInterface $request)
93
    {
94
        $this->logger->debug(
95
            sprintf('HTTP Request %s %s', $request->getMethod(), $request->getUrl()),
96
            array('headers' => $request->getHeaders(), 'body' => $request->toArray())
97
        );
98
99
        if (!$request->validate()) {
100
            throw new \RuntimeException('Request entity is not valid');
101
        }
102
103
        $ch = curl_init();
104
105
        if (!is_resource($ch)) {
106
            throw new \RuntimeException('Could not get cURL resource');
107
        }
108
109
        $options = array(
110
            CURLOPT_URL          => $request->getUrl(),
111
            CURLOPT_HTTPHEADER   => $request->getHeaders(),
112
            CURLOPT_HTTP_VERSION => $request->getProtocolVersion(),
113
        );
114
115
        $customMethods = array(
116
            Request::METHOD_PUT,
117
            Request::METHOD_PATCH,
118
            Request::METHOD_DELETE,
119
        );
120
121
        if ($request->getMethod() === Request::METHOD_POST) {
122
            $options[CURLOPT_POST] = true;
123
            $options[CURLOPT_POSTFIELDS] = $request->toArray();
124
        } elseif ($request->getMethod() === Request::METHOD_GET && $request->toArray()) {
125
            $paramChar = strpos('?', $request->getUrl()) === false ? '?' : '&';
126
            $options[CURLOPT_URL] = $request->getUrl() . $paramChar . http_build_query($request->toArray());
127
        } elseif (in_array($request->getMethod(), $customMethods)) {
128
            $options[CURLOPT_CUSTOMREQUEST] = $request->getMethod();
129
            $options[CURLOPT_POSTFIELDS] = $request->toArray();
130
        }
131
132
        if (isset($options[CURLOPT_POSTFIELDS])
133
            && $request->getHeader('Content-Type') == 'application/x-www-form-urlencoded'
134
        ) {
135
            $options[CURLOPT_POSTFIELDS] = http_build_query($options[CURLOPT_POSTFIELDS]);
136
        } elseif (isset($options[CURLOPT_POSTFIELDS])
137
            && $request->getHeader('Content-Type') == 'application/json'
138
        ) {
139
            $options[CURLOPT_POSTFIELDS] = json_encode($options[CURLOPT_POSTFIELDS]);
140
        }
141
142
        curl_setopt_array($ch, $this->getRequestOptions($options));
143
144
        $result       = curl_exec($ch);
145
        $httpCode     = curl_getinfo($ch, CURLINFO_HTTP_CODE);
146
        $errorMessage = curl_error($ch);
147
        $errorNumber  = curl_errno($ch);
148
149
        curl_close($ch);
150
151
        $this->logger->debug(
152
            sprintf('HTTP Response %s %s', $request->getMethod(), $request->getUrl()),
153
            array('httpCode' => $httpCode, 'body' => $result, 'curlError' => $errorMessage)
154
        );
155
156
        if ($errorNumber !== 0) {
157
            throw new PayeverCommunicationException($errorMessage, $errorNumber);
158
        }
159
160
        if ($httpCode >= 400) {
161
            $message = $result;
162
            $data = @json_decode($result, true);
163
164
            if (isset($data['error_description'])) {
165
                $message = $data['error_description'];
166
            } elseif (isset($data['message'])) {
167
                $message = $data['message'];
168
            }
169
170
            if (isset($data['error'])) {
171
                $message = sprintf('%s: %s', $data['error'], $message ?: 'Unknown');
172
            }
173
174
            throw new PayeverCommunicationException($message, $httpCode);
175
        }
176
177
        $response = new Response();
178
179
        $response
180
            ->setRequestEntity($request->getRequestEntity())
181
            ->setResponseEntity($request->getResponseEntity())
182
            ->load($result);
183
184
        if ($response->isFailed()) {
185
            throw new PayeverCommunicationException(
186
                $response->getResponseEntity()->getErrorDescription()
187
            );
188
        }
189
190
        return $response;
191
    }
192
193
    /**
194
     * Returns cURL request params array
195
     *
196
     * @param array $override
197
     *
198
     * @return array
199
     */
200
    protected function getRequestOptions($override)
201
    {
202
        $default = array(
203
            CURLOPT_HEADER         => 0,
204
            CURLOPT_RETURNTRANSFER => true,
205
            CURLOPT_SSL_VERIFYPEER => false,
206
            CURLOPT_SSL_VERIFYHOST => 0,
207
            CURLOPT_TIMEOUT        => 30,
208
            CURLOPT_CONNECTTIMEOUT => 15,
209
            CURLOPT_HTTP_VERSION   => 1.1,
210
        );
211
212
        return $override + $default;
213
    }
214
}
215