Passed
Pull Request — develop (#1389)
by Armando
17:06 queued 07:05
created

Entity::fixThumbnailRename()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of the TelegramBot package.
5
 *
6
 * (c) Avtandil Kikabidze aka LONGMAN <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Longman\TelegramBot\Entities;
13
14
use Longman\TelegramBot\Entities\InlineQuery\InlineEntity;
15
use Longman\TelegramBot\Entities\InputMedia\InputMedia;
16
17
/**
18
 * Class Entity
19
 *
20
 * This is the base class for all entities.
21
 *
22
 * @link https://core.telegram.org/bots/api#available-types
23
 *
24
 * @method array  getRawData()     Get the raw data passed to this entity
25
 * @method string getBotUsername() Return the bot name passed to this entity
26
 */
27
abstract class Entity implements \JsonSerializable
28
{
29
    public static $fixThumbnailRename = true;
30
31
    /**
32
     * Entity constructor.
33
     *
34
     * @todo Get rid of the $bot_username, it shouldn't be here!
35
     *
36
     * @param array  $data
37
     * @param string $bot_username
38
     */
39 81
    public function __construct(array $data, string $bot_username = '')
40
    {
41
        //Make sure we're not raw_data inception-ing
42 81
        if (array_key_exists('raw_data', $data)) {
43 11
            if ($data['raw_data'] === null) {
44 11
                unset($data['raw_data']);
45
            }
46
        } else {
47 75
            $data['raw_data'] = $data;
48
        }
49
50 81
        $data['bot_username'] = $bot_username;
51 81
        $this->assignMemberVariables($data);
52 81
        $this->validate();
53
    }
54
55
    /**
56
     * Return the data that should be serialized for Telegram.
57
     *
58
     * @return array
59
     */
60 2
    public function jsonSerialize(): array
61
    {
62 2
        $data = get_object_vars($this);
63
64
        // Delete unnecessary data
65 2
        unset($data['raw_data']);
66 2
        unset($data['bot_username']);
67
68 2
        return $data;
69
    }
70
71
    /**
72
     * Perform to json
73
     *
74
     * @return string
75
     */
76 2
    public function toJson(): string
77
    {
78 2
        return json_encode($this);
79
    }
80
81
    /**
82
     * Perform to string
83
     *
84
     * @return string
85
     */
86
    public function __toString()
87
    {
88
        return $this->toJson();
89
    }
90
91
    /**
92
     * Helper to set member variables
93
     *
94
     * @param array $data
95
     */
96 81
    protected function assignMemberVariables(array $data): void
97
    {
98 81
        foreach ($data as $key => $value) {
99 81
            $key = $this->fixThumbnailRename($key);
100
            $this->$key = $value;
101
        }
102
    }
103
104
    /**
105
     * Get the list of the properties that are themselves Entities
106
     *
107
     * @return array
108 34
     */
109
    protected function subEntities(): array
110 34
    {
111
        return [];
112
    }
113
114
    /**
115
     * Perform any special entity validation
116 57
     */
117
    protected function validate(): void
118 57
    {
119
    }
120
121
    /**
122
     * Get a property from the current Entity
123
     *
124
     * @param string $property
125
     * @param mixed  $default
126
     *
127
     * @return mixed
128 74
     */
129
    public function getProperty(string $property, $default = null)
130 74
    {
131
        return $this->$property ?? $default;
132
    }
133
134
    /**
135
     * Return the variable for the called getter or magically set properties dynamically.
136
     *
137
     * @param $method
138
     * @param $args
139
     *
140
     * @return mixed|null
141 61
     */
142
    public function __call($method, $args)
143
    {
144 61
        $method = $this->fixThumbnailRename($method);
145
146 61
        //Convert method to snake_case (which is the name of the property)
147 61
        $property_name = mb_strtolower(ltrim(preg_replace('/[A-Z]/', '_$0', substr($method, 3)), '_'));
148 61
        $property_name = $this->fixThumbnailRename($property_name);
149
150 61
        $action = substr($method, 0, 3);
151
        if ($action === 'get') {
152 57
            $property = $this->getProperty($property_name);
153
154 57
            if ($property !== null) {
155 15
                //Get all sub-Entities of the current Entity
156
                $sub_entities = $this->subEntities();
157 15
158 1
                if (isset($sub_entities[$property_name])) {
159
                    $class = $sub_entities[$property_name];
160
161 14
                    if (is_array($class)) {
162
                        return $this->makePrettyObjectArray(reset($class), $property_name);
163
                    }
164 60
165
                    return Factory::resolveEntityClass($class, $property, $this->getProperty('bot_username'));
0 ignored issues
show
Bug introduced by
It seems like $this->getProperty('bot_username') can also be of type null; however, parameter $bot_username of Longman\TelegramBot\Enti...y::resolveEntityClass() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

165
                    return Factory::resolveEntityClass($class, $property, /** @scrutinizer ignore-type */ $this->getProperty('bot_username'));
Loading history...
166 4
                }
167
168 4
                return $property;
169 4
            }
170 4
        } elseif ($action === 'set') {
171
            // Limit setters to specific classes.
172 4
            if ($this instanceof InlineEntity || $this instanceof InputMedia || $this instanceof Keyboard || $this instanceof KeyboardButton) {
173
                $this->$property_name = $args[0];
174
                $this->raw_data[$property_name] = $args[0];
0 ignored issues
show
Bug Best Practice introduced by
The property raw_data does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
175
176 32
                return $this;
177
            }
178
        }
179
180
        return null;
181
    }
182
183
    /**
184
     * BC for renamed thumb -> thumbnail methods and fields
185
     *
186
     * @todo Remove after a few versions.
187
     *
188
     * @param string $name
189
     * @return string
190 1
     */
191
    protected function fixThumbnailRename(string $name): string
192 1
    {
193 1
        return self::$fixThumbnailRename ? preg_replace('/([Tt])humb(nail)?/', '$1humbnail', $name, -1, $count) : $name;
194
195 1
        /*if ($count) {
196 1
            // Notify user that there are still outdated method calls?
197 1
        }*/
198
    }
199
200 1
    /**
201
     * Return an array of nice objects from an array of object arrays
202
     *
203
     * This method is used to generate pretty object arrays
204
     * mainly for PhotoSize and Entities object arrays.
205
     *
206
     * @param string $class
207
     * @param string $property_name
208
     *
209
     * @return array
210
     */
211
    protected function makePrettyObjectArray(string $class, string $property_name): array
212 2
    {
213
        $objects      = [];
214 2
        $bot_username = $this->getProperty('bot_username');
215
216
        $properties = array_filter($this->getProperty($property_name) ?: []);
217
        foreach ($properties as $property) {
218
            $objects[] = Factory::resolveEntityClass($class, $property, $bot_username);
0 ignored issues
show
Bug introduced by
It seems like $bot_username can also be of type null; however, parameter $bot_username of Longman\TelegramBot\Enti...y::resolveEntityClass() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

218
            $objects[] = Factory::resolveEntityClass($class, $property, /** @scrutinizer ignore-type */ $bot_username);
Loading history...
219
        }
220
221
        return $objects;
222
    }
223
224
    /**
225
     * Escape markdown (v1) special characters
226
     *
227
     * @see https://core.telegram.org/bots/api#markdown-style
228
     *
229
     * @param string $string
230 1
     *
231
     * @return string
232 1
     */
233
    public static function escapeMarkdown(string $string): string
234
    {
235
        return str_replace(
236
            ['[', '`', '*', '_',],
237
            ['\[', '\`', '\*', '\_',],
238
            $string
239
        );
240
    }
241
242
    /**
243
     * Escape markdown (v2) special characters
244
     *
245
     * @see https://core.telegram.org/bots/api#markdownv2-style
246
     *
247
     * @param string $string
248
     *
249
     * @return string
250
     */
251 3
    public static function escapeMarkdownV2(string $string): string
252
    {
253
        return str_replace(
254 3
            ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'],
255
            ['\_', '\*', '\[', '\]', '\(', '\)', '\~', '\`', '\>', '\#', '\+', '\-', '\=', '\|', '\{', '\}', '\.', '\!'],
256
            $string
257
        );
258
    }
259 3
260 3
    /**
261
     * Try to mention the user
262 3
     *
263
     * Mention the user with the username otherwise print first and last name
264 3
     * if the $escape_markdown argument is true special characters are escaped from the output
265 3
     *
266 3
     * @todo What about MarkdownV2?
267 3
     *
268
     * @param bool $escape_markdown
269
     *
270
     * @return string
271 3
     */
272 1
    public function tryMention($escape_markdown = false): string
273
    {
274
        // TryMention only makes sense for the User and Chat entity.
275 3
        if (!($this instanceof User || $this instanceof Chat)) {
276
            return '';
277
        }
278
279
        //Try with the username first...
280
        $name        = $this->getProperty('username');
281
        $is_username = $name !== null;
282
283
        if ($name === null) {
284
            //...otherwise try with the names.
285
            $name      = $this->getProperty('first_name');
286
            $last_name = $this->getProperty('last_name');
287
            if ($last_name !== null) {
288
                $name .= ' ' . $last_name;
289
            }
290
        }
291
292
        if ($escape_markdown) {
293
            $name = self::escapeMarkdown($name);
294
        }
295
296
        return ($is_username ? '@' : '') . $name;
297
    }
298
}
299