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;
21
    protected $tz_dst;
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
            if(!empty($this->tz_offset) && empty($this->tz_dst))
55
            {
56
                $this->httpClient = new Client(['headers' => ['X-TimeZoneOffset' => $this->tz_offset, 'X-TimeZoneDST' => $this->tz_dst]]);
57
            }
58
59
            $this->httpClient = new Client();
60
        }
61
62
        return $this->httpClient;
63
    }
64
65
    protected function section($section)
66
    {
67
        $verb       = $this->config(sprintf("routes.%s.method", $section));
68
        $path       = sprintf("%s%s", $this->getBaseUrl(), $this->config(sprintf("routes.%s.path", $section)));
69
        $verify     = $this->config(sprintf("routes.%s.verify", $section), false);
70
71
        return [
72
            'method' => $verb,
73
            'path' => $path,
74
            'verify' => $verify
75
        ];
76
    }
77
78
    protected function config($path, $default = null)
79
    {
80
        return $this->dotNotationFromArray($this->config, $path, $default);
81
    }
82
83
    /**
84
     * @param array $options
85
     * @return array
86
     */
87
    protected function options(array $options)
88
    {
89
        return array_merge($this->config('options', []), $options);
90
    }
91
92
    /**
93
     * @return string
94
     */
95
    protected function getBaseUrl()
96
    {
97
        return $this->baseUrl;
98
    }
99
100
    /**
101
     * @param callable $callback
102
     * @param $nextException
103
     * @param callable|null $afterException
104
     * @return mixed
105
     */
106
    protected function remote(callable $callback, $nextException = UserDirectoryException::class, callable $afterException = null)
107
    {
108
        try
109
        {
110
            return $callback();
111
        }
112
        catch(\Exception $e)
113
        {
114
            // Useful for error logging
115
            if(is_callable($afterException))
116
            {
117
                $afterException($e);
118
            }
119
120
            $this->handleException($e, $nextException);
121
        }
122
    }
123
124
    /**
125
     * @param $cfg_section
126
     * @param array|null $form_params
127
     * @param array|null $path_params
128
     * @param array|null $get_params
129
     * @return mixed|ResponseInterface
130
     */
131
    protected function request($cfg_section, array $form_params = null, array $path_params = null, array $get_params = null)
132
    {
133
        $client = $this->getHttpClient();
134
        $section = $this->section($cfg_section);
135
136
        $path = !empty($path_params) ? vsprintf($section['path'], $path_params) : $section['path'];
137
138
        $options = array_merge($this->config['options'], ['verify' => $section['verify']]);
139
140
        if(!empty($form_params))
141
        {
142
            $options['form_params'] = $form_params;
143
        }
144
145
        if(!empty($get_params))
146
        {
147
            $options['query'] = $get_params;
148
        }
149
150
        return $client->request($section['method'], $path, $options);
151
    }
152
153
    /**
154
     * @param array ...$params
155
     * @return array
156
     * @throws JsonDecodingException
157
     */
158
    protected function json(...$params): array
159
    {
160
        $result = $this->request(...$params);
161
162
        $json = @json_decode($result->getBody(), true);
163
164
        if(json_last_error() !== JSON_ERROR_NONE)
165
        {
166
            throw new JsonDecodingException(json_last_error_msg(), json_last_error());
167
        }
168
169
        return $json;
170
    }
171
172
    /**
173
     * @param RequestException $e
174
     * @param $nextException
175
     * @throws TransferException
176
     * @throws UserDirectoryException
177
     */
178
    protected function handleException(\Exception $e, $nextException)
179
    {
180
        if(method_exists($e, 'getResponse'))
181
        {
182
            $response = $e->getResponse();
183
184
            if($this->isJson($response))
185
            {
186
                $body = @json_decode($e->getResponse()->getBody(), true);
187
188
                $message = isset($body['message']) ? $body['message'] : $e->getResponse()->getBody()->getContents();
189
190
                throw new $nextException($message, (int)$e->getCode());
191
            }
192
193
            throw new TransferException($e->getResponse()->getBody()->getContents(), $e->getCode());
194
        }
195
196
        throw new UserDirectoryException($e->getMessage(), $e->getCode());
197
    }
198
199
    /**
200
     * @param ResponseInterface $response
201
     * @return bool
202
     */
203
    protected function isJson(ResponseInterface $response)
204
    {
205
        $result = false;
206
207
        if($response->hasHeader('content-type'))
208
        {
209
            $contentType = $response->getHeader('content-type');
210
211
            if(is_string($contentType))
212
            {
213
                $result = 0 === strcmp($contentType, 'application/json');
214
            }
215
216
            if(is_array($contentType))
217
            {
218
                foreach($contentType as $ctype)
219
                {
220
                    $result = 0 === strcmp($ctype, 'application/json');
221
222
                    if($result)
223
                        break;
224
                }
225
            }
226
        }
227
228
        return $result;
229
    }
230
}