Completed
Push — master ( a839bb...32192c )
by Camilo
07:42
created

TgLog::resetObjectValues()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1
Metric Value
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 4
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace unreal4u;
6
7
use \GuzzleHttp\Client;
8
use unreal4u\Abstracts\TelegramTypes;
9
use unreal4u\InternalFunctionality\TelegramDocument;
10
use unreal4u\Abstracts\TelegramMethods;
11
use unreal4u\Telegram\Types\File;
12
13
/**
14
 * The main API which does it all
15
 */
16
class TgLog
17
{
18
    /**
19
     * Stores the token
20
     * @var string
21
     */
22
    private $botToken = '';
23
24
    /**
25
     * Stores the API URL from Telegram
26
     * @var string
27
     */
28
    private $apiUrl = '';
29
30
    /**
31
     * With this flag we'll know what type of request to send to Telegram
32
     *
33
     * 'application/x-www-form-urlencoded' is the "normal" one, which is simpler and quicker.
34
     * 'multipart/form-data' should be used only to upload documents, photos, etc.
35
     *
36
     * @var string
37
     */
38
    private $formType = 'application/x-www-form-urlencoded';
39
40
    /**
41
     * Stores the last method name used
42
     * @var string
43
     */
44
    protected $methodName = '';
45
46
    /**
47
     * TelegramLog constructor.
48
     * @param string $botToken
49
     */
50 32
    public function __construct(string $botToken)
51
    {
52 32
        $this->botToken = $botToken;
53 32
        $this->constructApiUrl();
54 32
    }
55
56
    /**
57
     * Prepares and sends an API request to Telegram
58
     *
59
     * @param mixed $method
60
     * @return mixed
61
     */
62 19
    public function performApiRequest(TelegramMethods $method): TelegramTypes
63
    {
64 19
        $this->resetObjectValues();
65 19
        $jsonDecoded = $this->sendRequestToTelegram($method, $this->constructFormData($method));
66
67 15
        $returnObject = 'unreal4u\\Telegram\\Types\\' . $method::bindToObjectType();
68 15
        return new $returnObject($jsonDecoded['result']);
69
    }
70
71
    /**
72
     * Will download a file from the Telegram server. Before calling this function, you have to call the getFile method!
73
     *
74
     * @see unreal4u\Telegram\Types\File
75
     * @see unreal4u\Telegram\Methods\GetFile
76
     *
77
     * @param File $file
78
     * @return TelegramDocument
79
     */
80
    public function downloadFile(File $file): TelegramDocument
81
    {
82
        $url = $this->apiUrl . $file->file_path;
83
        $client = new Client();
84
        return new TelegramDocument($client->get($url));
0 ignored issues
show
Compatibility introduced by
$client->get($url) of type object<Psr\Http\Message\ResponseInterface> is not a sub-type of object<GuzzleHttp\Psr7\Response>. It seems like you assume a concrete implementation of the interface Psr\Http\Message\ResponseInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
85
    }
86
87
    /**
88
     * Builds up the Telegram API url
89
     * @return TgLog
90
     */
91 32
    final private function constructApiUrl(): TgLog
92
    {
93 32
        $this->apiUrl = 'https://api.telegram.org/bot' . $this->botToken . '/';
94 32
        return $this;
95
    }
96
97
    /**
98
     * This is the method that actually makes the call, which can be easily overwritten so that our unit tests can work
99
     *
100
     * @param TelegramMethods $method
101
     * @param array $formData
102
     * @return array
103
     */
104
    protected function sendRequestToTelegram(TelegramMethods $method, array $formData): array
105
    {
106
        $client = new Client();
107
        $response = $client->post($this->composeApiMethodUrl($method), $formData);
108
        return json_decode((string)$response->getBody(), true);
109
    }
110
111 19
    private function resetObjectValues(): TgLog
112
    {
113 19
        $this->formType = 'application/x-www-form-urlencoded';
114 19
        $this->methodName = '';
115
116 19
        return $this;
117
    }
118
119 19
    private function constructFormData(TelegramMethods $method): array
120
    {
121 19
        $result = $this->checkSpecialConditions($method);
122
123 19
        switch ($this->formType) {
124
            case 'application/x-www-form-urlencoded':
125
                $formData = [
126 15
                    'form_params' => get_object_vars($method),
127
                ];
128 15
                break;
129
            case 'multipart/form-data':
130 4
                $formData = $this->buildMultipartFormData(get_object_vars($method), $result['id'], $result['stream']);
131 4
                break;
132
            default:
133
                $formData = [];
134
                break;
135
        }
136
137 19
        return $formData;
138
    }
139
140
    /**
141
     * Can perform any special checks needed to be performed before sending the actual request to Telegram
142
     *
143
     * This will return an array with data that will be different in each case (for now). This can be changed in the
144
     * future.
145
     *
146
     * @param TelegramMethods $method
147
     * @return array
148
     */
149 19
    private function checkSpecialConditions(TelegramMethods $method): array
150
    {
151 19
        $return = [false];
152
153 19
        foreach ($method as $key => $value) {
0 ignored issues
show
Bug introduced by
The expression $method of type object<unreal4u\Abstracts\TelegramMethods> is not traversable.
Loading history...
154 17
            if (is_object($value)) {
155 5
                if (get_class($value) == 'unreal4u\\Telegram\\Types\\Custom\\InputFile') {
156
                    // If we are about to send a file, we must use the multipart/form-data way
157 4
                    $this->formType = 'multipart/form-data';
158
                    $return = [
159 4
                        'id' => $key,
160 4
                        'stream' => $value->getStream(),
161
                    ];
162 1
                } elseif (in_array('unreal4u\\Abstracts\\KeyboardMethods', class_parents($value))) {
163
                    // If we are about to send a KeyboardMethod, we must send a serialized object
164 1
                    $method->$key = json_encode($value);
165 17
                    $return = [true];
166
                }
167
            }
168
        }
169
170 19
        return $return;
171
    }
172
173
    /**
174
     * Builds up the URL with which we can work with
175
     *
176
     * @param TelegramMethods $call
177
     * @return string
178
     */
179 19
    protected function composeApiMethodUrl(TelegramMethods $call): string
180
    {
181 19
        $completeClassName = get_class($call);
182 19
        $this->methodName = lcfirst(substr($completeClassName, strrpos($completeClassName, '\\') + 1));
183
184 19
        return $this->apiUrl . $this->methodName;
185
    }
186
187
    /**
188
     * Builds up a multipart form-like array for Guzzle
189
     *
190
     * @param array $data The original object in array form
191
     * @param string $fileKeyName A file handler will be sent instead of a string, state here which field it is
192
     * @param resource $stream The actual file handler
193
     * @return array Returns the actual formdata to be sent
194
     */
195 5
    private function buildMultipartFormData(array $data, string $fileKeyName, $stream): array
196
    {
197
        $formData = [
198 5
            'multipart' => [],
199
        ];
200
201 5
        foreach ($data as $id => $value) {
202
            // Always send as a string unless it's a file
203
            $multiPart = [
204 5
                'name' => $id,
205
                'contents' => null,
206
            ];
207
208 5
            if ($id === $fileKeyName) {
209 4
                $multiPart['contents'] = $stream;
210
            } else {
211 5
                $multiPart['contents'] = (string)$value;
212
            }
213
214 5
            $formData['multipart'][] = $multiPart;
215
        }
216
217 5
        return $formData;
218
    }
219
}
220