Keyboard::validate()   A
last analyzed

Complexity

Conditions 5
Paths 5

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 5
nop 0
dl 0
loc 13
ccs 9
cts 9
cp 1
crap 5
rs 9.6111
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
 * Written by Marco Boretto <[email protected]>
12
 */
13
14
namespace Longman\TelegramBot\Entities;
15
16
use Longman\TelegramBot\Exception\TelegramException;
17
18
/**
19
 * Class Keyboard
20
 *
21
 * @link https://core.telegram.org/bots/api#replykeyboardmarkup
22
 *
23
 * @method bool getResizeKeyboard()  Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard.
24
 * @method bool getOneTimeKeyboard() Optional. Requests clients to remove the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat – the user can press a special button in the input field to see the custom keyboard again. Defaults to false.
25
 * @method bool getSelective()       Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
26
 *
27
 * @method $this setResizeKeyboard(bool $resize_keyboard)    Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard.
28
 * @method $this setOneTimeKeyboard(bool $one_time_keyboard) Optional. Requests clients to remove the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat – the user can press a special button in the input field to see the custom keyboard again. Defaults to false.
29
 * @method $this setSelective(bool $selective)               Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
30
 */
31
class Keyboard extends Entity
32
{
33 18
    public function __construct()
34
    {
35 18
        $data = $this->createFromParams(...func_get_args());
36 18
        parent::__construct($data);
37
38
        // Remove any empty buttons.
39 14
        $this->{$this->getKeyboardType()} = array_filter($this->{$this->getKeyboardType()});
40 14
    }
41
42
    /**
43
     * If this keyboard is an inline keyboard.
44
     *
45
     * @return bool
46
     */
47 18
    public function isInlineKeyboard(): bool
48
    {
49 18
        return $this instanceof InlineKeyboard;
50
    }
51
52
    /**
53
     * Get the proper keyboard button class for this keyboard.
54
     *
55
     * @return string
56
     */
57 12
    public function getKeyboardButtonClass(): string
58
    {
59 12
        return $this->isInlineKeyboard() ? InlineKeyboardButton::class : KeyboardButton::class;
60
    }
61
62
    /**
63
     * Get the type of keyboard, either "inline_keyboard" or "keyboard".
64
     *
65
     * @return string
66
     */
67 18
    public function getKeyboardType(): string
68
    {
69 18
        return $this->isInlineKeyboard() ? 'inline_keyboard' : 'keyboard';
70
    }
71
72
    /**
73
     * If no explicit keyboard is passed, try to create one from the parameters.
74
     *
75
     * @return array
76
     */
77 18
    protected function createFromParams(): array
78
    {
79 18
        $keyboard_type = $this->getKeyboardType();
80
81 18
        $args = func_get_args();
82
83
        // Force button parameters into individual rows.
84 18
        foreach ($args as &$arg) {
85 18
            !is_array($arg) && $arg = [$arg];
86
        }
87 18
        unset($arg);
88
89 18
        $data = reset($args);
90
91 18
        if ($from_data = array_key_exists($keyboard_type, (array) $data)) {
92 6
            $args = $data[$keyboard_type];
93
94
            // Make sure we're working with a proper row.
95 6
            if (!is_array($args)) {
96 2
                $args = [];
97
            }
98
        }
99
100 18
        $new_keyboard = [];
101 18
        foreach ($args as $row) {
102 15
            $new_keyboard[] = $this->parseRow($row);
103
        }
104
105 18
        if (!empty($new_keyboard)) {
106 15
            if (!$from_data) {
107 12
                $data = [];
108
            }
109 15
            $data[$keyboard_type] = $new_keyboard;
110
        }
111
112
        // If $args was empty, $data still contains `false`
113 18
        return $data ?: [];
114
    }
115
116
    /**
117
     * Create a new row in keyboard and add buttons.
118
     *
119
     * @return Keyboard
120
     */
121 2
    public function addRow(): Keyboard
122
    {
123 2
        if (($new_row = $this->parseRow(func_get_args())) !== null) {
0 ignored issues
show
introduced by
The condition $new_row = $this->parseR...nc_get_args()) !== null is always true.
Loading history...
124 2
            $this->{$this->getKeyboardType()}[] = $new_row;
125
        }
126
127 2
        return $this;
128
    }
129
130
    /**
131
     * Parse a given row to the correct array format.
132
     *
133
     * @param array|string $row
134
     *
135
     * @return array|null
136
     */
137 15
    protected function parseRow($row): ?array
138
    {
139 15
        if (!is_array($row)) {
140 2
            return null;
141
        }
142
143 13
        $new_row = [];
144 13
        foreach ($row as $button) {
145 12
            if (($new_button = $this->parseButton($button)) !== null) {
146 12
                $new_row[] = $new_button;
147
            }
148
        }
149
150 13
        return $new_row;
151
    }
152
153
    /**
154
     * Parse a given button to the correct KeyboardButton object type.
155
     *
156
     * @param array|string|KeyboardButton $button
157
     *
158
     * @return KeyboardButton|null
159
     */
160 12
    protected function parseButton($button): ?KeyboardButton
161
    {
162 12
        $button_class = $this->getKeyboardButtonClass();
163
164 12
        if ($button instanceof $button_class) {
165 6
            return $button;
166
        }
167
168 6
        if (!$this->isInlineKeyboard() || call_user_func([$button_class, 'couldBe'], $button)) {
169 6
            return new $button_class($button);
170
        }
171
172
        return null;
173
    }
174
175
    /**
176
     * {@inheritdoc}
177
     */
178 18
    protected function validate(): void
179
    {
180 18
        $keyboard_type = $this->getKeyboardType();
181 18
        $keyboard      = $this->getProperty($keyboard_type);
182
183 18
        if ($keyboard !== null) {
184 18
            if (!is_array($keyboard)) {
185 2
                throw new TelegramException($keyboard_type . ' field is not an array!');
186
            }
187
188 16
            foreach ($keyboard as $item) {
189 15
                if (!is_array($item)) {
190 2
                    throw new TelegramException($keyboard_type . ' subfield is not an array!');
191
                }
192
            }
193
        }
194 14
    }
195
196
    /**
197
     * Remove the current custom keyboard and display the default letter-keyboard.
198
     *
199
     * @link https://core.telegram.org/bots/api/#replykeyboardremove
200
     *
201
     * @param array $data
202
     *
203
     * @return Keyboard
204
     */
205 1
    public static function remove(array $data = []): Keyboard
206
    {
207 1
        return new static(array_merge(['keyboard' => [], 'remove_keyboard' => true, 'selective' => false], $data));
208
    }
209
210
    /**
211
     * Display a reply interface to the user (act as if the user has selected the bot's message and tapped 'Reply').
212
     *
213
     * @link https://core.telegram.org/bots/api#forcereply
214
     *
215
     * @param array $data
216
     *
217
     * @return Keyboard
218
     */
219 1
    public static function forceReply(array $data = []): Keyboard
220
    {
221 1
        return new static(array_merge(['keyboard' => [], 'force_reply' => true, 'selective' => false], $data));
222
    }
223
}
224