Completed
Push — master ( d32056...f6a2f3 )
by Vuong
03:08
created

BaseGateway   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 298
Duplicated Lines 0 %

Test Coverage

Coverage 80.95%

Importance

Changes 0
Metric Value
dl 0
loc 298
ccs 68
cts 84
cp 0.8095
rs 10
c 0
b 0
f 0
wmc 29

14 Methods

Rating   Name   Duplication   Size   Complexity  
A getDefaultClient() 0 13 3
A beforeRequest() 0 3 1
A getHttpClientConfig() 0 3 1
B request() 0 26 2
A setDefaultClient() 0 7 1
A getHttpClient() 0 13 3
B getClient() 0 22 6
A requestCommands() 0 12 3
A setClients() 0 7 2
A getVersion() 0 3 1
A hasClient() 0 3 1
A getClients() 0 8 2
A afterRequest() 0 3 1
A setClient() 0 9 2
1
<?php
2
/**
3
 * @link https://github.com/vuongxuongminh/yii2-gatewayclients
4
 * @copyright Copyright (c) 2018 Vuong Xuong Minh
5
 * @license [New BSD License](http://www.opensource.org/licenses/bsd-license.php)
6
 */
7
8
namespace vxm\gatewayclients;
9
10
use Yii;
11
use ReflectionClass;
12
13
use yii\base\Component;
14
use yii\base\InvalidArgumentException;
15
use yii\base\InvalidConfigException;
16
use yii\helpers\ArrayHelper;
17
use yii\httpclient\Client as HttpClient;
18
19
20
/**
21
 * Class BaseClient a base class that implements the [[GatewayInterface]].
22
 * It is an abstraction layer, implements classes will be add more properties to support create request to gateway server api.
23
 *
24
 * @property BaseClient $defaultClient
25
 * @property BaseClient $client
26
 *
27
 * @author Vuong Minh <[email protected]>
28
 * @since 1.0
29
 */
