Passed
Push — develop ( de24a7...37355b )
by
unknown
05:37 queued 13s
created

Entity::escapeMarkdownV2()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1.037

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 1
dl 0
loc 6
ccs 2
cts 3
cp 0.6667
crap 1.037
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
    private $parameters = [];
31
32
    /**
33
     * Entity constructor.
34
     *
35
     * @todo Get rid of the $bot_username, it shouldn't be here!
36
     *
37
     * @param array  $data
38
     * @param string $bot_username
39
     */
40 74
    public function __construct(array $data, string $bot_username = '')
41
    {
42
        //Make sure we're not raw_data inception-ing
43 74
        if (array_key_exists('raw_data', $data)) {
44 11
            if ($data['raw_data'] === null) {
45 11
                unset($data['raw_data']);
46
            }
47
        } else {
48 68
            $data['raw_data'] = $data;
49
        }
50
51 74
        $data['bot_username'] = $bot_username;
52 74
        $this->assignMemberVariables($data);
53 74
        $this->validate();
54
    }
55
56
    /**
57
     * Dynamically sets a parameter.
58
     *
59
     * @param string $name
60
     * @param        $value
61 2
     * @return void
62
     */
63 2
    public function __set(string $name, $value) : void {
64
        $this->parameters[$name] = $value;
65
    }
66 2
67 2
    /**
68
     * Gets a dynamic parameter.
69 2
     *
70
     * @param string $name
71
     * @return mixed|null
72
     */
73
    public function __get(string $name) {
74
        return $this->parameters[$name] ?? null;
75
    }
76
77 2
    /**
78
     * Return the data that should be serialized for Telegram.
79 2
     *
80
     * @return array
81
     */
82
    public function jsonSerialize(): array
83
    {
84
        $data = get_object_vars($this);
85
86
        // Delete unnecessary data
87
        unset($data['raw_data']);
88
        unset($data['bot_username']);
89
90
        return $data;
91
    }
92
93
    /**
94
     * Perform to json
95
     *
96
     * @return string
97 74
     */
98
    public function toJson(): string
99 74
    {
100 74
        return json_encode($this);
101 74
    }
102
103
    /**
104
     * Perform to string
105
     *
106
     * @return string
107
     */
108
    public function __toString()
109
    {
110 34
        return $this->toJson();
111
    }
112 34
113
    /**
114
     * Helper to set member variables
115
     *
116
     * @param array $data
117
     */
118 68
    protected function assignMemberVariables(array $data): void
119
    {
120 68
        foreach ($data as $key => $value) {
121
            $key = $this->fixThumbnailRename($key);
122
            $this->$key = $value;
123
        }
124
    }
125
126
    /**
127
     * Get the list of the properties that are themselves Entities
128
     *
129
     * @return array
130 65
     */
131
    protected function subEntities(): array
132 65
    {
133
        return [];
134
    }
135
136
    /**
137
     * Perform any special entity validation
138
     */
139
    protected function validate(): void
140
    {
141
    }
142
143 57
    /**
144
     * Get a property from the current Entity
145 57
     *
146
     * @param string $property
147
     * @param mixed  $default
148 57
     *
149 57
     * @return mixed
150
     */
151 57
    public function getProperty(string $property, $default = null)
152 57
    {
153 56
        return $this->$property ?? $default;
154
    }
155 56
156
    /**
157 53
     * Return the variable for the called getter or magically set properties dynamically.
158
     *
159 53
     * @param $method
160 13
     * @param $args
161
     *
162 13
     * @return mixed|null
163 1
     */
164
    public function __call($method, $args)
165
    {
166 12
        $method = $this->fixThumbnailRename($method);
167
168
        //Convert method to snake_case (which is the name of the property)
169 54
        $property_name = mb_strtolower(ltrim(preg_replace('/[A-Z]/', '_$0', substr($method, 3)), '_'));
170
        $property_name = $this->fixThumbnailRename($property_name);
171 2
172
        $action = substr($method, 0, 3);
173 2
        if ($action === 'get') {
174 2
            $property = $this->getProperty($property_name);
175 2
176
            if ($property !== null) {
177 2
                //Get all sub-Entities of the current Entity
178
                $sub_entities = $this->subEntities();
179
180
                if (isset($sub_entities[$property_name])) {
181 19
                    $class = $sub_entities[$property_name];
182
183
                    if (is_array($class)) {
184
                        return $this->makePrettyObjectArray(reset($class), $property_name);
185
                    }
186
187
                    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

187
                    return Factory::resolveEntityClass($class, $property, /** @scrutinizer ignore-type */ $this->getProperty('bot_username'));
Loading history...
188
                }
189
190
                return $property;
191
            }
192 74
        } elseif ($action === 'set') {
193
            // Limit setters to specific classes.
194 74
            if ($this instanceof InlineEntity || $this instanceof InputMedia || $this instanceof Keyboard || $this instanceof KeyboardButton) {
195
                $this->$property_name = $args[0];
196
                $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...
197
198
                return $this;
199
            }
200
        }
201
202
        return null;
203
    }
