Completed
Pull Request — master (#53)
by Rick
02:25
created

TgLog::checkSpecialConditions()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 21
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 21
ccs 11
cts 11
cp 1
rs 9.0534
cc 4
eloc 12
nc 3
nop 1
crap 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace unreal4u\TelegramAPI;
6
7
use GuzzleHttp\Psr7\MultipartStream;
8
use React\Promise\Deferred;
9
use React\Promise\PromiseInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Log\LoggerInterface;
12
use unreal4u\TelegramAPI\Abstracts\TelegramMethods;
13
use unreal4u\TelegramAPI\Abstracts\TelegramTypes;
14
use unreal4u\TelegramAPI\Exceptions\ClientException as CustomClientException;
15
use unreal4u\TelegramAPI\InternalFunctionality\DummyLogger;
16
use unreal4u\TelegramAPI\InternalFunctionality\TelegramDocument;
17
use unreal4u\TelegramAPI\InternalFunctionality\TelegramRawData;
18
use unreal4u\TelegramAPI\Telegram\Types\Custom\InputFile;
19
use unreal4u\TelegramAPI\Telegram\Types\Custom\UnsuccessfulRequest;
20
use unreal4u\TelegramAPI\Telegram\Types\File;
21
22
/**
23
 * The main API which does it all
24
 */
25
class TgLog
26
{
27
    /**
28
     * @var RequestHandlerInterface
29
     */
30
    protected $requestHandler;
31
32
    /**
33
     * Stores the token
34
     * @var string
35
     */
36
    private $botToken;
37
38
    /**
39
     * Contains an instance to a PSR-3 compatible logger
40
     * @var LoggerInterface
41
     */
42
    protected $logger;
43
44
    /**
45
     * Stores the API URL from Telegram
46
     * @var string
47
     */
48
    private $apiUrl = '';
49
50
    /**
51
     * With this flag we'll know what type of request to send to Telegram
52
     *
53
     * 'application/x-www-form-urlencoded' is the "normal" one, which is simpler and quicker.
54
     * 'multipart/form-data' should be used only when you upload documents, photos, etc.
55
     *
56
     * @var string
57
     */
58
    private $formType = 'application/x-www-form-urlencoded';
59
60
    /**
61
     * Stores the last method name used
62
     * @var string
63
     */
64
    protected $methodName = '';
65
66
    /**
67
     * TelegramLog constructor.
68
     *
69
     * @param string $botToken
70
     * @param LoggerInterface $logger
71
     * @param RequestHandlerInterface $handler
72
     */
73 40
    public function __construct(
74
        string $botToken,
75
        LoggerInterface $logger = null,
76
        RequestHandlerInterface $handler = null
77
    ) {
78 40
        $this->botToken = $botToken;
79
80
        // Initialize new dummy logger (PSR-3 compatible) if not injected
81 40
        if ($logger === null) {
82 40
            $logger = new DummyLogger();
83
        }
84 40
        $this->logger = $logger;
85
86
        // Initialize new Guzzle client if not injected
87 40
        if ($handler === null) {
88 40
            $handler = new GuzzleRequestHandler(null, $logger);
89
        }
90 40
        $this->requestHandler = $handler;
91
92 40
        $this->constructApiUrl();
93 40
    }
94
95
    /**
96
     * Prepares and sends an API request to Telegram
97
     *
98
     * @param TelegramMethods $method
99
     * @return TelegramTypes
100
     * @throws \unreal4u\TelegramAPI\Exceptions\MissingMandatoryField
101
     */
102 31
    public function performApiRequest(TelegramMethods $method): TelegramTypes
103
    {
104 31
        $this->logger->debug('Request for API call, resetting internal values', [get_class($method)]);
105 31
        $this->resetObjectValues();
106 31
        $telegramRawData = $this->sendRequestToTelegram($method, $this->constructFormData($method));
107 24
        if ($telegramRawData->isError()) {
108
            $this->handleOffErrorRequest($telegramRawData);
109
        }
110
111 24
        return $method::bindToObject($telegramRawData, $this->logger);
112
    }
113
114
    /**
115
     * @param TelegramMethods $method
116
     *
117
     * @return PromiseInterface
118
     */
119
    public function performAsyncApiRequest(TelegramMethods $method)
120
    {
121
        $this->logger->debug('Request for async API call, resetting internal values', [get_class($method)]);
122
        $this->resetObjectValues();
123
        return $this->sendAsyncRequestToTelegram($method, $this->constructFormData($method));
124
    }
125
126
    /**
127
     * Will download a file from the Telegram server. Before calling this function, you have to call the getFile method!
128
     *
129
     * @see \unreal4u\TelegramAPI\Telegram\Types\File
130
     * @see \unreal4u\TelegramAPI\Telegram\Methods\GetFile
131
     *
132
     * @param File $file
133
     * @return TelegramDocument
134
     */
135
    public function downloadFile(File $file): TelegramDocument
136
    {
137
        $this->logger->debug('Downloading file from Telegram, creating URL');
138
        $url = 'https://api.telegram.org/file/bot' . $this->botToken . '/' . $file->file_path;
139
        $this->logger->debug('About to perform request to begin downloading file');
140
        return new TelegramDocument($this->requestHandler->get($url));
141
    }
142
143
    /**
144
     * @param File $file
145
     *
146
     * @return PromiseInterface
147
     */
148
    public function downloadFileAsync(File $file): PromiseInterface
149
    {
150
        $this->logger->debug('Downloading file async from Telegram, creating URL');
151
        $url = 'https://api.telegram.org/file/bot' . $this->botToken . '/' . $file->file_path;
152
        $this->logger->debug('About to perform request to begin downloading file');
153
154
        $deferred = new Deferred();
155
156
        return $this->requestHandler->getAsync($url)->then(
157
            function (ResponseInterface $response) use ($deferred) {
158
                $deferred->resolve(new TelegramDocument($response));
159
            },
160
            function (\Exception $exception) use ($deferred) {
161
                if (method_exists($exception, 'getResponse') && !empty($exception->getResponse()->getBody())) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getResponse() does only exist in the following sub-classes of Exception: GuzzleHttp\Exception\BadResponseException, GuzzleHttp\Exception\ClientException, GuzzleHttp\Exception\ConnectException, GuzzleHttp\Exception\RequestException, GuzzleHttp\Exception\ServerException, GuzzleHttp\Exception\TooManyRedirectsException. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
162
                    $deferred->resolve(new TelegramDocument($exception->getResponse()));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getResponse() does only exist in the following sub-classes of Exception: GuzzleHttp\Exception\BadResponseException, GuzzleHttp\Exception\ClientException, GuzzleHttp\Exception\ConnectException, GuzzleHttp\Exception\RequestException, GuzzleHttp\Exception\ServerException, GuzzleHttp\Exception\TooManyRedirectsException. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
163
                } else {
164
                    $deferred->reject($exception);
165
                }
166
            }
167
        );
168
    }
169
170
    /**
171
     * Builds up the Telegram API url
172
     * @return TgLog
173
     */
174 40
    final private function constructApiUrl(): TgLog
175
    {
176 40
        $this->apiUrl = 'https://api.telegram.org/bot' . $this->botToken . '/';
177 40
        $this->logger->debug('Built up the API URL');
178 40
        return $this;
179
    }
180
181
    /**
182
     * This is the method that actually makes the call, which can be easily overwritten so that our unit tests can work
183
     *
184
     * @param TelegramMethods $method
185
     * @param array $formData
186
     *
187
     * @return TelegramRawData
188
     * @throws \Exception
189
     */
190 24
    protected function sendRequestToTelegram(TelegramMethods $method, array $formData): TelegramRawData
191
    {
192 24
        $e = null;
193 24
        $this->logger->debug('About to perform HTTP call to Telegram\'s API');
194
        try {
195 24
            $response = $this->requestHandler->post($this->composeApiMethodUrl($method), $formData);
196 24
            $this->logger->debug('Got response back from Telegram');
197 24
            return $response;
198
        } catch (\Exception $e) {
199
            // It can happen that we have a network problem, in such case, we can't do nothing about it, so rethrow
200
            if (!method_exists($e, 'getResponse') || empty($e->getResponse())) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getResponse() does only exist in the following sub-classes of Exception: GuzzleHttp\Exception\BadResponseException, GuzzleHttp\Exception\ClientException, GuzzleHttp\Exception\ConnectException, GuzzleHttp\Exception\RequestException, GuzzleHttp\Exception\ServerException, GuzzleHttp\Exception\TooManyRedirectsException. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
201
                throw $e;
202
            }
203
            return new TelegramRawData((string)$e->getResponse()->getBody(), $e);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getResponse() does only exist in the following sub-classes of Exception: GuzzleHttp\Exception\BadResponseException, GuzzleHttp\Exception\ClientException, GuzzleHttp\Exception\ConnectException, GuzzleHttp\Exception\RequestException, GuzzleHttp\Exception\ServerException, GuzzleHttp\Exception\TooManyRedirectsException. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
204
        }
205
    }
