Completed
Push — master ( 9f091f...8b6ddc )
by Camilo
03:11
created

TgLog   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 207
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 81.25%
Metric Value
wmc 18
lcom 1
cbo 5
dl 0
loc 207
ccs 52
cts 64
cp 0.8125
rs 10

10 Methods

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