Methods

Source

LineCode
1
<?php
2
3
namespace AuthStack\UserDirectory\Service;
4
5
use AuthStack\UserDirectory\Exceptions\JsonDecodingException;
6
use AuthStack\UserDirectory\Exceptions\TransferException;
7
use AuthStack\UserDirectory\Exceptions\UserDirectoryException;
8
use Buckhill\Support\Traits\DotNotationAccessorTrait;
9
use GuzzleHttp\Client;
10
use Psr\Http\Message\ResponseInterface;
11
use GuzzleHttp\Exception\RequestException;
12
13
abstract class RESTService
14
{
15
    use DotNotationAccessorTrait;
16
17
    protected $httpClient;
18
    protected $baseUrl;
19
    protected $config = [];
20
    protected $tz_offset = null;
21
    protected $tz_dst = null;
22
23
    public function __construct($baseUrl = '', array $options = [], array $routes = [], string $tz_offset = null, string $tz_dst = null)
24
    {
25
        $this->baseUrl = $baseUrl;
26
27
        $this->config = [
28
            'options' => $options,
29
            'routes' => $routes
30
        ];
31
32
        $this->tz_offset = $tz_offset;
33
        $this->tz_dst = $tz_dst;
34
    }
35
36
    public function setTimeZoneOffset(string $offset): self
37
    {
38
        $this->tz_offset = $offset;
39
40
        return $this;
41
    }
42
43
    public function setTimeZoneDST(string $dst): self
44
    {
45
        $this->tz_dst = $dst;
46
47
        return $this;
48
    }
49
50
    public function getHttpClient(): Client
51
    {
52
        if(!$this->httpClient instanceof Client)
53
        {
54
            $this->httpClient = new Client();
55
        }
56
57
        return $this->httpClient;
58
    }
59
60
    protected function section($section)
61
    {
62
        $verb       = $this->config(sprintf("routes.%s.method", $section));
63
        $path       = sprintf("%s%s", $this->getBaseUrl(), $this->config(sprintf("routes.%s.path", $section)));
64
        $verify     = $this->config(sprintf("routes.%s.verify", $section), false);
65
66
        return [
67
            'method' => $verb,
68
            'path' => $path,
69
            'verify' => $verify
70
        ];
71
    }
72
73
    protected function config($path, $default = null)
74
    {
75
        return $this->dotNotationFromArray($this->config, $path, $default);
76
    }
77
78
    /**
79
     * @param array $options
80
     * @return array
81
     */
82
    protected function options(array $options)
83
    {
84
        return array_merge($this->config('options', []), $options);
85
    }
86
87
    /**
88
     * @return string
89
     */
90
    protected function getBaseUrl()
91
    {
92
        return $this->baseUrl;
93
    }
94
95
    /**
96
     * @param callable $callback
97
     * @param $nextException
98
     * @param callable|null $afterException
99
     * @return mixed
100
     */
101
    protected function remote(callable $callback, $nextException = UserDirectoryException::class, callable $afterException = null)
102
    {
103
        try
104
        {
105
            return $callback();
106
        }
107
        catch(\Exception $e)
108
        {
109
            // Useful for error logging
110
            if(is_callable($afterException))
111
            {
112
                $afterException($e);
113
            }
114
115
            $this->handleException($e, $nextException);
116
        }
117
    }
118
119
    /**
120
     * @param $cfg_section
121
     * @param array|null $form_params
122
     * @param array|null $path_params
123
     * @param array|null $get_params
124
     * @return mixed|ResponseInterface
125
     */
126
    protected function request($cfg_section, array $form_params = null, array $path_params = null, array $get_params = null)
127
    {
128
        $client = $this->getHttpClient();
129
        $section = $this->section($cfg_section);
130
131
        $path = !empty($path_params) ? vsprintf($section['path'], $path_params) : $section['path'];
132
133
        $options = array_merge($this->config['options'], ['verify' => $section['verify']]);
134
135
        // Add time zone headers
136
        if(!is_null($this->tz_offset) && !is_null($this->tz_dst))
137
        {
138
            if(!isset($options['headers'])) $options['headers'] = [];
139
140
            $options['headers']['X-TimeZoneOffset'] = $this->tz_offset;
141
            $options['headers']['X-TimeZoneDST'] = $this->tz_dst;
142
        }
143
144
        if(!empty($form_params))
145
        {
146
            $options['form_params'] = $form_params;
147
        }
148
149
        if(!empty($get_params))
150
        {
151
            $options['query'] = $get_params;
152
        }
153
154
        return $client->request($section['method'], $path, $options);
155
    }
156
157
    /**
158
     * @param array ...$params
159
     * @return array
160
     * @throws JsonDecodingException
161
     */
162
    protected function json(...$params): array
163
    {
164
        $result = $this->request(...$params);
165
166
        $json = @json_decode($result->getBody(), true);
167
168
        if(json_last_error() !== JSON_ERROR_NONE)
169
        {
170
            throw new JsonDecodingException(json_last_error_msg(), json_last_error());
171
        }
172
173
        return $json;
174
    }
175
176
    /**
177
     * @param RequestException $e
178
     * @param $nextException
179
     * @throws TransferException
180
     * @throws UserDirectoryException
181
     */
182
    protected function handleException(\Exception $e, $nextException)
183
    {
184
        if(method_exists($e, 'getResponse'))
185
        {
186
            $response = $e->getResponse();
187
188
            if($this->isJson($response))
189
            {
190
                $body = @json_decode($e->getResponse()->getBody(), true);
191
192
                $message = isset($body) && is_array($body) ? $body : $e->getResponse()->getBody()->getContents();
193
194
                throw new $nextException($message, (int)$e->getCode());
195
            }
196
197
            throw new TransferException($e->getResponse()->getBody()->getContents(), $e->getCode());
198
        }
199
200
        throw new UserDirectoryException($e->getMessage(), $e->getCode());
201
    }
202
203
    /**
204
     * @param ResponseInterface $response
205
     * @return bool
206
     */
207
    protected function isJson(ResponseInterface $response)
208
    {
209
        $result = false;
210
211
        if($response->hasHeader('content-type'))
212
        {
213
            $contentType = $response->getHeader('content-type');
214
215
            if(is_string($contentType))
216
            {
217
                $result = 0 === strcmp($contentType, 'application/json');
218
            }
219
220
            if(is_array($contentType))
221
            {
222
                foreach($contentType as $ctype)
223
                {
224
                    $result = 0 === strcmp($ctype, 'application/json');
225
226
                    if($result)
227
                        break;
228
                }
229
            }
230
        }
231
232
        return $result;
233
    }
234
}