Passed
Push — master ( 32692a...12a81e )
by Sebastian
02:54
created

RequestHelper::setTimeout()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 5
rs 10
cc 1
nc 1
nop 1
1
<?php
2
/**
3
 * File containing the {@link RequestHelper} class.
4
 * @package Application Utils
5
 * @subpackage RequestHelper
6
 * @see RequestHelper
7
 */
8
9
namespace AppUtils;
10
11
/**
12
 * Handles sending POST requests with file attachments and regular variables.
13
 * Creates the raw request headers required for the request and sends them
14
 * using file_get_contents with the according context parameters.
15
 *
16
 * @package Application Utils
17
 * @subpackage RequestHelper
18
 * @author Sebastian Mordziol <[email protected]>
19
 */
20
class RequestHelper
21
{
22
    const FILETYPE_TEXT = 'text/plain';
23
24
    const FILETYPE_XML = 'text/xml';
25
26
    const ENCODING_UTF8 = 'UTF-8';
27
28
    const ERROR_REQUEST_FAILED = 17902;
29
    
30
    const ERROR_CURL_INIT_FAILED = 17903;
31
32
   /**
33
    * @var string
34
    */
35
    protected $eol = "\r\n";
36
37
   /**
38
    * @var string
39
    */
40
    protected $mimeBoundary;
41
42
   /**
43
    * @var string
44
    */
45
    protected $data = '';
46
47
   /**
48
    * @var string
49
    */
50
    protected $destination;
51
52
   /**
53
    * @var array
54
    */
55
    protected $headers = array();
56
    
57
   /**
58
    * Whether to verify SSL certificates.
59
    * @var bool
60
    */
61
    protected $verifySSL = true;
62
    
63
   /**
64
    * @var RequestHelper_Boundaries
65
    */
66
    protected $boundaries;
67
    
68
   /**
69
    * @var RequestHelper_Response|NULL
70
    */
71
    protected $response;
72
73
   /**
74
    * @var integer
75
    */
76
    protected $timeout = 30;
77
    
78
   /**
79
    * Creates a new request helper to send POST data to the specified destination URL.
80
    * @param string $destinationURL
81
    */
82
    public function __construct(string $destinationURL)
83
    {
84
        $this->destination = $destinationURL;
85
        $this->mimeBoundary = md5('request-helper-boundary');
86
        $this->boundaries = new RequestHelper_Boundaries($this);
87
        
88
        requireCURL();
89
    }
90
    
91
    public function getMimeBoundary() : string
92
    {
93
        return $this->mimeBoundary;
94
    }
95
    
96
    public function getEOL() : string
97
    {
98
        return $this->eol;
99
    }
100
    
101
    public function setTimeout(int $seconds) : RequestHelper
102
    {
103
        $this->timeout = $seconds;
104
        
105
        return $this;
106
    }
107
108
   /**
109
    * Adds a file to be sent with the request.
110
    *
111
    * @param string $varName The variable name to send the file in
112
    * @param string $fileName The name of the file as it should be received at the destination
113
    * @param string $content The raw content of the file
114
    * @param string $contentType The content type, use the constants to specify this
115
    * @param string $encoding The encoding of the file, use the constants to specify this
116
    */
117
    public function addFile(string $varName, string $fileName, string $content, string $contentType = self::FILETYPE_TEXT, string $encoding = self::ENCODING_UTF8) : RequestHelper
118
    {
119
        $this->boundaries->addFile($varName, $fileName, $content, $contentType, $encoding);
120
        
121
        return $this;
122
    }
123
    
124
   /**
125
    * Adds arbitrary content.
126
    * 
127
    * @param string $varName The variable name to send the content in.
128
    * @param string $content
129
    * @param string $contentType
130
    */
131
    public function addContent(string $varName, string $content, string $contentType) : RequestHelper
132
    {
133
        $this->boundaries->addContent($varName, $content, $contentType);
134
        
135
        return $this;
136
    }
137
138
    /**
139
     * Adds a variable to be sent with the request. If it
140
     * already exists, its value is overwritten.
141
     *
142
     * @param string $name
143
     * @param string $value
144
     */
145
    public function addVariable(string $name, string $value) : RequestHelper
146
    {
147
        $this->boundaries->addVariable($name, $value);
148
        
149
        return $this;
150
    }
151
    
152
   /**
153
    * Sets an HTTP header to include in the request.
154
    * 
155
    * @param string $name
156
    * @param string $value
157
    * @return RequestHelper
158
    */
159
    public function setHeader(string $name, string $value) : RequestHelper
160
    {
161
        $this->headers[$name] = $value;
162
        
163
        return $this;
164
    }
165
    
166
   /**
167
    * Disables SSL certificate checking.
168
    * 
169
    * @return RequestHelper
170
    */
171
    public function disableSSLChecks() : RequestHelper
172
    {
173
        $this->verifySSL = false;
174
        return $this;
175
    }
176
   
177
   /**
178
    * @var integer
179
    */
180
    protected $contentLength = 0;
181
182
   /**
183
    * Sends the POST request to the destination, and returns
184
    * the response text.
185
    *
186
    * The response object is stored internally, so after calling
187
    * this method it may be retrieved at any moment using the
188
    * {@link getResponse()} method.
189
    *
190
    * @return string
191
    * @see RequestHelper::getResponse()
192
    * @throws RequestHelper_Exception
193
    * 
194
    * @see RequestHelper::ERROR_REQUEST_FAILED
195
    */
196
    public function send() : string
197
    {
198
        $this->data = $this->boundaries->render();
199
200
        $info = parseURL($this->destination);
201
        
202
        $ch = $this->createCURL($info);
203
        
204
        $output = curl_exec($ch);
205
206
        $info = curl_getinfo($ch);
207
208
        $this->response = new RequestHelper_Response($this, $info);
209
        
210
        // CURL will complain about an empty response when the 
211
        // server sends a 100-continue code. That should not be
212
        // regarded as an error.
213
        if($output === false && $this->response->getCode() !== 100)
214
        {
215
            $this->response->setError(
216
                curl_errno($ch),
217
                curl_error($ch)
218
            );
219
        }
220
        else
221
        {
222
            $this->response->setBody($output);
0 ignored issues
show
Bug introduced by
It seems like $output can also be of type boolean; however, parameter $body of AppUtils\RequestHelper_Response::setBody() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

222
            $this->response->setBody(/** @scrutinizer ignore-type */ $output);
Loading history...
223
        }
224
        
225
        curl_close($ch);
226
        
227
        return $this->response->getResponseBody();
228
    }
229
    
230
    public function getBody() : string
231
    {
232
        return $this->data;
233
    }
234
    
235
   /**
236
    * Creates a new CURL resource configured according to the
237
    * request's settings.
238
    * 
239
    * @param URLInfo $url
240
    * @throws RequestHelper_Exception
241
    * @return resource
242
    */
243
    protected function createCURL(URLInfo $url)
244
    {
245
        $ch = curl_init();
246
        if($ch === false)
247
        {
248
            throw new RequestHelper_Exception(
249
                'Could not initialize a new cURL instance.',
250
                'Calling curl_init returned false. Additional information is not available.',
251
                self::ERROR_CURL_INIT_FAILED
252
            );
253
        }
254
255
        $this->setHeader('Content-Length', $this->boundaries->getContentLength());
256
        $this->setHeader('Content-Type', 'multipart/form-data; charset=UTF-8; boundary=' . $this->mimeBoundary);
257
        
258
        //curl_setopt($ch, CURLOPT_VERBOSE, true);
259
        curl_setopt($ch, CURLOPT_POST, true);
260
        curl_setopt($ch, CURLOPT_URL, $url->getNormalizedWithoutAuth());
261
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
262
        curl_setopt($ch, CURLINFO_HEADER_OUT, true);
263
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
264
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
265
        curl_setopt($ch, CURLOPT_HTTPHEADER, $this->renderHeaders());
266
        
267
        if($this->verifySSL)
268
        {
269
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
270
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
271
        }
272
        
273
        if($url->hasUsername())
274
        {
275
            curl_setopt($ch, CURLOPT_USERNAME, $url->getUsername());
276
            curl_setopt($ch, CURLOPT_PASSWORD, $url->getPassword());
277
        }
278
        
279
        return $ch;
280
    }
281
282
    protected function renderHeaders() : array
283
    {
284
        $result = array();
285
        
286
        foreach($this->headers as $name => $value) {
287
            $result[] = $name.': '.$value;
288
        }
289
        
290
        return $result;
291
    }
292
    
293
   /**
294
    * Retrieves the raw response header, in the form of an indexed
295
    * array containing all response header lines, for example:
296
    */
297
    public function getResponseHeader()
298
    {
299
        return $this->response->getInfo();
0 ignored issues
show
Bug introduced by
The method getInfo() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

299
        return $this->response->/** @scrutinizer ignore-call */ getInfo();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
The method getInfo() does not exist on AppUtils\RequestHelper_Response. Did you maybe mean getInfoKey()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

299
        return $this->response->/** @scrutinizer ignore-call */ getInfo();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
300
    }
301
302
    /**
303
     * After calling the {@link send()} method, this may be used to
304
     * retrieve the response text from the POST request.
305
     *
306
     * @return RequestHelper_Response|NULL
307
     */
308
    public function getResponse() : ?RequestHelper_Response
309
    {
310
        return $this->response;
311
    }
312
    
313
    public function getHeaders()
314
    {
315
        return $this->headers;
316
    }
317
}
318