MyriadApi   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 278
Duplicated Lines 0 %

Test Coverage

Coverage 97.35%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 105
c 2
b 0
f 0
dl 0
loc 278
ccs 110
cts 113
cp 0.9735
rs 8.8
wmc 45

12 Methods

Rating   Name   Duplication   Size   Complexity  
A isFault() 0 3 2
A mockClient() 0 10 2
A call() 0 13 3
B assocListResponseToCollection() 0 33 8
A assocListResponseToArray() 0 15 5
A faultString() 0 3 1
B __call() 0 34 6
A makeSoapParams() 0 9 2
A functionsSet() 0 7 2
B listResponseToArray() 0 14 7
A __construct() 0 3 1
B listResponseToCollection() 0 30 6

How to fix   Complexity   

Complex Class

Complex classes like MyriadApi often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MyriadApi, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
4
namespace MyriadSoap;
5
6
use Illuminate\Support\Facades\Config;
7
use Illuminate\Support\Str;
8
use MyriadSoap\Endpoints\FunctionsSet;
9
use MyriadSoap\Exceptions\UnexpectedTypeException;
10
11
class MyriadApi
12
{
13
    protected MyriadSoapClient $client;
14
15
    /**
16
     * MyriadManager constructor.
17
     *
18
     * @param  MyriadSoapClient  $client
19
     */
20 27
    public function __construct(MyriadSoapClient $client)
21
    {
22 27
        $this->client = $client;
23
    }
24
25
    /**
26
     * @param  string  $class
27
     *
28
     * @return mixed
29
     * @throws MyriadSoapException
30
     */
31 6
    public function functionsSet(string $class): FunctionsSet
32
    {
33 6
        if (!is_subclass_of($class, FunctionsSet::class)) {
34 1
            throw new MyriadSoapException('class should extend FunctionsSet');
35
        }
36
37 5
        return new $class($this);
38
    }
39
40
    /**
41
     * @param $method
42
     * @param $arguments
43
     *
44
     * @return array|mixed
45
     * @throws MyriadSoapException
46
     */
47 17
    public function __call($method, $arguments)
48
    {
49 17
        if (strlen($method) > 5
50 17
            && Str::startsWith($method, 'SOAP_')) {
51 16
            if (Str::endsWith($method, '_List')) {
52 5
                $method = Str::beforeLast($method, '_List');
53
54 5
                return $this->listResponseToArray(
55 5
                    $this->call($method, $arguments[0] ?? []),
56 5
                    $arguments[1] ?? 0,
57 5
                    $arguments[2] ?? Str::singular(Str::after($method, 'SOAP_get'))
58 5
                );
59 11
            } elseif (Str::endsWith($method, '_Collection')) {
60 5
                $method = Str::beforeLast($method, '_Collection');
61
62 5
                return $this->listResponseToCollection(
63 5
                    $this->call($method, $arguments[0] ?? []),
64 5
                    $arguments[1] ?? [],
65 5
                    $arguments[2] ?? Str::singular(Str::after($method, 'SOAP_get'))
66 5
                );
67 6
            } elseif (Str::endsWith($method, '_AssocCollection')) {
68 2
                $method = Str::beforeLast($method, '_AssocCollection');
69
70 2
                return $this->assocListResponseToCollection(
71 2
                    $this->call($method, $arguments[0] ?? []),
72 2
                    $arguments[1] ?? [],
73 2
                    $arguments[2] ?? Str::singular(Str::after($method, 'SOAP_get'))
74 2
                );
75
            } else {
76 4
                return $this->call($method, $arguments[0] ?? []);
77
            }
78
        }
79
80 1
        throw new \BadMethodCallException("Method {$method} not exists");
81
    }
82
83
    /**
84
     * @param  string  $method
85
     * @param  array  $parameters
86
     *
87
     * @return mixed|array
88
     * @throws MyriadSoapException
89
     */
90 20
    public function call(string $method, array $parameters = [])
91
    {
92 20
        $response = $this->client->__soapCall($method, $this->makeSoapParams($parameters));
93
94 20
        if ($this->isFault($response)) {
95 4
            throw new MyriadSoapException($this->faultString($response), $method, $parameters);
96
        }
97
98 16
        if ((bool) Config::get('myriad-soap.format_response', false)) {
99 16
            $response = json_decode(json_encode($response), true);
100
        }
101
102 16
        return $response;
103
    }
104
105
    /**
106
     * Convert possibles myriad responses formats to array.
107
     *
108
     * @param  mixed  $response
109
     * @param  int  $separatorsCount
110
     * @param  string  $wrapperKey
111
     * @return array
112
     */
113 12
    public function listResponseToArray(mixed $response, int $separatorsCount = 0, string $wrapperKey = 'Items'): array
114
    {
115 12
        $formattedArray = [];
116 12
        if (is_string($response) && Str::substrCount($response, ';') == $separatorsCount) {
117 8
            $formattedArray[] = $response;
118 9
        } elseif (is_array($response)
119 9
                  && isset($response[$wrapperKey])
120 9
                  && is_array($response[$wrapperKey])) {
121 5
            foreach ($response[$wrapperKey] as $listItem) {
122 5
                $formattedArray = array_merge($formattedArray, $this->listResponseToArray($listItem, $separatorsCount));
123
            }
124
        }
125
126 12
        return $formattedArray;
127
    }
128
129
    /**
130
     * Convert possibles myriad responses formats to collection.
131
     *
132
     * @param  mixed  $response
133
     * @param  array  $keys
134
     * @param  string  $wrapperKey
135
     * @return \Illuminate\Support\Collection
136
     */
137 4
    public function listResponseToCollection(mixed $response, array $keys, string $wrapperKey = 'Items'): \Illuminate\Support\Collection
138
    {
139 4
        $array = $this->listResponseToArray($response, !empty($keys) ? count($keys) - 1 : 0, $wrapperKey);
140
141 4
        return collect($array)
0 ignored issues
show
Bug introduced by
$array of type array|string[] is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

141
        return collect(/** @scrutinizer ignore-type */ $array)
Loading history...
142 4
            ->map(function ($collected) use ($keys) {
143 3
                $collectedParts = collect(explode(';', $collected))
0 ignored issues
show
Bug introduced by
explode(';', $collected) of type string[] is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

143
                $collectedParts = collect(/** @scrutinizer ignore-type */ explode(';', $collected))
Loading history...
144 3
                    ->map(fn ($part) => trim($part))
145 3
                    ->filter();
146 3
                if ($collectedParts->count() == count($keys)) {
147
                    try {
148 3
                        $item    = [];
149 3
                        $counter = 0;
150 3
                        foreach ($keys as $key => $callback) {
151 3
                            $value = $collectedParts->get($counter);
152 3
                            if (is_callable($callback)) {
153 3
                                $item[$key] = call_user_func($callback, $value);
154
                            } else {
155 3
                                $item[$callback] = $value;
156
                            }
157 3
                            $counter++;
158
                        }
159
160 3
                        return $item;
161 1
                    } catch (UnexpectedTypeException) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
162
                    }
163
                }
164
165 1
                return null;
166 4
            })->filter()->values();
167
    }
