Completed
Push — master ( 276638...21b86e )
by Gennady
02:29
created

AbstractApi::call()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 12
cts 12
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 5
nop 2
crap 4
1
<?php
2
3
/*
4
 * This file is part of the php-shop-logistics.ru-api package.
5
 *
6
 * (c) Gennady Knyazkin <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Gennadyx\ShopLogisticsRu\Api;
13
14
use Gennadyx\ShopLogisticsRu\ApiClient;
15
use Gennadyx\ShopLogisticsRu\CallResult;
16
use Gennadyx\ShopLogisticsRu\Exception\BadMethodCallException;
17
use Gennadyx\ShopLogisticsRu\Response\Error;
18
use Gennadyx\ShopLogisticsRu\Response\Keys;
19
use Http\Client\Exception as HttpClientException;
20
21
/**
22
 * Abstract class of any api instance
23
 *
24
 * @author Gennady Knyazkin <[email protected]>
25
 */
26
abstract class AbstractApi implements ApiInterface
27
{
28
    /**
29
     * @var ApiClient
30
     */
31
    protected $client;
32
33
    /**
34
     * AbstractApi constructor.
35
     *
36
     * @param ApiClient $client Api client instance
37
     */
38 10
    public function __construct(ApiClient $client)
39
    {
40 10
        $this->client = $client;
41 10
    }
42
43
    /**
44
     * {@inheritdoc}
45
     */
46 7
    public function call($name, ...$arguments)
47
    {
48 7
        $map = $this->getMethodsMap();
49
50 7
        if (!array_key_exists($name, $map)) {
51 1
            throw new BadMethodCallException(
52 1
                sprintf('Call to undefined method %s::%s()', static::class, $name)
53 1
            );
54
        }
55
56 6
        $method = $map[$name];
57
58 6
        if (is_string($method)) {
59 1
            $method = $map[$method];
60 1
        }
61
62 6
        $arguments = isset($method['arguments']) ? $method['arguments'] : $arguments;
63
64 6
        return $this->callMethod($method, $arguments);
65
    }
66
67
    /**
68
     * @param string $name
69
     * @param array  $arguments
70
     *
71
     * @return array|Error
72
     * @throws \Gennadyx\ShopLogisticsRu\Exception\BadMethodCallException
73
     */
74 7
    public function __call($name, array $arguments)
75
    {
76 7
        return $this->call($name, ...$arguments);
77
    }
78
79
    /**
80
     * Get all available methods with remote functions
81
     *
82
     * @return array
83
     */
84
    abstract protected function getMethodsMap();
85
86
    /**
87
     * @param array $method Method info
88
     * @param array $args   Arguments
89
     *
90
     * @return array|Error
91
     */
92 6
    private function callMethod(array $method, array $args)
93
    {
94 6
        $callResult = $this->callRemote($method, $args);
95
96 6
        if ($callResult->isSuccess()) {
97 3
            return $callResult->getData();
98
        }
99
100 3
        return $callResult->getError();
101
    }
102
103
    /**
104
     * @param array $method    Method info
105
     * @param array $arguments Method arguments
106
     *
107
     * @return CallResult
108
     */
109 6
    private function callRemote(array $method, array $arguments)
110
    {
111 6
        $decode   = $this->client->getDecoder();
112 6
        $content  = $this->makeRequestXml($method['remote'], $arguments);
113 6
        $response = $decode($this->sendRequest($content));
114
115 6
        return $this->processResponse($response, $method['keys']);
116
    }
117
118
    /**
119
     * @param string $method Method name
120
     * @param array  $args   Method arguments
121
     *
122
     * @return string XML fore request
123
     */
124 6
    private function makeRequestXml($method, array $args)
125
    {
126 6
        $encode          = $this->client->getEncoder();
127 6
        $arrayForRequest = $this->buildRequestData($method, $args);
128
129 6
        return $encode($arrayForRequest);
130
    }
131
132
    /**
133
     * @param string $method Method name
134
     * @param array  $args   Method arguments
135
     *
136
     * @return array Array for convert to xml
137
     */
138 6
    private function buildRequestData($method, array $args)
139
    {
140
        $array = [
141 6
            'function' => $method,
142 6
            'api_id'   => $this->client->getKey(),
143 6
        ];
144
145 6
        return array_merge($array, $args);
146
    }
147
148
    /**
149
     * @param string $xml Request xml
150
     *
151
     * @return string|null
152
     */
153 6
    private function sendRequest($xml)
154
    {
155 6
        $requestFactory = $this->client->getRequestFactory();
156 6
        $httpClient     = $this->client->getHttpClient();
157
158 6
        $uri  = $this->client->getUrl();
159 6
        $data = urlencode(base64_encode($xml));
160
161 6
        $requestBody = sprintf('xml=%s', $data);
162 6
        $request     = $requestFactory->createRequest('POST', $uri, [], $requestBody);
163
164
        try {
165 6
            $request  = $request->withHeader('Content-Type', 'application/x-www-form-urlencoded');
166 6
            $response = $httpClient->sendRequest($request);
167 4
            $result   = $response->getBody()->getContents();
168 6
        } catch (HttpClientException $e) {
169 1
            $result = null;
170 2
        } catch (\Exception $e) {
171 1
            $result = null;
172
        }
173
174 6
        return $result;
175
    }
176
177
    /**
178
     * @param array      $response     Response from api server
179
     * @param array|null $responseKeys Response root xml keys
180
     *
181
     * @return CallResult
182
     */
183 6
    private function processResponse(array $response = [], array $responseKeys = null)
184
    {
185 6
        $callResult = new CallResult();
186 6
        $errorCode  = Error::NO;
187
188 6
        if (isset($response['error'])) {
189 3
            $errorCode = (int) $response['error'];
190 3
            unset($response['error']);
191 3
        }
192
193 6
        $data = $this->buildResponseData($response, $responseKeys);
194
195 6
        if (null === $data) {
196 3
            $errorCode = Error::EMPTY_RESPONSE;
197 3
            $data      = [];
198 3
        }
199
200 6
        $callResult->setError($errorCode);
201 6
        $callResult->setData($data);
202
203 6
        return $callResult;
204
    }
205
206
    /**
207
     * @param array      $response
208
     * @param array|null $keys
209
     *
210
     * @return array|null
211
     */
212 6
    private function buildResponseData(array $response, array $keys = null)
213
    {
214 6
        if ($keys === Keys::ROOT) {
215 2
            return is_array($response) ? $response : [];
216
        }
217
218 5
        $list = $keys['list'];
219 5
        $item = $keys['item'];
220
221 5
        if (!isset($response[$list], $response[$list][$item])
222 5
            || empty($response[$list][$item])
223 5
        ) {
224 3
            return null;
225
        }
226
227 2
        $response = $response[$list][$item];
228
229 2
        if (!is_int(array_keys($response)[0])) {
230 1
            $response = [$response];
231 1
        }
232
233 2
        return $response;
234
    }
235
}
236