206
207
    /**
208
     * @param TelegramMethods $method
209
     * @param array $formData
210
     *
211
     * @return PromiseInterface
212
     */
213
    protected function sendAsyncRequestToTelegram(TelegramMethods $method, array $formData): PromiseInterface
214
    {
215
        $this->logger->debug('About to perform async HTTP call to Telegram\'s API');
216
        $deferred = new Deferred();
217
218
        $promise = $this->requestHandler->postAsync($this->composeApiMethodUrl($method), $formData);
219
        $promise->then(
220
            function (ResponseInterface $response) use ($deferred) {
221
                $deferred->resolve(new TelegramRawData((string)$response->getBody()));
222
            },
223
            function (\Exception $exception) use ($deferred) {
224
                if (method_exists($exception, 'getResponse') && !empty($exception->getResponse()->getBody())) {
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getResponse() does only exist in the following sub-classes of Exception: GuzzleHttp\Exception\BadResponseException, GuzzleHttp\Exception\ClientException, GuzzleHttp\Exception\ConnectException, GuzzleHttp\Exception\RequestException, GuzzleHttp\Exception\ServerException, GuzzleHttp\Exception\TooManyRedirectsException. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
225
                    $deferred->resolve(new TelegramRawData((string)$exception->getResponse()->getBody(), $exception));
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Exception as the method getResponse() does only exist in the following sub-classes of Exception: GuzzleHttp\Exception\BadResponseException, GuzzleHttp\Exception\ClientException, GuzzleHttp\Exception\ConnectException, GuzzleHttp\Exception\RequestException, GuzzleHttp\Exception\ServerException, GuzzleHttp\Exception\TooManyRedirectsException. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
226
                } else {
227
                    $deferred->reject($exception);
228
                }
229
            }
230
        );
231
232
        return $deferred->promise();
233
    }
