Completed
Push — master ( 9e9528...25f4f6 )
by Gareth
04:01
created

Client::__call()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4.25

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 13
ccs 6
cts 8
cp 0.75
rs 9.2
cc 4
eloc 8
nc 5
nop 2
crap 4.25
1
<?php
2
3
namespace garethp\ews\HttpPlayback;
4
5
use GuzzleHttp\HandlerStack;
6
use GuzzleHttp\Middleware;
7
use GuzzleHttp\Handler\MockHandler;
8
use GuzzleHttp\Psr7\Request;
9
use GuzzleHttp\Psr7\Response;
10
use Psr\Http\Message\ResponseInterface;
11
use GuzzleHttp\Client as GuzzleClient;
12
13
/**
14
 * @method ResponseInterface get($uri, array $options = [])
15
 * @method ResponseInterface head($uri, array $options = [])
16
 * @method ResponseInterface put($uri, array $options = [])
17
 * @method ResponseInterface post($uri, array $options = [])
18
 * @method ResponseInterface patch($uri, array $options = [])
19
 * @method ResponseInterface delete($uri, array $options = [])
20
 * @method ResponseInterface getAsync($uri, array $options = [])
21
 * @method ResponseInterface headAsync($uri, array $options = [])
22
 * @method ResponseInterface putAsync($uri, array $options = [])
23
 * @method ResponseInterface postAsync($uri, array $options = [])
24
 * @method ResponseInterface patchAsync($uri, array $options = [])
25
 * @method ResponseInterface deleteAsync($uri, array $options = [])
26
 */
