Issues (6)

src/Client.php (1 issue)

1
<?php
2
3
namespace Uon;
4
5
use GuzzleHttp\Client as HttpClient;
6
use GuzzleHttp\Exception\GuzzleException;
7
use GuzzleHttp\RequestOptions;
8
use Psr\Http\Message\ResponseInterface;
9
use Uon\Exceptions\UonEmptyResponseException;
10
use Uon\Exceptions\UonHttpClientException;
11
use Uon\Exceptions\UonTooManyRequests;
12
use Uon\Interfaces\ClientInterface;
13
use function str_replace;
14
use function strtoupper;
15
use function ucwords;
16
17
/**
18
 * @author  Paul Rock <[email protected]>
19
 * @link    http://drteam.rocks
20
 * @license MIT
21
 * @package Uon
22
 *
23
 * @property \Uon\Endpoints\Avia           $avia           Aero transfer
24
 * @property \Uon\Endpoints\Bcard          $bcard          Bonus cards
25
 * @property \Uon\Endpoints\CallHistory    $callHistory    History of calls
26
 * @property \Uon\Endpoints\Cash           $cash           Money operations
27
 * @property \Uon\Endpoints\Catalog        $catalog        Catalog of products
28
 * @property \Uon\Endpoints\Chat           $chat           For work with chat messages
29
 * @property \Uon\Endpoints\Cities         $cities         Cities of countries
30
 * @property \Uon\Endpoints\Countries      $countries      Work with countries
31
 * @property \Uon\Endpoints\Currencies     $currencies     Work with currencies
32
 * @property \Uon\Endpoints\ExtendedFields $extendedFields For manipulation with extended fields
33
 * @property \Uon\Endpoints\Hotels         $hotels         Hotels methods
34
 * @property \Uon\Endpoints\Leads          $leads          Details about clients
35
 * @property \Uon\Endpoints\Mails          $mails          For work with emails
36
 * @property \Uon\Endpoints\Managers       $managers       Get access to list of managers (sale operators)
37
 * @property \Uon\Endpoints\Misc           $misc           Optional single methods
38
 * @property \Uon\Endpoints\Notifications  $notifications  For creating new notifications for managers
39
 * @property \Uon\Endpoints\Nutrition      $nutrition      Some methods about eat
40
 * @property \Uon\Endpoints\Offices        $offices        Get access to list of all offices
41
 * @property \Uon\Endpoints\Payments       $payments       Payment methods
42
 * @property \Uon\Endpoints\ReasonsDeny    $reasonsDeny    List of all deny reasons
43
 * @property \Uon\Endpoints\Reminders      $reminders      Work with reminders
44
 * @property \Uon\Endpoints\Requests       $requests       New requests from people
45
 * @property \Uon\Endpoints\Services       $services       All available services
46
 * @property \Uon\Endpoints\Sources        $sources        All available sources
47
 * @property \Uon\Endpoints\Statuses       $statuses       Request statuses
48
 * @property \Uon\Endpoints\Suppliers      $suppliers      External companies
49
 * @property \Uon\Endpoints\Users          $users          For work with users
50
 * @property \Uon\Endpoints\Visa           $visa           For work with visa statuses
51
 * @property \Uon\Endpoints\Webhooks       $webhooks       Webhooks management
52
 */
