Completed
Push — master ( 9e6269...55e422 )
by Gareth
04:08
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 1
Bugs 0 Features 0
Metric Value
c 1
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 GuzzleHttp\Client as GuzzleClient;
11
12
/**
13
 * @method get($uri, array $options = [])
14
 * @method head($uri, array $options = [])
15
 * @method put($uri, array $options = [])
16
 * @method post($uri, array $options = [])
17
 * @method patch($uri, array $options = [])
18
 * @method delete($uri, array $options = [])
19
 * @method getAsync($uri, array $options = [])
20
 * @method headAsync($uri, array $options = [])
21
 * @method putAsync($uri, array $options = [])
22
 * @method postAsync($uri, array $options = [])
23
 * @method patchAsync($uri, array $options = [])
24
 * @method deleteAsync($uri, array $options = [])
25
 */
26
class Client
27
{
28
    protected $mode = 'live';
29
30
    protected $callList = [];
31
32
    protected $recordLocation;
33
34
    protected $recordFileName = 'saveState.json';
35
36
    private $shutdownRegistered = false;
37
38
    /**
39
     * @var GuzzleClient
40
     */
41
    private $client;
42
43 32
    public function __construct($options = [])
44
    {
45 32
        $options = array_replace_recursive(
46 32
            ['mode' => null, 'recordLocation' => null, 'recordFileName' => null],
47
            $options
48
        );
49
50 32
        if ($options['mode'] !== null) {
51 32
            $this->mode = $options['mode'];
52
        }
53
54 32
        if ($options['recordLocation'] !== null) {
55 1
            $this->recordLocation = $options['recordLocation'];
56
        }
57
58 32
        if ($options['recordFileName'] !== null) {
59 32
            $this->recordFileName = $options['recordFileName'];
60
        }
61
62 32
        $this->setupClient();
63
    }
64
65 28
    public function __call($method, $args)
66
    {
67 28
        if (count($args) < 1) {
68
            throw new \InvalidArgumentException('Magic request methods require a URI and optional options array');
69
        }
70
71 28
        $uri = $args[0];
72 28
        $opts = isset($args[1]) ? $args[1] : [];
73
74 28
        return substr($method, -5) === 'Async'
75
            ? $this->client->requestAsync(substr($method, 0, -5), $uri, $opts)
76 28
            : $this->client->request($method, $uri, $opts);
77
    }
78
79
    /**
80
     * Get the client for making calls
81
     *
82
     * @return Client
83
     */
84 32
    protected function setupClient()
85
    {
86 32
        if ($this->client !== null) {
87
            return $this->client;
88
        }
89
90 32
        $handler = HandlerStack::create();
91
92 32
        if ($this->mode == 'record') {
93 1
            $history = Middleware::history($this->callList);
94 1
            $handler->push($history);
95 32
        } elseif ($this->mode == 'playback') {
96 31
            $recordings = $this->getRecordings();
97 28
            $mockHandler = new MockHandler($this->arrayToResponses($recordings));
98 28
            $handler = HandlerStack::create($mockHandler);
99
        }
100
101 29
        $this->registerShutdown();
102 29
        $this->client = new GuzzleClient(['handler' => $handler]);
103
104 29
        return $this->client;
105
    }
106
107 32
    protected function getRecordLocation()
108
    {
109 32
        if (!$this->recordLocation) {
110 31
            $dir = __DIR__;
111 31
            $dirPos = strrpos($dir, "src/API");
112 31
            $dir = substr($dir, 0, $dirPos);
113
114 31
            $this->recordLocation = $dir.'Resources/recordings/';
115
        }
116
117 32
        return $this->recordLocation;
118
    }
119
120 32
    protected function getRecordFilePath()
121
    {
122 32
        $path = $this->getRecordLocation() . $this->recordFileName;
123 32
        $path = str_replace("\\", "/", $path);
124
125 32
        return $path;
126
    }
127
128 31
    protected function getRecordings()
129
    {
130 31
        $saveLocation = $this->getRecordFilePath();
131 31
        $records = json_decode(file_get_contents($saveLocation), true);
132
133 28
        return $records;
134
    }
135
136 1
    public function endRecord()
137
    {
138 1
        if ($this->mode != 'record') {
139 1
            return;
140
        }
141
142 1
        $saveList = $this->responsesToArray($this->callList);
143
144 1
        $saveLocation = $this->getRecordFilePath();
145 1
        $folder = pathinfo($saveLocation)['dirname'];
146 1
        if (!is_dir($folder)) {
147 1
            mkdir($folder, 0777, true);
148
        }
149
150 1
        file_put_contents($saveLocation, json_encode($saveList));
151
    }
152
153 29
    protected function registerShutdown()
154
    {
155 29
        if (!$this->shutdownRegistered) {
156 29
            register_shutdown_function(array($this, 'endRecord'));
157 29
            $this->shutdownRegistered = true;
158
        }
159
    }
160
161
    /**
162
     * @param $responses
163
     * @return array
164
     */
165 1
    protected function responsesToArray($responses)
166
    {
167 1
        $array = [];
168 1
        foreach ($responses as $item) {
169
            /** @var Response $response */
170
            $response = $item['response'];
171
172
            if (!isset($item['error'])) {
173
                $save = [
174
                    'error' => false,
175
                    'statusCode' => $response->getStatusCode(),
176
                    'headers' => $response->getHeaders(),
177
                    'body' => $response->getBody()->__toString()
178
                ];
179
            } else {
180
                $save = [
181
                    'error' => true,
182
                    'errorClass' => get_class($item['error']),
183
                    'errorMessage' => $item['error']->getMessage(),
184
                    'request' => [
185
                        'method' => $item['request']->getMethod(),
186
                        'uri' => $item['request']->getUri()->__toString(),
187
                        'headers' => $item['request']->getHeaders(),
188
                        'body' => $item['request']->getBody()->__toString()
189
                    ]
190
                ];
191
            }
192 1
            $array[] = $save;
193
        }
194
195 1
        return $array;
196
    }
197
198
    /**
199
     * @param $items
200
     * @return Response[]
201
     */
202 28
    protected function arrayToResponses($items)
203
    {
204 28
        $mockedResponses = [];
205 28
        foreach ($items as $item) {
206 28
            if (!$item['error']) {
207 28
                $mockedResponses[] = new Response($item['statusCode'], $item['headers'], $item['body']);
208
            } else {
209 2
                $errorClass = $item['errorClass'];
210 2
                $request = new Request(
211 2
                    $item['request']['method'],
212 2
                    $item['request']['uri'],
213 2
                    $item['request']['headers'],
214 2
                    $item['request']['body']
215
                );
216 28
                $mockedResponses[] = new $errorClass($item['errorMessage'], $request);
217
            }
218
        }
219
220 28
        return $mockedResponses;
221
    }
222
}
223