Test Setup Failed
Push — master ( 278b0f...921fc4 )
by Syed Abidur
02:09
created

Client::executeWithCurl()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 27
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 14
c 1
b 0
f 0
nc 8
nop 1
dl 0
loc 27
rs 9.7998
1
<?php
2
3
namespace Sarahman\SmsService;
4
5
use Exception;
6
use GuzzleHttp\Psr7\Response as GuzzleResponse;
7
use Illuminate\Support\Facades\Config;
8
use Illuminate\Support\Facades\Log;
9
use Illuminate\Support\Facades\Validator;
10
use Sarahman\HttpRequestApiLog\Traits\WritesHttpLogs;
11
use Sarahman\SmsService\Interfaces\NeedsAuthenticationInterface;
12
use Sarahman\SmsService\Interfaces\ProviderInterface;
13
14
class Client
15
{
16
    use WritesHttpLogs;
17
18
    const PROVIDER_BANGLALINK = Providers\Banglalink::class;
19
    const PROVIDER_BD_WEB_HOST_24 = Providers\BdWebHost24::class;
20
    const PROVIDER_BOOM_CAST = Providers\BoomCast::class;
21
    const PROVIDER_ELITBUZZ = Providers\Elitbuzz::class;
22
    const PROVIDER_GRAMEENPHONE = Providers\Grameenphone::class;
23
    const PROVIDER_NOVOCOM = Providers\Novocom::class;
24
    const PROVIDER_PAYSTATION = Providers\Paystation::class;
25
    const PROVIDER_ROBI = Providers\Robi::class;
26
    const PROVIDER_SSL = Providers\Ssl::class;
27
    const PROVIDER_VALUE_FIRST = Providers\ValueFirst::class;
28
29
    private $provider;
30
31
    public function __construct(ProviderInterface $provider)
32
    {
33
        $this->provider = $provider;
34
        $this->enableLogging = Config::get('sms-service-with-bd-providers::config.enable_api_call_logging', false);
35
    }
36
37
    /**
38
     * Return a SMS provider according to the given provider name.
39
     *
40
     * @param string $providerName
41
     * @param array  $config
42
     * @param string $url
43
     *
44
     * @return ProviderInterface
45
     */
46
    public static function getProvider($providerName = self::PROVIDER_SSL, array $config = [], $url = null)
47
    {
48
        switch ($providerName) {
49
            case self::PROVIDER_BANGLALINK:
50
            case self::PROVIDER_BD_WEB_HOST_24:
51
            case self::PROVIDER_BOOM_CAST:
52
            case self::PROVIDER_ELITBUZZ:
53
            case self::PROVIDER_GRAMEENPHONE:
54
            case self::PROVIDER_NOVOCOM:
55
            case self::PROVIDER_PAYSTATION:
56
            case self::PROVIDER_ROBI:
57
            case self::PROVIDER_SSL:
58
            case self::PROVIDER_VALUE_FIRST:
59
                return new $providerName($config, $url);
60
61
            default:
62
                throw new Exception('Invalid SMS provider name is given.');
63
        }
64
    }
65
66
    public function send($recipients, $message, array $params = [])
67
    {
68
        $log = ['sent' => [], 'failed' => []];
69
        is_array($recipients) || $recipients = [$recipients];
70
71
        foreach ($recipients as $recipient) {
72
            $options = ['url' => $this->provider->getUrl()];
73
74
            try {
75
                if (!$data = $this->provider->mapParams($recipient, $message, $params)) {
76
                    throw new Exception(json_encode('Failed to map the params.'), 422);
77
                }
78
79
                $data = array_merge($this->provider->getConfig(), $data);
80
                $validator = Validator::make($data, $this->provider->getValidationRules());
81
82
                if ($validator->fails()) {
83
                    throw new Exception(json_encode($validator->messages()->all()), 422);
84
                }
85
86
                $options += $this->prepareCurlOptions($data);
87
                $response = $this->provider->parseResponse($this->executeWithCurl($options));
88
89
                if (!$response->getStatus()) {
90
                    throw new Exception($response->getResponseString(), 500);
91
                }
92
93
                $log['sent'][$recipient] = $response;
94
95
                $this->log('POST', $options['url'], $options, new GuzzleResponse(200, [], $response->getResponseString()));
96
            } catch (Exception $e) {
97
                $errorCode = $e->getCode() >= 100 ? $e->getCode() : 500;
98
                $errorMessage = 422 != $errorCode ? $e->getMessage() : json_decode($e->getMessage(), true);
99
                $log['failed'][$recipient] = [
100
                    'success'  => false,
101
                    'response' => $errorMessage,
102
                ];
103
104
                $this->log('POST', $options['url'], $options, new GuzzleResponse($errorCode, [], $errorMessage));
105
            }
106
        }
107
108
        return $this->getSummaryWithLogs($log);
109
    }
110
111
    public function sendWithFallback($recipients, $message, array $params = [])
112
    {
113
        $log = ['sent' => [], 'failed' => []];
114
        is_array($recipients) || $recipients = [$recipients];
115
116
        foreach ($recipients as $recipient) {
117
            $options = ['url' => $this->provider->getUrl()];
118
119
            try {
120
                if (!$data = $this->provider->mapParams($recipient, $message, $params)) {
121
                    throw new Exception(json_encode('Failed to map the params.'), 422);
122
                }
123
124
                $data = array_merge($this->provider->getConfig(), $data);
125
                $validator = Validator::make($data, $this->provider->getValidationRules());
126
127
                if ($validator->fails()) {
128
                    throw new Exception(json_encode($validator->messages()->all()), 422);
129
                }
130
131
                $options += $this->prepareCurlOptions($data);
132
133
                try {
134
                    $response = $this->executeWithCurl($options);
135
                } catch (Exception $e) {
136
                    $log['failed'][$recipient] = [
137
                        'success'  => false,
138
                        'response' => $e->getMessage(),
139
                    ];
140
                    $response = '';
141
                }
142
143
                $response = $this->provider->parseResponse($response);
144
145
                if (!$response->getStatus()) {
146
                    $this->log('POST', $options['url'], $options, new GuzzleResponse(500, [], $response->getResponseString()));
147
148
                    //Resend sms
149
                    Log::info('SMS sending failed response!');
150
151
                    try {
152
                        $response = $this->provider->parseResponse($this->executeWithCurl($options));
153
                        Log::info('Second try of sending SMS', $response);
154
155
                        if (!$response->getStatus()) {
156
                            throw new Exception($response->getResponseString(), 500);
157
                        }
158
                    } catch (Exception $e) {
159
                        Log::error('Curl error response: '.$e->getMessage());
160
161
                        throw $e;
162
                    }
163
                }
164
165
                $log['sent'][$recipient] = $response;
166
167
                $this->log('POST', $options['url'], $options, new GuzzleResponse(200, [], $response->getResponseString()));
168
            } catch (Exception $e) {
169
                $errorCode = $e->getCode() >= 100 ? $e->getCode() : 500;
170
                $errorMessage = 422 != $errorCode ? $e->getMessage() : json_decode($e->getMessage(), true);
171
                $log['failed'][$recipient] = [
172
                    'success'  => false,
173
                    'response' => $errorMessage,
174
                ];
175
176
                $this->log('POST', $options['url'], $options, new GuzzleResponse($errorCode, [], $errorMessage));
177
            }
178
        }
179
180
        return $this->getSummaryWithLogs($log);
181
    }
182
183
    /**
184
     * Prepare the curl options array according to the given data.
185
     *
186
     * @param array $data
187
     *
188
     * @return array
189
     */
190
    private function prepareCurlOptions(array $data)
191
    {
192
        $options = [
193
            'timeout' => 30,
194
        ];
195
196
        switch(get_class($this->provider)) {
197
            case self::PROVIDER_GRAMEENPHONE:
198
            case self::PROVIDER_NOVOCOM:
199
                $options += [
200
                    'httpheader' => ['Content-Type: application/json'],
201
                    'post'       => 1,
202
                    'postfields' => json_encode($data),
203
                ];
204
                break;
205
206
            case self::PROVIDER_PAYSTATION:
207
                $options += [
208
                    'httpheader' => [
209
                        'Content-Type: application/json',
210
                        'Accept: application/json',
211
                        'user_id:'.$data['user_id'],
212
                        'password:'.$data['password'],
213
                    ],
214
                    'post'       => 1,
215
                    'postfields' => json_encode($data),
216
                ];
217
                break;
218
219
            default:
220
                $options += [
221
                    'post'       => count($data),
222
                    'postfields' => http_build_query($data),
223
                ];
224
        }
225
226
        if ($this->provider instanceof NeedsAuthenticationInterface) {
227
            $options['httpheader'][] = 'Authorization: Bearer '.$this->provider->getAccessToken();
0 ignored issues
show
Bug introduced by
The method getAccessToken() does not exist on Sarahman\SmsService\Interfaces\ProviderInterface. It seems like you code against a sub-type of Sarahman\SmsService\Interfaces\ProviderInterface such as Sarahman\SmsService\Providers\ValueFirst. ( Ignorable by Annotation )

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

227
            $options['httpheader'][] = 'Authorization: Bearer '.$this->provider->/** @scrutinizer ignore-call */ getAccessToken();
Loading history...
228
        }
229
230
        return $options;
231
    }
232
233
    /**
234
     * Execute the Curl request according to the given curl options.
235
     *
236
     * @param array $options
237
     *
238
     * @return string
239
     */
240
    private function executeWithCurl(array $options)
241
    {
242
        $curlOptions = [];
243
        isset($options['returntransfer']) || $options['returntransfer'] = true;
244
245
        foreach ($options as $key => $value) {
246
            $option = 'CURLOPT_'.strtoupper($key);
247
            $curlOptions[constant($option)] = $value;
248
        }
249
250
        $ch = curl_init();
251
252
        curl_setopt_array($ch, $curlOptions);
253
254
        $response = curl_exec($ch);
255
256
        if ($response != true) {
257
            $eMsg = 'cURL Error # '.curl_errno($ch).' | cURL Error Message: '.curl_error($ch);
258
259
            curl_close($ch);
260
261
            throw new Exception($eMsg);
262
        }
263
264
        curl_close($ch);
265
266
        return (string) $response;
267
    }
268
269
    private function getSummaryWithLogs(array $log)
270
    {
271
        $sent = count($log['sent']);
272
        $failed = count($log['failed']);
273
274
        return [
275
            'summary' => [
276
                'sent'   => $sent,
277
                'failed' => $failed,
278
                'total'  => $sent + $failed,
279
            ],
280
            'log'     => $log,
281
        ];
282
    }
283
}
284