Completed
Push — master ( a7d441...9e9528 )
by Gareth
03:30
created

Client::getRecordFilePath()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 0
crap 1
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 31
        );
56
57 31
        if ($options['mode'] !== null) {
58 31
            $this->mode = $options['mode'];
59 31
        }
60
61 31
        if ($options['recordLocation'] !== null) {
62 1
            $this->recordLocation = $options['recordLocation'];
63 1
        }
64
65 31
        if ($options['recordFileName'] !== null) {
66 31
            $this->recordFileName = $options['recordFileName'];
67 31
        }
68
69 31
        $this->setupClient();
70 31
        $this->registerShutdown();
71 31
    }
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 28
            ? $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
        try {
113
            if ($async) {
114
                return $this->client->requestAsync($method, $uri, $options);
115
            } else {
116
                return $this->client->request($method, $uri, $options);
117
            }
118
        } catch (\Exception $e) {
119
            return $e;
120
        }
121
    }
122
123 28
    protected function doRequest($method, $uri = null, array $options = [], $async = false) {
124 28
        if ($this->mode === self::PLAYBACK) {
125 28
            $response = array_shift($this->callList);
126 28
        } else {
127
            $response = $this->requestWrapper($method, $uri, $options, $async);
128
        }
129
130 28
        if ($this->mode === self::RECORD) {
131
            $this->callList[] = $response;
132
        }
133
134 28
        if ($response instanceof \Exception) {
135 2
            throw $response;
136
        }
137
138 28
        return $response;
139
    }
140
141
    /**
142
     * Get the client for making calls
143
     *
144
     * @return Client
145
     */
146 31
    protected function setupClient()
147
    {
148 31
        if ($this->mode === self::PLAYBACK) {
149 30
            $this->callList = $this->arrayToResponses($this->getRecordings());
150 30
        }
151
152 31
        $this->client = new GuzzleClient();
153 31
    }
154
155 31
    protected function getRecordLocation()
156
    {
157 31
        if (!$this->recordLocation) {
158 30
            $dir = __DIR__;
159 30
            $dirPos = strrpos($dir, "src/API");
160 30
            $dir = substr($dir, 0, $dirPos);
161
162 30
            $this->recordLocation = $dir.'Resources/recordings/';
163 30
        }
164
165 31
        return $this->recordLocation;
166
    }
167
168 31
    protected function getRecordFilePath()
169
    {
170 31
        $path = $this->getRecordLocation() . $this->recordFileName;
171 31
        $path = str_replace("\\", DIRECTORY_SEPARATOR, $path);
172
173 31
        return $path;
174
    }
175
176 30
    protected function getRecordings()
177
    {
178 30
        $saveLocation = $this->getRecordFilePath();
179 30
        $records = json_decode(file_get_contents($saveLocation), true);
180
181 30
        return $records;
182
    }
183
184 1
    public function endRecord()
185
    {
186 1
        if ($this->mode != self::RECORD) {
187 1
            return;
188
        }
189
190 1
        $saveList = $this->responsesToArray($this->callList);
191 1
        $this->mode = self::LIVE;
192
193 1
        $saveLocation = $this->getRecordFilePath();
194 1
        $folder = pathinfo($saveLocation)['dirname'];
195 1
        if (!is_dir($folder)) {
196 1
            mkdir($folder, 0777, true);
197 1
        }
198
199 1
        file_put_contents($saveLocation, json_encode($saveList));
200 1
    }
201
202 31
    protected function registerShutdown()
203
    {
204 31
        if (!$this->shutdownRegistered) {
205 31
            register_shutdown_function(array($this, 'endRecord'));
206 31
            $this->shutdownRegistered = true;
207 31
        }
208 31
    }
209
210
    /**
211
     * @param $responses
212
     * @return array
213
     */
214 1
    protected function responsesToArray($responses)
215
    {
216 1
        $array = [];
217 1
        foreach ($responses as $response) {
218
            /** @var Response $response */
219
220
            if ($response instanceof \Exception) {
221
                $save = [
222
                    'error' => true,
223
                    'errorClass' => get_class($response),
224
                    'errorMessage' => $response->getMessage(),
225
                    'request' => [
226
                        'method' => $response->getRequest()->getMethod(),
227
                        'uri' => $response->getRequest()->getUri()->__toString(),
228
                        'headers' => $response->getRequest()->getHeaders(),
229
                        'body' => $response->getRequest()->getBody()->__toString()
230
                    ]
231
                ];
232
            } else {
233
                $save = [
234
                    'error' => false,
235
                    'statusCode' => $response->getStatusCode(),
236
                    'headers' => $response->getHeaders(),
237
                    'body' => $response->getBody()->__toString()
238
                ];
239
            }
240
            $array[] = $save;
241 1
        }
242
243 1
        return $array;
244
    }
245
246
    /**
247
     * @param $items
248
     * @return Response[]
249
     */
250 30
    protected function arrayToResponses($items)
251
    {
252 30
        $mockedResponses = [];
253 30
        foreach ($items as $item) {
254 28
            if (!$item['error']) {
255 28
                $mockedResponses[] = new Response($item['statusCode'], $item['headers'], $item['body']);
256 28
            } else {
257 2
                $errorClass = $item['errorClass'];
258 2
                $request = new Request(
259 2
                    $item['request']['method'],
260 2
                    $item['request']['uri'],
261 2
                    $item['request']['headers'],
262 2
                    $item['request']['body']
263 2
                );
264 2
                $mockedResponses[] = new $errorClass($item['errorMessage'], $request);
265
            }
266 30
        }
267
268 30
        return $mockedResponses;
269
    }
270
}
271