168
169
    /**
170
     * Convert possibles myriad associative responses formats to array.
171
     *
172
     * @param  mixed  $response
173
     * @param  string  $wrapperKey
174
     * @return array
175
     */
176 2
    public function assocListResponseToArray(mixed $response, string $wrapperKey = 'Items'): array
177
    {
178 2
        $formattedArray = [];
179 2
        if (is_array($response)) {
180 2
            if (isset($response[$wrapperKey])
181 2
                && is_array($response[$wrapperKey])) {
182 1
                foreach ($response[$wrapperKey] as $listItem) {
183 1
                    $formattedArray = array_merge($formattedArray, $this->assocListResponseToArray($listItem, $wrapperKey));
184
                }
185
            } else {
186 2
                $formattedArray = array_merge($formattedArray, [$response]);
187
            }
188
        }
189
190 2
        return $formattedArray;
191
    }
192
193
    /**
194
     * Convert possibles myriad assoc responses formats to collection.
195
     *
196
     * @param  mixed  $response
197
     * @param  array  $keys
198
     * @param  string  $wrapperKey
199
     * @return \Illuminate\Support\Collection
200
     */
201 2
    public function assocListResponseToCollection(mixed $response, array $keys, string $wrapperKey = 'Items'): \Illuminate\Support\Collection
202
    {
203 2
        $array = $this->assocListResponseToArray($response, $wrapperKey);
204
205 2
        return collect($array)
0 ignored issues
show
Bug introduced by
$array of type array is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $value of collect(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

205
        return collect(/** @scrutinizer ignore-type */ $array)
Loading history...
206 2
            ->map(function ($collected) use ($keys) {
207 2
                if (!is_array($collected)) {
208
                    return null;
209
                }
210 2
                if (count($collected) < count($keys)) {
211 1
                    return null;
212
                }
213
214
                try {
215 2
                    $item = [];
216 2
                    foreach ($keys as $key => $callback) {
217 2
                        if (!is_callable($callback)) {
218 2
                            $key      = $callback;
219 2
                            $callback = null;
220
                        }
221 2
                        if (!array_key_exists($key, $collected)) {
222
                            return null;
223
                        }
224
225 2
                        $item[$key] = is_callable($callback) ? call_user_func($callback, $collected[$key]) : $collected[$key];
226
                    }
227
228 2
                    return $item;
229 1
                } catch (UnexpectedTypeException) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
230
                }
231
232 1
                return null;
233 2
            })->filter()->values();
234
    }
235
236
    /**
237
     * @param  array  $params
238
     *
239
     * @return array
240
     */
241 20
    protected function makeSoapParams(array $params)
242
    {
243 20
        $data = [];
244
245 20
        foreach ($params as $key => $value) {
246 18
            $data[] = new \SoapParam($value, $key);
247
        }
248
249 20
        return $data;
250
    }
251
252
    /**
253
     * @param  mixed|array  $response
254
     *
255
     * @return bool
256
     */
257 20
    protected function isFault($response): bool
258
    {
259 20
        return is_array($response) && isset($response['faultcode']);
260
    }
261
262
    /**
263
     * @param  array  $response
264
     *
265
     * @return string
266
     */
267 4
    protected function faultString(array $response): string
268
    {
269 4
        return 'MyriadSoapError ['.($response['faultcode'] ?? '-').']: '.($response['faultstring'] ?? '');
270
    }
271
272
273
    /**
274
     * Only for testing purpose
275
     *
276
     * @return \Mockery\Mock
277
     * @throws MyriadSoapException
278
     */
279 20
    public function mockClient()
280
    {
281 20
        if (class_exists('\Mockery')) {
282 20
            $mock         = \Mockery::mock(get_class($this->client));
283 20
            $this->client = $mock;
284
285 20
            return $this->client;
286
        }
287
288
        throw new MyriadSoapException('mockery not installed');
289
    }
290
}
291