204
205
    /**
206
     * BC for renamed thumb -> thumbnail methods and fields
207
     *
208
     * @todo Remove after a few versions.
209
     *
210
     * @param string $name
211
     * @return string
212 1
     */
213
    protected function fixThumbnailRename(string $name): string
214 1
    {
215 1
        return self::$fixThumbnailRename ? preg_replace('/([Tt])humb(nail)?/', '$1humbnail', $name, -1, $count) : $name;
216
217 1
        /*if ($count) {
218 1
            // Notify user that there are still outdated method calls?
219 1
        }*/
220
    }
221
222 1
    /**
223
     * Return an array of nice objects from an array of object arrays
224
     *
225
     * This method is used to generate pretty object arrays
226
     * mainly for PhotoSize and Entities object arrays.
227
     *
228
     * @param string $class
229
     * @param string $property_name
230
     *
231
     * @return array
232
     */
233
    protected function makePrettyObjectArray(string $class, string $property_name): array
234 2
    {
235
        $objects      = [];
236 2
        $bot_username = $this->getProperty('bot_username');
237 2
238 2
        $properties = array_filter($this->getProperty($property_name) ?: []);
239 2
        foreach ($properties as $property) {
240 2
            $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

240
            $objects[] = Factory::resolveEntityClass($class, $property, /** @scrutinizer ignore-type */ $bot_username);
Loading history...
241
        }
242
243
        return $objects;
244
    }
245
246
    /**
247
     * Escape markdown (v1) special characters
248
     *
249
     * @see https://core.telegram.org/bots/api#markdown-style
250
     *
251
     * @param string $string
252 1
     *
253
     * @return string
254 1
     */
255 1
    public static function escapeMarkdown(string $string): string
256 1
    {
257 1
        return str_replace(
258 1
            ['[', '`', '*', '_',],
259
            ['\[', '\`', '\*', '\_',],
260
            $string
261
        );
262
    }
263
264
    /**
265
     * Escape markdown (v2) special characters
266
     *
267
     * @see https://core.telegram.org/bots/api#markdownv2-style
268
     *
269
     * @param string $string
270
     *
271
     * @return string
272
     */
273 3
    public static function escapeMarkdownV2(string $string): string
274
    {
275
        return str_replace(
276 3
            ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'],
277
            ['\_', '\*', '\[', '\]', '\(', '\)', '\~', '\`', '\>', '\#', '\+', '\-', '\=', '\|', '\{', '\}', '\.', '\!'],
278
            $string
279
        );
280
    }
281 3
282 3
    /**
283
     * Try to mention the user
284 3
     *
285
     * Mention the user with the username otherwise print first and last name
286 3
     * if the $escape_markdown argument is true special characters are escaped from the output
287 3
     *
288 3
     * @todo What about MarkdownV2?
289 3
     *
290
     * @param bool $escape_markdown
291
     *
292
     * @return string
293 3
     */
294 1
    public function tryMention($escape_markdown = false): string
295
    {
296
        // TryMention only makes sense for the User and Chat entity.
297 3
        if (!($this instanceof User || $this instanceof Chat)) {
298
            return '';
299
        }
300
301
        //Try with the username first...
302
        $name        = $this->getProperty('username');
303
        $is_username = $name !== null;
304
305
        if ($name === null) {
306
            //...otherwise try with the names.
307
            $name      = $this->getProperty('first_name');
308
            $last_name = $this->getProperty('last_name');
309
            if ($last_name !== null) {
310
                $name .= ' ' . $last_name;
311
            }
312
        }
313
314
        if ($escape_markdown) {
315
            $name = self::escapeMarkdown($name);
316
        }
317
318
        return ($is_username ? '@' : '') . $name;
319
    }
320
}
321