Completed
Push — master ( 84b68e...1d2060 )
by Lawrence
01:47
created

Client::__call()   C

Complexity

Conditions 10
Paths 10

Size

Total Lines 64
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 10

Importance

Changes 18
Bugs 2 Features 1
Metric Value
c 18
b 2
f 1
dl 0
loc 64
ccs 31
cts 31
cp 1
rs 6.309
cc 10
eloc 30
nc 10
nop 2
crap 10

How to fix   Long Method    Complexity   

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 Plinker\Core;
4
5
use Requests;
6
7
/**
8
 * Plinker Client
9
 */
10
class Client
11
{
12
    private $endpoint;
13
    private $component;
14
    private $publicKey;
15
    private $privateKey;
16
    private $config;
17
    private $encrypt;
18
    private $response;
19
    private $signer;
20
21
    /**
22
     * @param string $endpoint
23
     * @param string $component
24
     * @param string $publicKey
25
     * @param string $privateKey
26
     * @param array  $config
27
     * @param bool   $encrypt
28
     */
29 8
    public function __construct(
30
        $endpoint,
31
        $component,
32
        $publicKey = '',
33
        $privateKey = '',
34
        $config = [],
35
        $encrypt = true
36
    ) {
37
        // define vars
38 8
        $this->endpoint = $endpoint;
39 8
        $this->component = $component;
40 8
        $this->publicKey = hash('sha256', gmdate('h').$publicKey);
41 8
        $this->privateKey = hash('sha256', gmdate('h').$privateKey);
42 8
        $this->config = $config;
43 8
        $this->encrypt = $encrypt;
44 8
        $this->response = null;
45
46
        // init signer
47 8
        $this->signer = new Signer($this->publicKey, $this->privateKey, $this->encrypt);
48 8
    }
49
50
    /**
51
     * Helper which changes the server component on the fly without changing
52
     * the connection.
53
     *
54
     * @param string $component - component class namespace
55
     * @param array  $config    - component array
56
     */
57 1
    public function useComponent($component = '', $config = [], $encrypt = true)
58
    {
59 1
        $this->component = $component;
60 1
        $this->config = $config;
61 1
        $this->encrypt = $encrypt;
62
63 1
        return new $this(
64 1
            $this->endpoint,
65 1
            $this->component,
66 1
            $this->publicKey,
67 1
            $this->privateKey,
68 1
            $this->config,
69 1
            $this->encrypt
70
        );
71
    }
72
    
73
    /**
74
     * Call endpoint
75
     * 
76
     * @param string $encoded
77
     * @param array  $params
78
     * @return mixed
79
     */
80 4
    final private function callEndpoint($encoded, $params = [])
81
    {
82
        // testing
83 4
        if (getenv('APP_ENV') === 'testing') {
84
            // good
85 4
            $response = new \stdClass();
86 4
            $response->body = serialize($this->signer->encode([
87 4
                'response' => $params,
88
            ]));
89
            
90
            // fail
91 4
            if (getenv('TEST_CONDITION') === 'http_empty_response') {
92 1
                $response->raw = 'Testing fail';
93 1
                $response->body = null;
94
            }
95
            
96
            // fail
97 4
            if (getenv('TEST_CONDITION') === 'http_invalid_response') {
98 1
                $response->raw = 'Testing fail';
99 4
                $response->body = 'Invalid text response';
100
            }
101
        } 
102
        // normal request, store in response
103
        else {
104
            // @codeCoverageIgnoreStart
105
            $response = Requests::post(
106
                $this->endpoint,
107
                [
108
                    // send plinker header
109
                    'plinker' => true,
110
                    // sign token generated from encoded packet, send as header
111
                    'token'   => hash_hmac('sha256', $encoded['token'], $this->privateKey),
112
                ],
113
                $encoded,
114
                [
115
                    'timeout' => (!empty($this->config['timeout']) ? (int) $this->config['timeout'] : 60),
116
                ]
117
            );
118
            // @codeCoverageIgnoreEnd
119
        }
120
        
121 4
        return $response;
122
    }
123
    
124
    /**
125
     * Decode response
126
     * 
127
     * @param string $encoded
128
     * @param array  $params
129
     * @return mixed
130
     */
131 4
    final private function decodeResponse($response)
132
    {
133
        // check response is a serialized string
134 4
        if (@unserialize($response->body) === false) {
135 1
            if (empty($response->body)) {
136 1
                $message = $response->raw;
137
            } else {
138
                // @codeCoverageIgnoreStart
139
                $message = $response->body;
140
                // @codeCoverageIgnoreEnd
141
            }
142
143 1
            throw new \Exception('Could not unserialize response: '.$message);
144
        }
145
146
        // initial unserialize response body
147 4
        $response->body = unserialize($response->body);
148
        
149
        // testing - http_slow_response
150 4
        if (getenv('APP_ENV') === 'testing') {
151
            // fail
152 4
            if (getenv('TEST_CONDITION') === 'http_slow_packet_response') {
153 1
                $response->body['time'] = $response->body['time']-5;
154
            }
155
        }
156
157
        // decode response
158 4
        $return  = $this->signer->decode(
159 4
            $response->body
160
        );
161
        
162
        // testing - http_slow_response
163 4
        if (getenv('APP_ENV') === 'testing') {
164
            // fail
165 4
            if (getenv('TEST_CONDITION') === 'http_slow_data_response') {
166 1
                $return['time'] = $return['time']-5;
167
            }
168
            
169
            // fail
170 4
            if (getenv('TEST_CONDITION') === 'data_empty_response') {
171 1
                $return['response'] = '';
172
            }  
173
            
174
            // fail
175 4
            if (getenv('TEST_CONDITION') === 'data_invalid_response') {
176 1
                $return['response'] = 'Response not serialized';
177
            }   
178
            
179
            // fail
180 4
            if (getenv('TEST_CONDITION') === 'data_error_response') {
181 1
                $return['response'] = serialize(['error' => 'Error from component']);
182
            }
183
        }
184
185 4
        return $return;
186
    }
187
188
    /**
189
     * Magic caller.
190
     *
191
     * @param string $action
192
     * @param array  $params
193
     * @return mixed
194
     */
195 5
    public function __call($action, $params)
196
    {
197 5
        if (!is_scalar($action)) {
198 1
            throw new \InvalidArgumentException('Method name has no scalar value');
199
        }
200
201 5
        if (!is_array($params)) {
202 1
            throw new \InvalidArgumentException('Arguments must be given as array');
203
        }
204
205
        // change arguments array into numeric indexed
206 4
        $params = array_values($params);
207
208
        // unset local private key
209 4
        unset($this->config['plinker']['private_key']);
210
211
        // encode payload
212 4
        $encoded = $this->signer->encode([
213 4
            'time'      => microtime(true),
214 4
            'self'      => $this->endpoint,
215 4
            'component' => $this->component,
216 4
            'config'    => $this->config,
217 4
            'action'    => $action,
218 4
            'params'    => $params,
219
        ]);
220
221
        // call endpoint
222 4
        $this->response = $this->callEndpoint($encoded, $params);
223
224
        // decode response
225 4
        $response = $this->decodeResponse($this->response);
226
227
        // verify response packet timing validity
228 4
        $response['packet_time'] = microtime(true) - $this->response->body['time'];
229 4
        if ($response['packet_time'] >= 1) {
230 1
            throw new \Exception('Response timing packet check failed');
231
        }
232
233
        // verify data timing validity
234 3
        $response['data_time'] = (microtime(true) - $response['time']);
235 3
        if ($response['data_time'] >= 1) {
236 1
            throw new \Exception('Response timing data check failed');
237
        }
238
239
        // decode response data
240 3
        if (is_string($response['response'])) {
241
            // empty data response
242 3
            if (empty($response['response'])) {
243 1
                return '';
244
            }
245
            // response should be a serialized string
246 3
            if (@unserialize($response['response']) === false) {
247 1
                throw new \Exception('Could not unserialize response: '.$response['response']);
248
            }
249 2
            $response['response'] = unserialize($response['response']);
250
        }
251
252
        // check for errors
253 2
        if (is_array($response['response']) && !empty($response['response']['error'])) {
254 1
            throw new \Exception(ucfirst($response['response']['error']));
255
        }
256
257
        // unserialize data
258 1
        return $response['response'];
259
    }
260
}
261