Connector::request()   B
last analyzed

Complexity

Conditions 7
Paths 2

Size

Total Lines 55
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 37
dl 0
loc 55
ccs 0
cts 28
cp 0
rs 8.3946
c 3
b 0
f 0
cc 7
nc 2
nop 3
crap 56

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace LE_ACME2\Connector;
4
5
use LE_ACME2\Request;
6
use LE_ACME2\Response;
7
8
use LE_ACME2\SingletonTrait;
9
use LE_ACME2\Cache;
10
use LE_ACME2\Utilities;
11
use LE_ACME2\Exception;
12
13
class Connector {
14
15
    use SingletonTrait;
16
    
17
    const METHOD_GET = 'GET';
18
    const METHOD_HEAD = 'HEAD';
19
    const METHOD_POST = 'POST';
20
21
    private function __construct() {}
22
23
    protected $_baseURL = 		 'https://acme-v02.api.letsencrypt.org';
24
    protected $_stagingBaseURL = 'https://acme-staging-v02.api.letsencrypt.org';
25
26
    protected $_useStagingServer = true;
27
28
    protected $_delayedResponseTime = 0;
29
30
    public function useStagingServer(bool $useStagingServer) {
31
        $this->_useStagingServer = $useStagingServer;
32
    }
33
34
    public function isUsingStagingServer() : bool {
35
        return $this->_useStagingServer;
36
    }
37
38
    public function getBaseURL() : string {
39
        return $this->_useStagingServer ? $this->_stagingBaseURL : $this->_baseURL;
40
    }
41
42
    /**
43
     * Delay the response to prevent bleaching rate limits
44
     */
45
    public function delayResponse(int $milliSeconds) : void {
46
        $this->_delayedResponseTime = $milliSeconds;
47
    }
48
49
    /**
50
     * Makes a Curl request.
51
     *
52
     * @param string	$method	The HTTP method to use. Accepting GET, POST and HEAD requests.
53
     * @param string 	$url 	The URL to make the request to.
54
     * @param string|null 	$data  	The body to attach to a POST request. Expected as a JSON encoded string.
55
     *
56
     * @throws Exception\InvalidResponse
57
     * @throws Exception\RateLimitReached
58
     * @throws Exception\ServiceUnavailable
59
     */
60
    public function request(string $method, string $url, string $data = null) : RawResponse {
61
62
        Utilities\Event::getInstance()->trigger(Utilities\Event::EVENT_CONNECTOR_WILL_REQUEST, [
0 ignored issues
show
Bug introduced by
It seems like trigger() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

62
        Utilities\Event::getInstance()->/** @scrutinizer ignore-call */ trigger(Utilities\Event::EVENT_CONNECTOR_WILL_REQUEST, [
Loading history...
63
            'method' => $method,
64
            'url' => $url,
65
            'data' => $data,
66
        ]);
67
        Utilities\Logger::getInstance()->add(Utilities\Logger::LEVEL_INFO, 'will request from ' . $url, ['data' => $data]);
0 ignored issues
show
Bug introduced by
It seems like add() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

67
        Utilities\Logger::getInstance()->/** @scrutinizer ignore-call */ add(Utilities\Logger::LEVEL_INFO, 'will request from ' . $url, ['data' => $data]);
Loading history...
68
69
        $handle = curl_init();
70
71
        $headers = array(
72
            'Accept: application/json',
73
            'Content-Type: ' . ($method == self::METHOD_POST ? 'application/jose+json' : 'application/json') //  ACME draft-10, section 6.2
74
        );
75
76
        curl_setopt($handle, CURLOPT_URL, $url);
77
        curl_setopt($handle, CURLOPT_HTTPHEADER, $headers);
78
        curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
79
        curl_setopt($handle, CURLOPT_HEADER, true);
80
81
        switch ($method) {
82
            case self::METHOD_GET:
83
                break;
84
            case self::METHOD_POST:
85
                curl_setopt($handle, CURLOPT_POST, true);
86
                curl_setopt($handle, CURLOPT_POSTFIELDS, $data);
87
                break;
88
            case self::METHOD_HEAD:
89
                curl_setopt($handle, CURLOPT_CUSTOMREQUEST, 'HEAD');
90
                curl_setopt($handle, CURLOPT_NOBODY, true);
91
                break;
92
            default:
93
                throw new \RuntimeException('HTTP request ' . $method . ' not supported.');
94
                break;
95
        }
96
        $response = curl_exec($handle);
97
98
        if($this->_delayedResponseTime > 0) {
99
            usleep($this->_delayedResponseTime * 1000);
100
        }
101
102
        if(curl_errno($handle)) {
103
            throw new \RuntimeException('Curl: ' . curl_error($handle));
104
        }
105
106
        $header_size = curl_getinfo($handle, CURLINFO_HEADER_SIZE);
107
108
        $rawResponse = RawResponse::createFrom($method, $url, $response, $header_size);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $response of LE_ACME2\Connector\RawResponse::createFrom() 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

108
        $rawResponse = RawResponse::createFrom($method, $url, /** @scrutinizer ignore-type */ $response, $header_size);
Loading history...
109
110
        Utilities\Logger::getInstance()->add(Utilities\Logger::LEVEL_INFO, self::class . ': response received', [get_class($rawResponse) => $rawResponse]);
111
112
        $this->_saveNewNonceFrom($rawResponse, $method);
113
114
        return $rawResponse;
115
    }
116
117
    /**
118
     * @throws Exception\InvalidResponse
119
     * @throws Exception\RateLimitReached
120
     * @throws Exception\ServiceUnavailable
121
     */
122
    private function _saveNewNonceFrom(RawResponse $rawResponse, string $method) : void {
123
124
        try {
125
            $getNewNonceResponse = new Response\GetNewNonce($rawResponse);
126
            Cache\NewNonceResponse::getInstance()->set($getNewNonceResponse);
0 ignored issues
show
Bug introduced by
It seems like set() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

126
            Cache\NewNonceResponse::getInstance()->/** @scrutinizer ignore-call */ set($getNewNonceResponse);
Loading history...
127
128
        } catch(Exception\InvalidResponse $e) {
129
130
            if($method == self::METHOD_POST) {
131
                $request = new Request\GetNewNonce();
132
                $getNewNonceResponse = $request->getResponse();
133
                Cache\NewNonceResponse::getInstance()->set($getNewNonceResponse);
134
            }
135
        }
136
    }
137
}