Completed
Push — master ( d43687...cf40f1 )
by Camilo
03:28
created

TgLog::downloadFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2
Metric Value
dl 0
loc 6
ccs 0
cts 4
cp 0
rs 9.4286
cc 1
eloc 4
nc 1
nop 1
crap 2
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 14 and the first side effect is on line 3.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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 13
    public function __construct(string $botToken)
49
    {
50 13
        $this->botToken = $botToken;
51 13
        $this->constructApiUrl();
52 13
    }
53
54
    /**
55
     * Performs the actual telegram request to telegram's servers
56
     *
57
     * @param $method
58
     * @return mixed
59
     */
60 8
    public function performApiRequest($method)
61
    {
62 8
        $this->resetObjectValues();
63 8
        $jsonDecoded = $this->sendRequestToTelegram($method, $this->constructFormData($method));
64
65 6
        $returnObject = 'unreal4u\\Telegram\\Types\\' . $method::bindToObjectType();
66 6
        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 string
77
     */
78
    public function downloadFile(File $file): TelegramDocument
79
    {
80
        $url = 'https://api.telegram.org/file/bot' . $this->botToken . '/' . $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 13
    final private function constructApiUrl(): TgLog
90
    {
91 13
        $this->apiUrl = 'https://api.telegram.org/bot' . $this->botToken;
92 13
        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 8
    private function resetObjectValues(): TgLog
110
    {
111 8
        $this->formType = 'application/x-www-form-urlencoded';
112 8
        $this->methodName = '';
113
114 8
        return $this;
115
    }
116
117 8
    private function constructFormData($method): array
118
    {
119 8
        $result = $this->checkSpecialConditions($method);
120
121 8
        switch ($this->formType) {
122
            case 'application/x-www-form-urlencoded':
123
                $formData = [
124 8
                    'form_params' => get_object_vars($method),
125
                ];
126 8
                break;
127
            case 'multipart/form-data':
128
                $formData = $this->buildMultipartFormData(get_object_vars($method), $result['id'], $result['stream']);
129
                break;
130
            default:
131
                $formData = [];
132
                break;
133
        }
134
135 8
        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 8
    private function checkSpecialConditions($method): array
148
    {
149 8
        $return = [false];
150
151 8
        foreach ($method as $key => $value) {
152 6
            if (is_object($value)) {
153 1
                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
                    $this->formType = 'multipart/form-data';
156
                    $return = [
157
                        'id' => $key,
158
                        '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 6
                    $return = [true];
164
                }
165
            }
166
        }
167
168 8
        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 8
    protected function composeApiMethodUrl($call): string
178
    {
179 8
        $completeClassName = get_class($call);
180 8
        $this->methodName = lcfirst(substr($completeClassName, strrpos($completeClassName, '\\') + 1));
181
182 8
        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
    private function buildMultipartFormData(array $data, string $fileKeyName, $stream): array
194
    {
195
        $formData = [
196
            'multipart' => [],
197
        ];
198
199
        foreach ($data as $id => $value) {
200
            // Always send as a string unless it's a file
201
            $multiPart = [
202
                'name' => $id,
203
                'contents' => null,
204
            ];
205
206
            if ($id === $fileKeyName) {
207
                $multiPart['contents'] = $stream;
208
            } else {
209
                $multiPart['contents'] = (string)$value;
210
            }
211
212
            $formData['multipart'][] = $multiPart;
213
        }
214
215
        return $formData;
216
    }
217
}
218