234
235
    /**
236
     * Resets everything to the default values
237
     *
238
     * @return TgLog
239
     */
240 31
    private function resetObjectValues(): TgLog
241
    {
242 31
        $this->formType = 'application/x-www-form-urlencoded';
243 31
        $this->methodName = '';
244
245 31
        return $this;
246
    }
247
248
    /**
249
     * Builds up the form elements to be sent to Telegram
250
     *
251
     * @TODO Move this to apart function
252
     *
253
     * @param TelegramMethods $method
254
     * @return array
255
     * @throws \unreal4u\TelegramAPI\Exceptions\MissingMandatoryField
256
     */
257 31
    private function constructFormData(TelegramMethods $method): array
258
    {
259 31
        $result = $this->checkSpecialConditions($method);
260
261 31
        switch ($this->formType) {
262 31
            case 'application/x-www-form-urlencoded':
263 27
                $this->logger->debug('Creating x-www-form-urlencoded form (AKA fast request)');
264
                $formData = [
265 27
                    'headers' => [
266
                        'Content-Type' => 'application/x-www-form-urlencoded',
267
                    ],
268 27
                    'body' => http_build_query($method->export(), '', '&'),
269
                ];
270 25
                break;
271 4
            case 'multipart/form-data':
272 4
                $formData = $this->buildMultipartFormData($method->export(), $result['id'], $result['stream']);
273 4
                break;
274
            default:
275
                $this->logger->critical(sprintf(
276
                    'Invalid form-type detected, if you incur in such a situation, this is most likely a product to ' .
277
                    'a bug. Please copy entire line and report at %s',
278
                    'https://github.com/unreal4u/telegram-api/issues'
279
                ), [
280
                    $this->formType
281
                ]);
282
                $formData = [
283
                    'headers' => [
284
                        'Content-Type' => $this->formType
285
                    ]
286
                ];
287
                break;
288
        }
289 29
        $this->logger->debug('About to send following data', $formData);
290
291 29
        return $formData;
292
    }
