Completed
Push — master ( 8f227e...0a60c9 )
by Camilo
05:21
created

TgLog::constructFormData()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.3332
Metric Value
dl 0
loc 20
ccs 8
cts 12
cp 0.6667
rs 9.4286
cc 3
eloc 14
nc 3
nop 1
crap 3.3332
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace unreal4u;
6
7
use \GuzzleHttp\Client;
8
use unreal4u\InternalFunctionality\TelegramDocument;
9
use unreal4u\Telegram\Types\File;
10
11
/**
12
 * The main API which does it all
13
 */
14
class TgLog
15
{
16
    /**
17
     * Stores the token
18
     * @var string
19
     */
20
    private $botToken = '';
21
22
    /**
23
     * Stores the API URL from Telegram
24
     * @var string
25
     */
26
    private $apiUrl = '';
27
28
    /**
29
     * With this flag we'll know what type of request to send to Telegram
30
     *
31
     * 'application/x-www-form-urlencoded' is the "normal" one, which is simpler and quicker.
32
     * 'multipart/form-data' should be used only to upload documents, photos, etc.
33
     *
34
     * @var string
35
     */
36
    private $formType = 'application/x-www-form-urlencoded';
37
38
    /**
39
     * Stores the last method name used
40
     * @var string
41
     */
42
    protected $methodName = '';
43
44
    /**
45
     * TelegramLog constructor.
46
     * @param string $botToken
47
     */
48 32
    public function __construct(string $botToken)
49
    {
50 32
        $this->botToken = $botToken;
51 32
        $this->constructApiUrl();
52 32
    }
53
54
    /**
55
     * Prepares and sends an API request to Telegram
56
     *
57
     * @param mixed $method
58
     * @return mixed
59
     */
60 19
    public function performApiRequest($method)
61
    {
62 19
        $this->resetObjectValues();
63 19
        $jsonDecoded = $this->sendRequestToTelegram($method, $this->constructFormData($method));
64
65 15
        $returnObject = 'unreal4u\\Telegram\\Types\\' . $method::bindToObjectType();
66 15
        return new $returnObject($jsonDecoded->result);
67
    }
68
69
    /**
70
     * Will download a file from the Telegram server. Before calling this function, you have to call the getFile method!
71
     *
72
     * @see unreal4u\Telegram\Types\File
73
     * @see unreal4u\Telegram\Methods\GetFile
74
     *
75
     * @param File $file
76
     * @return TelegramDocument
77
     */
78
    public function downloadFile(File $file): TelegramDocument
79
    {
80
        $url = $this->apiUrl . $file->file_path;
81
        $client = new Client();
82
        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...
83
    }
84
85
    /**
86
     * Builds up the Telegram API url
87
     * @return TgLog
88
     */
89 32
    final private function constructApiUrl(): TgLog
90
    {
91 32
        $this->apiUrl = 'https://api.telegram.org/bot' . $this->botToken . '/';
92 32
        return $this;
93
    }
94
95
    /**
96
     * This is the method that actually makes the call, which can be easily overwritten so that our unit tests can work
97
     *
98
     * @param $method
99
     * @param array $formData
100
     * @return \stdClass
101
     */
102
    protected function sendRequestToTelegram($method, array $formData): \stdClass
103
    {
104
        $client = new Client();
105
        $response = $client->post($this->composeApiMethodUrl($method), $formData);
106
        return json_decode((string)$response->getBody());
107
    }
108
109 19
    private function resetObjectValues(): TgLog
110
    {
111 19
        $this->formType = 'application/x-www-form-urlencoded';
112 19
        $this->methodName = '';
113
114 19
        return $this;
115
    }
116
117 19
    private function constructFormData($method): array
118
    {
119 19
        $result = $this->checkSpecialConditions($method);
120
121 19
        switch ($this->formType) {
122
            case 'application/x-www-form-urlencoded':
123
                $formData = [
124 15
                    'form_params' => get_object_vars($method),
125
                ];
126 15
                break;
127
            case 'multipart/form-data':
128 4
                $formData = $this->buildMultipartFormData(get_object_vars($method), $result['id'], $result['stream']);
129 4
                break;
130
            default:
131
                $formData = [];
132
                break;
133
        }
134
135 19
        return $formData;
136
    }
137
138
    /**
139
     * Can perform any special checks needed to be performed before sending the actual request to Telegram
140
     *
141
     * This will return an array with data that will be different in each case (for now). This can be changed in the
142
     * future.
143
     *
144
     * @param $method
145
     * @return array
146
     */
147 19
    private function checkSpecialConditions($method): array
148
    {
149 19
        $return = [false];
150
151 19
        foreach ($method as $key => $value) {
152 17
            if (is_object($value)) {
153 5
                if (get_class($value) == 'unreal4u\\Telegram\\Types\\Custom\\InputFile') {
154
                    // If we are about to send a file, we must use the multipart/form-data way
155 4
                    $this->formType = 'multipart/form-data';
156
                    $return = [
157 4
                        'id' => $key,
158 4
                        'stream' => $value->getStream(),
159
                    ];
160 1
                } elseif (in_array('unreal4u\\InternalFunctionality\\AbstractKeyboardMethods', class_parents($value))) {
161
                    // If we are about to send a KeyboardMethod, we must send a serialized object
162 1
                    $method->$key = json_encode($value);
163 17
                    $return = [true];
164
                }
165
            }
166
        }
167
168 19
        return $return;
169
    }
170
171
    /**
172
     * Builds up the URL with which we can work with
173
     *
174
     * @param $call
175
     * @return string
176
     */
177 19
    protected function composeApiMethodUrl($call): string
178
    {
179 19
        $completeClassName = get_class($call);
180 19
        $this->methodName = lcfirst(substr($completeClassName, strrpos($completeClassName, '\\') + 1));
181
182 19
        return $this->apiUrl . $this->methodName;
183
    }
184
185
    /**
186
     * Builds up a multipart form-like array for Guzzle
187
     *
188
     * @param array $data The original object in array form
189
     * @param string $fileKeyName A file handler will be sent instead of a string, state here which field it is
190
     * @param mixed $stream The actual file handler
191
     * @return array Returns the actual formdata to be sent
192
     */
193 5
    private function buildMultipartFormData(array $data, string $fileKeyName, $stream): array
194
    {
195
        $formData = [
196 5
            'multipart' => [],
197
        ];
198
199 5
        foreach ($data as $id => $value) {
200
            // Always send as a string unless it's a file
201
            $multiPart = [
202 5
                'name' => $id,
203
                'contents' => null,
204
            ];
205
206 5
            if ($id === $fileKeyName) {
207 4
                $multiPart['contents'] = $stream;
208
            } else {
209 5
                $multiPart['contents'] = (string)$value;
210
            }
211
212 5
            $formData['multipart'][] = $multiPart;
213
        }
214
215 5
        return $formData;
216
    }
217
}
218