30
abstract class BaseGateway extends Component implements GatewayInterface
31
{
32
33
    /**
34
     * @event RequestEvent an event that is triggered at the beginning of request to gateway server api.
35
     */
36
    const EVENT_BEFORE_REQUEST = 'beforeRequest';
37
38
    /**
39
     * @event RequestEvent an event that is triggered at the end of requested to gateway server api.
40
     */
41
    const EVENT_AFTER_REQUEST = 'afterRequest';
42
43
    /**
44
     * @var array config use for setup properties of all request data to send to gateway server api. It called by [[request()]].
45
     */
46
    public $requestDataConfig;
47
48
    /**
49
     * @var array config use for setup properties of all response data get from gateway server api. It called by [[request()]].
50
     */
51
    public $responseDataConfig;
52
53
    /**
54
     * @var array config of client use for setup properties of the clients list.
55
     */
56
    public $clientConfig = [];
57
58
    /**
59
     * The clients list.
60
     *
61
     * @var array|BaseClient[]
62
     */
63
    private $_clients = [];
64
65
    /**
66
     * @inheritdoc
67
     */
68
    public function getVersion(): string
69
    {
70
        return '1.0';
71
    }
72
73
    /**
74
     * @inheritdoc
75
     * @throws InvalidConfigException
76
     */
77
    public function getClients(): array
78
    {
79
        $clients = [];
80
        foreach ($this->_clients as $id => $client) {
81
            $clients[$id] = $this->getClient($id);
82
        }
83
84
        return $clients;
85
    }
86
87
    /**
88
     * @inheritdoc
89
     * @throws InvalidConfigException
90
     */
91 2
    public function setClients(array $clients): bool
92
    {
93 2
        foreach ($clients as $id => $client) {
94 2
            $this->setClient($id, $client);
95
        }
96
97 2
        return true;
98
    }
99
100
    /**
101
     * @inheritdoc
102
     * @throws InvalidConfigException|InvalidArgumentException
103
     */
104 14
    public function getClient($id = null): ClientInterface
105
    {
106 14
        if ($id === null) {
107 12
            return $this->getDefaultClient();
108 11
        } elseif ($this->hasClient($id)) {
109 11
            $client = $this->_clients[$id];
110
111 11
            if (is_string($client)) {
112
                $client = ['class' => $client];
113
            }
114
115 11
            if (is_array($client)) {
116 10
                $client = ArrayHelper::merge($this->clientConfig, $client);
117
            }
118
119 11
            if (!$client instanceof BaseClient) {
120 10
                $client = $this->_clients[$id] = Yii::createObject($client, [$this]);
121
            }
122
123 11
            return $client;
124
        } else {
125
            throw new InvalidArgumentException("An id client: `$id` not exist!");
126
        }
127
    }
128
129
    /**
130
     * @var BaseClient|ClientInterface The default client value.
131
     */
132
    private $_defaultClient;
133
134
    /**
135
     * @inheritdoc
136
     * @return BaseClient|ClientInterface
137
     * @throws InvalidConfigException
138
     */
139 12
    public function getDefaultClient(): ClientInterface
140
    {
141 12
        if ($this->_defaultClient === null) {
142 12
            if (!empty($this->_clients)) {
143 9
                $ids = array_keys($this->_clients);
144 9
                $id = array_pop($ids);
145
146 9
                return $this->_defaultClient = $this->getClient($id);
147
            } else {
148 3
                throw new InvalidConfigException('Can not detect default client from an empty client lists!');
149
            }
150
        } else {
151 9
            return $this->_defaultClient;
152
        }
153
    }
154
155
    /**
156
     * @inheritdoc
157
     * @throws InvalidConfigException
158
     */
159 11
    public function setClient($id, $client = null): bool
160
    {
161 11
        if ($client === null) {
162 9
            $this->setDefaultClient($id);
163
        } else {
164 6
            $this->_clients[$id] = $client;
165
        }
166
167 11
        return true;
168
    }
169
170
    /**
171
     * @inheritdoc
172
     * @throws InvalidConfigException
173
     */
174 9
    public function setDefaultClient($client): bool
175
    {
176 9
        array_push($this->_clients, $client);
177 9
        $this->_defaultClient = null;
178 9
        $this->getDefaultClient();
179
180 9
        return true;
181
    }
182
183
    /**
184
     * @inheritdoc
185
     */
186 11
    public function hasClient($id): bool
187
    {
188 11
        return array_key_exists($id, $this->_clients);
189
    }
190
191
    /**
192
     * @inheritdoc
193
     * @return ResponseData|DataInterface
194
     * @throws InvalidConfigException|InvalidArgumentException|\ReflectionException
195
     */
196 13
    public function request($command, array $data, $clientId = null): DataInterface
197
    {
198 13
        if (in_array($command, $this->requestCommands(), true)) {
199 9
            $client = $this->getClient($clientId);
200
201
            /** @var RequestData $requestData */
202
203 6
            $requestData = Yii::createObject($this->requestDataConfig, [$command, $data, $client]);
204 6
            $event = Yii::createObject([
205 6
                'class' => RequestEvent::class,
206 6
                'command' => $command,
207 6
                'client' => $client,
208 6
                'requestData' => $requestData
209
            ]);
210
211 6
            $this->beforeRequest($event);
212 6
            $httpClient = $this->getHttpClient();
213 6
            $data = $this->requestInternal($requestData, $httpClient);
214 5
            $responseData = Yii::createObject($this->responseDataConfig, [$command, $data, $client]);
215 5
            $event->responseData = $responseData;
216 5
            $this->afterRequest($event);
217 5
            Yii::debug(__CLASS__ . " requested sent with command: `$command` - version: " . $this->getVersion());
218
219 5
            return $responseData;
220
        } else {
221 4
            throw new InvalidArgumentException("Unknown request command `$command`");
222
        }
223
    }
224
225
    /**
226
     * This method automatically detect request commands supported via constants name prefix with `RC_`.
227
     * `RC` meaning Request Command.
228
     *
229
     * @return array An array constants value have name prefix with `RC_`
230
     * @throws \ReflectionException
231
     */
232
    public function requestCommands(): array
233
    {
234
        $reflection = new ReflectionClass($this);
235
236
        $commands = [];
237
        foreach ($reflection->getConstants() as $name => $value) {
238
            if (strpos($name, 'RC_') === 0) {
239
                $commands[] = $value;
240
            }
241
        }
242
243
        return $commands;
244
    }
245
246
    /**
247
     * This method is called at the beginning of requesting data to gateway server api.
248
     *
249
     * The default implementation will trigger an [[EVENT_BEFORE_REQUEST]] event.
250
     * When overriding this method, make sure you call the parent implementation like the following:
251
     *
252
     * ```php
253
     * public function beforeRequest($insert)
254
     * {
255
     *     // ...custom code here...
256
     *
257
     *     parent::beforeSave($insert);
258
     * }
259
     * ```
260
     *
261
     * @param RequestEvent $event an event will be trigger in this method.
262
     */
263 6
    public function beforeRequest(RequestEvent $event)
264
    {
265 6
        $this->trigger(self::EVENT_BEFORE_REQUEST, $event);
266 6
    }
267
268
    /**
269
     * @var HttpClient will be use to send request to gateway server api.
270
     */
271
    private $_httpClient;
272
273
    /**
274
     * This method is called in [[request()]] invoke client and use it make request send to gateway server api.
275
     *
276
     * @param bool $force
277
     * @return object|HttpClient
278
     * @throws InvalidConfigException
279
     */
280 6
    protected function getHttpClient(bool $force = false): HttpClient
281
    {
282 6
        if ($this->_httpClient === null || $force) {
283
            /** @var HttpClient $client */
284
285 6
            $client = $this->_httpClient = Yii::createObject(ArrayHelper::merge([
286 6
                'class' => HttpClient::class,
287 6
                'baseUrl' => $this->getBaseUrl()
288 6
            ], $this->getHttpClientConfig()));
289
290 6
            return $client;
291
        } else {
292 5
            return $this->_httpClient;
293
        }
294
    }
295
296
    /**
297
     * Returns the config for the HttpClient.
298
     * An implement class may override this method for add default headers, transport, options...
299
     *
300
     * @return array
301
     */
302 6
    protected function getHttpClientConfig(): array
303
    {
304 6
        return [];
305
    }
306
307
    /**
308
     * An internal method get and send customize data depend on gateway server api. This method called by [[request()]] .
309
     *
310
     * @param RequestData $requestData use to get data, command, client for prepare request to send.
311
     * @param HttpClient $httpClient use for make request to gateway server api.
312
     * @return array response requested data.
313
     */
314
    abstract protected function requestInternal(RequestData $requestData, HttpClient $httpClient): array;
315
316
317
    /**
318
     * This method is called at the end of requesting data to gateway server api.
319
     * The default implementation will trigger an [[EVENT_AFTER_REQUEST]] event.
320
     * When overriding this method, make sure you call the parent implementation at the end so that
321
     * the event is triggered.
322
     *
323
     * @param RequestEvent $event an event will be trigger in this method.
324
     */
325 5
    public function afterRequest(RequestEvent $event)
326
    {
327 5
        $this->trigger(self::EVENT_AFTER_REQUEST, $event);
328 5
    }
329
330
}
331