293
294
    /**
295
     * Can perform any special checks needed to be performed before sending the actual request to Telegram
296
     *
297
     * This will return an array with data that will be different in each case (for now). This can be changed in the
298
     * future.
299
     *
300
     * @param TelegramMethods $method
301
     * @return array
302
     */
303 31
    private function checkSpecialConditions(TelegramMethods $method): array
304
    {
305 31
        $this->logger->debug('Checking whether to apply special conditions to this request');
306 31
        $method->performSpecialConditions();
307
308 31
        $return = [false];
309
310 31
        foreach ($method as $key => $value) {
0 ignored issues
show
Bug introduced by
The expression $method of type object<unreal4u\Telegram...tracts\TelegramMethods> is not traversable.
Loading history...
311 27
            if (is_object($value) && $value instanceof InputFile) {
312 4
                $this->logger->debug('About to send a file, so changing request to use multi-part instead');
313
                // If we are about to send a file, we must use the multipart/form-data way
314 4
                $this->formType = 'multipart/form-data';
315
                $return = [
316 4
                    'id' => $key,
317 27
                    'stream' => $value->getStream(),
318
                ];
319
            }
320
        }
321
322 31
        return $return;
323
    }
324
325
    /**
326
     * Builds up the URL with which we can work with
327
     *
328
     * All methods in the Bot API are case-insensitive.
329
     * All queries must be made using UTF-8.
330
     *
331
     * @see https://core.telegram.org/bots/api#making-requests
332
     *
333
     * @param TelegramMethods $call
334
     * @return string
335
     */
336 30
    protected function composeApiMethodUrl(TelegramMethods $call): string
337
    {
338 30
        $completeClassName = get_class($call);
339 30
        $this->methodName = substr($completeClassName, strrpos($completeClassName, '\\') + 1);
340 30
        $this->logger->info('About to perform API request', ['method' => $this->methodName]);
341
342 30
        return $this->apiUrl . $this->methodName;
343
    }
344
345
    /**
346
     * Builds up a multipart form-like array for Guzzle
347
     *
348
     * @param array $data The original object in array form
349
     * @param string $fileKeyName A file handler will be sent instead of a string, state here which field it is
350
     * @param resource $stream The actual file handler
351
     * @return array Returns the actual formdata to be sent
352
     */
353 4
    private function buildMultipartFormData(array $data, string $fileKeyName, $stream): array
354
    {
355 4
        $this->logger->debug('Creating multi-part form array data (complex and expensive)');
356
        $formData = [
357 4
            'body' => null
358
        ];
359
360 4
        $multiPartArray = [];
361 4
        foreach ($data as $id => $value) {
362
            // Always send as a string unless it's a file
363
            $multiPart = [
364 4
                'name' => $id,
365
                'contents' => null,
366
            ];
367
368 4
            if ($id === $fileKeyName) {
369 4
                $multiPart['contents'] = $stream;
370
            } else {
371 4
                $multiPart['contents'] = (string)$value;
372
            }
373
374 4
            $multiPartArray[] = $multiPart;
375
        }
376
377 4
        $formData['body'] = new MultipartStream($multiPartArray);
378 4
        return $formData;
379
    }
380
381
    /**
382
     * @param TelegramRawData $telegramRawData
383
     * @return TgLog
384
     * @throws CustomClientException
385
     */
386
    private function handleOffErrorRequest(TelegramRawData $telegramRawData): TgLog
387
    {
388
        $errorRequest = new UnsuccessfulRequest($telegramRawData->getErrorData(), $this->logger);
389
390
        $clientException = new CustomClientException(
391
            $errorRequest->description,
392
            $errorRequest->error_code,
393
            $telegramRawData->getException()
394
        );
395
        $clientException->setParameters($errorRequest->parameters);
396
        throw $clientException;
397
    }
398
}
399