53
class Client implements ClientInterface
54
{
55
    /**
56
     * @var string
57
     */
58
    protected $namespace = __NAMESPACE__ . '\\Endpoints';
59
60
    /**
61
     * Initial state of some variables
62
     *
63
     * @var null|\GuzzleHttp\Client
64
     */
65
    public $client;
66
67
    /**
68
     * Object of main config
69
     *
70
     * @var \Uon\Config
71 2
     */
72
    public $config;
73
74 2
    /**
75
     * Type of query
76
     *
77 2
     * @var string
78
     */
79
    protected $type;
80
81
    /**
82 2
     * Endpoint of query
83
     *
84
     * @var string
85
     */
86
    protected $endpoint;
87 2
88
    /**
89
     * Parameters of query
90
     *
91
     * @var array
92 2
     */
93
    protected $params = [];
94
95 2
    /**
96 2
     * @var array
97
     */
98
    protected static $variables = [];
99
100
    /**
101
     * API constructor.
102
     *
103
     * @param array|\Uon\Config $config
104
     * @param bool              $init
105
     *
106
     * @throws \ErrorException
107 61
     */
108
    public function __construct($config, bool $init = true)
109 61
    {
110
        if (!$config instanceof Config) {
111
            $config = new Config($config);
112 61
        }
113 61
114 61
        // Save config into local variable
115
        $this->config = $config;
116
117 61
        // Init if need
118
        if ($init) {
119
            $this->client = $this->initClient($config->guzzle());
120 61
        }
121 60
    }
122
123
    /**
124
     * Get current client instance
125 1
     *
126 1
     * @return null|\GuzzleHttp\Client
127
     */
128
    public function getClient(): ?HttpClient
129 1
    {
130
        return $this->client;
131
    }
132
133
    /**
134
     * Store the client object
135
     *
136
     * @param array $configs
137
     *
138
     * @return \GuzzleHttp\Client
139
     */
140
    public function initClient(array $configs = []): HttpClient
141 61
    {
142
        return new HttpClient($configs);
143
    }
144 61
145
    /**
146
     * Convert underscore_strings to camelCase (medial capitals).
147 61
     *
148
     * @param string $str
149
     *
150
     * @return string
151 61
     */
152
    private function snakeToPascal(string $str): string
153
    {
154
        // Remove underscores, capitalize words, squash, lowercase first.
155 61
        return str_replace(' ', '', ucwords(str_replace('_', ' ', $str)));
156 61
    }
157 1
158 60
    /**
159 60
     * Magic method required for call of another classes
160 60
     *
161 61
     * @param string $name
162
     *
163
     * @return bool|object
164
     */
165
    public function __get(string $name)
166
    {
167
        if (isset(self::$variables[$name])) {
168
            return self::$variables[$name];
169
        }
170
171
        // Set class name as namespace
172
        $class = $this->namespace . '\\' . $this->snakeToPascal($name);
173
174
        // Try to create object by name
175
        return new $class($this->config);
176
    }
177
178
    /**
179
     * Check if class is exist in folder
180
     *
181
     * @param string $name
182
     *
183
     * @return bool
184
     */
185
    public function __isset(string $name): bool
186
    {
187
        return isset(self::$variables[$name]);
188
    }
189
190
    /**
191
     * Ordinary dummy setter, it should be ignored (added to PSR reasons)
192
     *
193
     * @param string $name
194
     * @param mixed  $value
195
     */
196
    public function __set(string $name, $value)
197
    {
198
        self::$variables[$name] = $value;
199
    }
200
201
    /**
202
     * Request executor with timeout and repeat tries
203
     *
204
     * @param string $type   Request method
205
     * @param string $url    endpoint url
206
     * @param array  $params List of parameters
207
     *
208
     * @return \Psr\Http\Message\ResponseInterface|null
209
     *
210
     * @throws \Uon\Exceptions\UonTooManyRequests
211
     * @throws \Uon\Exceptions\UonParameterNotSetException
212
     * @throws \Uon\Exceptions\UonHttpClientException
213
     */
214
    private function repeatRequest(string $type, string $url, array $params = []): ?ResponseInterface
215
    {
216
        $type = strtoupper($type);
217
218
        for ($i = 1; $i <= $this->config->get('tries'); $i++) {
219
220
            $requestEndpoint =
221
                $this->config->get('base_uri') . '/'
222
                . $this->config->get('token') . '/'
223
                . $url . '.' . $this->config->get('format');
224
225
            if ($this->config->get('verbose')) {
226
                var_dump('[' . $type . '] endpoint:' . $requestEndpoint . ' parameters:' . json_encode($params));
0 ignored issues
show
Security Debugging Code introduced by
var_dump('[' . $type . '.... json_encode($params)) looks like debug code. Are you sure you do not want to remove it?
Loading history...
227
            }
228
229
            try {
230
231
                if (empty($params)) {
232
                    // Execute the request to server
233
                    $result = $this->client->request($type, $requestEndpoint);
234
                } else {
235
                    // Execute the request to server
236
                    $result = $this->client->request($type, $requestEndpoint, [RequestOptions::FORM_PARAMS => $params]);
237
                }
238
239
                // Check the code status
240
                $code = $result->getStatusCode();
241
242
                // If success response from server
243
                if ($code === 200 || $code === 201) {
244
                    return $result;
245
                }
246
247
                // If not "too many requests", then probably some bug on remote or our side
248
                if ($code !== 429) {
249
                    throw new UonTooManyRequests();
250
                }
251
252
            } catch (GuzzleException $exception) {
253
                throw new UonHttpClientException($exception);
254
            }
255
256
            // Waiting in seconds
257
            sleep($this->config->get('seconds'));
258
        }
259
260
        // Return false if loop is done but no answer from server
261
        return null;
262
    }
263
264
    /**
265
     * Execute request and return response
266
     *
267
     * @return null|object Array with data or NULL if error
268
     *
269
     * @throws \Uon\Exceptions\UonEmptyResponseException
270
     * @throws \Uon\Exceptions\UonTooManyRequests
271
     * @throws \Uon\Exceptions\UonHttpClientException
272
     */
273
    public function exec()
274
    {
275
        return $this->doRequest($this->type, $this->endpoint, $this->params);
276
    }
277
278
    /**
279
     * Execute query and return RAW response from remote API
280
     *
281
     * @return null|\Psr\Http\Message\ResponseInterface RAW response or NULL if error
282
     *
283
     * @throws \Uon\Exceptions\UonEmptyResponseException
284
     * @throws \Uon\Exceptions\UonTooManyRequests
285
     * @throws \Uon\Exceptions\UonHttpClientException
286
     */
287
    public function raw(): ?ResponseInterface
288
    {
289
        return $this->doRequest($this->type, $this->endpoint, $this->params, true);
290
    }
291
292
    /**
293
     * Make the request and analyze the result
294
     *
295
     * @param string $type            Request method
296
     * @param string $requestEndpoint Api request endpoint
297
     * @param mixed  $params          List of parameters
298
     * @param bool   $raw             Return data in raw format
299
     *
300
     * @return null|object|ResponseInterface Array with data, RAW response or NULL if error
301
     *
302
     * @throws \Uon\Exceptions\UonEmptyResponseException If empty response received from U-On
303
     * @throws \Uon\Exceptions\UonTooManyRequests If amount or repeats is more than allowed
304
     * @throws \Uon\Exceptions\UonHttpClientException If http exception occurred
305
     */
306
    private function doRequest(string $type, string $requestEndpoint, array $params = [], bool $raw = false)
307
    {
308
        // Execute the request to server
309
        $result = $this->repeatRequest($type, $requestEndpoint, $params);
310
311
        // If debug then return Guzzle object
312
        if ($this->config->get('debug') === true) {
313
            return $result;
314
        }
315
316
        if (null === $result) {
317
            throw new UonEmptyResponseException();
318
        }
319
320
        // Return RAW result if required
321
        if ($raw) {
322
            return $result;
323
        }
324
325
        return json_decode($result->getBody(), false);
326
    }
327
328
    /**
329
     * @since 2.0
330
     *
331
     * @return null|object|\Uon\Interfaces\ClientInterface
332
     *
333
     * @throws \Uon\Exceptions\UonEmptyResponseException
334
     * @throws \Uon\Exceptions\UonHttpClientException
335
     * @throws \Uon\Exceptions\UonTooManyRequests
336
     */
337
    protected function done()
338
    {
339
        return $this->config->get('auto_exec') ? $this->exec() : $this;
340
    }
341
}
342