27
class Client
28
{
29
    const LIVE = 'live';
30
31
    const RECORD = 'record';
32
33
    const PLAYBACK = 'playback';
34
35
    protected $mode = 'live';
36
37
    protected $callList = [];
38
39
    protected $recordLocation;
40
41
    protected $recordFileName = 'saveState.json';
42
43
    private $shutdownRegistered = false;
44
45
    /**
46
     * @var GuzzleClient
47
     */
48
    private $client;
49
50 31
    public function __construct($options = [])
51
    {
52 31
        $options = array_replace_recursive(
53 31
            ['mode' => null, 'recordLocation' => null, 'recordFileName' => null],
54
            $options
55
        );
56
57 31
        if ($options['mode'] !== null) {
58 31
            $this->mode = $options['mode'];
59
        }
60
61 31
        if ($options['recordLocation'] !== null) {
62 1
            $this->recordLocation = $options['recordLocation'];
63
        }
64
65 31
        if ($options['recordFileName'] !== null) {
66 31
            $this->recordFileName = $options['recordFileName'];
67
        }
68
69 31
        $this->setupClient();
70 31
        $this->registerShutdown();
71
    }
72
73 28
    public function __call($method, $args)
74
    {
75 28
        if (count($args) < 1) {
76
            throw new \InvalidArgumentException('Magic request methods require a URI and optional options array');
77
        }
78
79 28
        $uri = $args[0];
80 28
        $opts = isset($args[1]) ? $args[1] : [];
81
82 28
        return substr($method, -5) === 'Async'
83
            ? $this->requestAsync(substr($method, 0, -5), $uri, $opts)
84 28
            : $this->request($method, $uri, $opts);
85
    }
86
87
    /**
88
     * @param $method
89
     * @param null $uri
90
     * @param array $options
91
     *
92
     * @return ResponseInterface
93
     */
94 28
    public function request($method, $uri = null, array $options = [])
95
    {
96 28
        return $this->doRequest($method, $uri, $options);
97
    }
98
99
    /**
100
     * @param $method
101
     * @param null $uri
102
     * @param array $options
103
     *
104
     * @return ResponseInterface
105
     */
106
    public function requestAsync($method, $uri = null, array $options = [])
107
    {
108
        return $this->doRequest($method, $uri, $options, true);
109
    }
110
111
    protected function requestWrapper($method, $uri = null, array $options = [], $async = false)
112
    {
113
        try {
114
            if ($async) {
115
                return $this->client->requestAsync($method, $uri, $options);
116
            } else {
117
                return $this->client->request($method, $uri, $options);
118
            }
119
        } catch (\Exception $e) {
120
            return $e;
121
        }
122
    }
123
124 28
    protected function doRequest($method, $uri = null, array $options = [], $async = false)
125
    {
126 28
        if ($this->mode === self::PLAYBACK) {
127 28
            $response = array_shift($this->callList);
128
        } else {
129
            $response = $this->requestWrapper($method, $uri, $options, $async);
130
        }
131
132 28
        if ($this->mode === self::RECORD) {
133
            $this->callList[] = $response;
134
        }
135
136 28
        if ($response instanceof \Exception) {
137 2
            throw $response;
138
        }
139
140 28
        return $response;
141
    }
142
143
    /**
144
     * Get the client for making calls
145
     *
146
     * @return Client
147
     */
148 31
    protected function setupClient()
149
    {
150 31
        if ($this->mode === self::PLAYBACK) {
151 30
            $this->callList = $this->arrayToResponses($this->getRecordings());
152
        }
153
154 31
        $this->client = new GuzzleClient();
155
    }
156
157 31
    protected function getRecordLocation()
158
    {
159 31
        if (!$this->recordLocation) {
160 30
            $dir = __DIR__;
161 30
            $dirPos = strrpos($dir, "src/API");
162 30
            $dir = substr($dir, 0, $dirPos);
163
164 30
            $this->recordLocation = $dir.'Resources/recordings/';
165
        }
166
167 31
        return $this->recordLocation;
168
    }
169
170 31
    protected function getRecordFilePath()
171
    {
172 31
        $path = $this->getRecordLocation() . $this->recordFileName;
173 31
        $path = str_replace("\\", DIRECTORY_SEPARATOR, $path);
174
175 31
        return $path;
176
    }
177
178 30
    protected function getRecordings()
179
    {
180 30
        $saveLocation = $this->getRecordFilePath();
181 30
        $records = json_decode(file_get_contents($saveLocation), true);
182
183 30
        return $records;
184
    }
185
186 1
    public function endRecord()
187
    {
188 1
        if ($this->mode != self::RECORD) {
189 1
            return;
190
        }
191
192 1
        $saveList = $this->responsesToArray($this->callList);
193 1
        $this->mode = self::LIVE;
194
195 1
        $saveLocation = $this->getRecordFilePath();
196 1
        $folder = pathinfo($saveLocation)['dirname'];
197 1
        if (!is_dir($folder)) {
198 1
            mkdir($folder, 0777, true);
199
        }
200
201 1
        file_put_contents($saveLocation, json_encode($saveList));
202
    }
203
204 31
    protected function registerShutdown()
205
    {
206 31
        if (!$this->shutdownRegistered) {
207 31
            register_shutdown_function(array($this, 'endRecord'));
208 31
            $this->shutdownRegistered = true;
209
        }
210
    }
211
212
    /**
213
     * @param $responses
214
     * @return array
215
     */
216 1
    protected function responsesToArray($responses)
217
    {
218 1
        $array = [];
219 1
        foreach ($responses as $response) {
220
            /** @var Response $response */
221
222
            if ($response instanceof \Exception) {
223
                $save = [
224
                    'error' => true,
225
                    'errorClass' => get_class($response),
226
                    'errorMessage' => $response->getMessage(),
227
                    'request' => [
228
                        'method' => $response->getRequest()->getMethod(),
229
                        'uri' => $response->getRequest()->getUri()->__toString(),
230
                        'headers' => $response->getRequest()->getHeaders(),
231
                        'body' => $response->getRequest()->getBody()->__toString()
232
                    ]
233
                ];
234
            } else {
235
                $save = [
236
                    'error' => false,
237
                    'statusCode' => $response->getStatusCode(),
238
                    'headers' => $response->getHeaders(),
239
                    'body' => $response->getBody()->__toString()
240
                ];
241
            }
242 1
            $array[] = $save;
243
        }
244
245 1
        return $array;
246
    }
247
248
    /**
249
     * @param $items
250
     * @return Response[]
251
     */
252 30
    protected function arrayToResponses($items)
253
    {
254 30
        $mockedResponses = [];
255 30
        foreach ($items as $item) {
256 28
            if (!$item['error']) {
257 28
                $mockedResponses[] = new Response($item['statusCode'], $item['headers'], $item['body']);
258
            } else {
259 2
                $errorClass = $item['errorClass'];
260 2
                $request = new Request(
261 2
                    $item['request']['method'],
262 2
                    $item['request']['uri'],
263 2
                    $item['request']['headers'],
264 2
                    $item['request']['body']
265
                );
266 30
                $mockedResponses[] = new $errorClass($item['errorMessage'], $request);
267
            }
268
        }
269
270 30
        return $mockedResponses;
271
    }
272
}
273