Passed
Pull Request — master (#1)
by Samuel
03:41 queued 02:16
created

Client   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 230
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 24
eloc 63
dl 0
loc 230
c 0
b 0
f 0
ccs 67
cts 67
cp 1
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A sendSMS() 0 27 4
A getHttpClient() 0 3 1
A decode() 0 3 1
A requestAndParseTextResponse() 0 34 5
A create() 0 4 1
A requestAndExtractDetails() 0 15 3
A getDeliveryReport() 0 13 2
A sendOTP() 0 8 1
A __construct() 0 3 1
A makeRequest() 0 14 3
A getBalance() 0 7 1
A setSender() 0 3 1
1
<?php
2
3
namespace Elmage\TextNg;
4
5
use Elmage\TextNg\Enum\Param;
6
use Elmage\TextNg\Enum\Route;
7
use Elmage\TextNg\Exception\InvalidParamException;
8
use Elmage\TextNg\Exception\SendingLimitException;
9
10
class Client
11
{
12
    const VERSION = '0.1.0';
13
14
    private HttpClient $http;
15
16
    /** @var int The max number of recipients in one transaction */
17
    private int $bulkLimit = 100000;
18
19 20
    public function __construct(HttpClient $http)
20
    {
21 20
        $this->http = $http;
22 20
    }
23
24
    /**
25
     * Creates a new TextNg client.
26
     *
27
     * @param Configuration $configuration
28
     *
29
     * @return Client A new TextNg client
30
     */
31 2
    public static function create(Configuration $configuration)
32
    {
33 2
        return new static(
34 2
            $configuration->createHttpClient()
35
        );
36
    }
37
38 2
    public function getHttpClient()
39
    {
40 2
        return $this->http;
41
    }
42
43
    /**
44
     * @return array
45
     */
46 2
    public function getBalance(): array
47
    {
48
        $params = array(
49 2
            Param::CHECK_BALANCE => 1,
50
        );
51
52 2
        return $this->makeRequest('/smsbalance/', 'get', $params);
53
    }
54
55
    /**
56
     * @param int    $route
57
     * @param string $phoneNumber
58
     * @param string $message
59
     * @param string $bypassCode
60
     * @param array  $params
61
     *
62
     * @return array
63
     */
64 8
    public function sendOTP(
65
        int $route,
66
        string $phoneNumber,
67
        string $message,
68
        string $bypassCode = '',
69
        array $params = array()
70
    ): array {
71 8
        return $this->sendSMS($route, array($phoneNumber), $message, $bypassCode, $params);
72
    }
73
74
    /**
75
     * @param int    $route
76
     * @param array  $phoneNumbers
77
     * @param string $message
78
     * @param string $bypassCode
79
     * @param array  $params
80
     *
81
     * @return array
82
     */
83 14
    public function sendSMS(
84
        int $route,
85
        array $phoneNumbers,
86
        string $message,
87
        string $bypassCode = '',
88
        array $params = array()
89
    ): array {
90 14
        if (!in_array($route, Route::$routes)) {
91 2
            throw new InvalidParamException("Invalid 'Route' parameter supplied");
92
        }
93
94 12
        if (count($phoneNumbers) > $this->bulkLimit) {
95 2
            throw new SendingLimitException('Too many recipients');
96
        }
97
98 10
        $phoneNumbers = implode(',', $phoneNumbers);
99
100 10
        $params[Param::SENDER] = $this->http->getSender();
101 10
        $params[Param::ROUTE] = $route;
102 10
        $params[Param::PHONE] = trim($phoneNumbers, ',');
103 10
        $params[Param::MESSAGE] = $message;
104
105 10
        if ($bypassCode) {
106 10
            $params[Param::BYPASSCODE] = $bypassCode;
107
        }
108
109 10
        return $this->makeRequest('/pushsms/', 'post', $params);
110
    }
111
112 2
    public function getDeliveryReport(string $reference, $req, $used_route)
113
    {
114 2
        if (!in_array($used_route, Route::$routes)) {
115 2
            throw new InvalidParamException("Invalid 'used_route' parameter supplied");
116
        }
117
118
        $params = array(
119 2
            Param::REFERENCE => $reference,
120 2
            Param::REQ => $req,
121 2
            Param::USED_ROUTE => $used_route,
122
        );
123
124 2
        return $this->makeRequest('/deliveryreport/', 'get', $params);
125
    }
126
127 2
    public function setSender(string $sender): void
128
    {
129 2
        $this->http->setSender($sender);
130 2
    }
131
132
    /**------------------------------------------------------------------------------
133
     * | PRIVATE METHODS
134
     * /*------------------------------------------------------------------------------*/
135
136
    /**
137
     * This method makes the intended request than parses the response body
138
     * and returns an array of the the response data.
139
     *
140
     * @param string $path
141
     * @param string $method
142
     * @param array  $params
143
     *
144
     * @return array
145
     */
146 14
    private function makeRequest(string $path, string $method, array $params): array
147
    {
148 14
        $response = $this->http->$method($path, $params);
149 14
        $body = $response->getBody();
150
151
        // check if response by is encapsulated in curly braces
152
        // this indicates if the response is JSON or not
153
        // Yeah, I'm not checking the response header for content type because
154
        // The API docs are not consistent with certain things
155
156 14
        if (substr($body, 0, 1) == '{' && substr($body, -1) == '}') {
157 4
            return $this->requestAndExtractDetails($body);
158
        } else {
159 10
            return $this->requestAndParseTextResponse($body);
160
        }
161
    }
162
163
    /**
164
     * This method is only used where the response body is Plain Text and extra parsing and
165
     * formatting has to be done.
166
     *
167
     * @param string $body
168
     *
169
     * @return array
170
     */
171 10
    private function requestAndParseTextResponse(string $body): array
172
    {
173
        //if the response body starts with the string "ERROR"
174
        //return an array showing the status as "error" and set the error message
175
        //to the remainder part of the response body
176 10
        if (substr($body, 0, 5) == 'ERROR') {
177
            return array(
178 2
                'status' => 'error',
179 2
                'message' => substr($body, 6),
180
            );
181
        }
182
183 8
        $body = explode('||', $body);
184
185
        // Format each section of the body delimited by "||" into key value pairs
186
        // with the keys being in all lower case
187 8
        $data = array();
188
189 8
        $i = 0;
190 8
        foreach ($body as $item) {
191 8
            if (0 == $i && strpos($item, 'units')) {
192 8
                $data['units_used'] = trim(explode(' ', $item)[0]);
193
            } else {
194 8
                $data_parts = explode(':', $item);
195 8
                @$data[strtolower(trim($data_parts[0]))] = trim($data_parts[1]);
196
            }
197
198 8
            $i++;
199
        }
200
201
        // Remove the API key from the response
202 8
        unset($data[Param::API_KEY]);
203
204 8
        return $data;
205
    }
206
207
    /**
208
     * This method is only used where the response body is JSON.
209
     *
210
     * @param string $body
211
     *
212
     * @return array
213
     */
214 4
    private function requestAndExtractDetails(string $body): array
215
    {
216 4
        $details = $this->decode($body)['D']['details'];
217
218 4
        if (count($details) == 1) {
219 2
            $details = $details[0];
220
        } else {
221 2
            $details = array('data' => $details);
222
        }
223
224 4
        if (array_key_exists(Param::API_KEY, $details)) {
225 2
            unset($details[Param::API_KEY]);
226
        }
227
228 4
        return $details;
229
    }
230
231
    /**
232
     * @param $data
233
     * @param bool $assoc
234
     *
235
     * @return array
236
     */
237 4
    private function decode($data, $assoc = true): array
238
    {
239 4
        return json_decode($data, $assoc);
240
    }
241
}
242