Passed
Push — develop ( ce83ec...1c6380 )
by
unknown
18:21 queued 16:17
created

Keyboard::parseButton()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4.0466

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 3
nop 1
dl 0
loc 13
ccs 6
cts 7
cp 0.8571
crap 4.0466
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
 * 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   getIsPersistent()          Optional. Requests clients to always show the keyboard when the regular keyboard is hidden. Defaults to false, in which case the custom keyboard can be hidden and opened with a keyboard icon.
24
 * @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.
25
 * @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.
26
 * @method string getInputFieldPlaceholder() Optional. The placeholder to be shown in the input field when the keyboard is active; 1-64 characters
27
 * @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.
28
 *
29
 * @method $this setIsPersistent(bool $is_persistent)                      Optional. Requests clients to always show the keyboard when the regular keyboard is hidden. Defaults to false, in which case the custom keyboard can be hidden and opened with a keyboard icon.
30
 * @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.
31
 * @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.
32
 * @method $this setInputFieldPlaceholder(string $input_field_placeholder) Optional. The placeholder to be shown in the input field when the keyboard is active; 1-64 characters
33
 * @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.
34
 */
35
class Keyboard extends Entity
36
{
37 19
    public function __construct()
38
    {
39 19
        $data = $this->createFromParams(...func_get_args());
40 19
        parent::__construct($data);
41
42
        // Remove any empty buttons.
43 15
        $this->{$this->getKeyboardType()} = array_filter($this->{$this->getKeyboardType()});
44
    }
45
46
    /**
47
     * If this keyboard is an inline keyboard.
48
     *
49
     * @return bool
50
     */
51 19
    public function isInlineKeyboard(): bool
52
    {
53 19
        return $this instanceof InlineKeyboard;
54
    }
55
56
    /**
57
     * Get the proper keyboard button class for this keyboard.
58
     *
59
     * @return string
60
     */
61 13
    public function getKeyboardButtonClass(): string
62
    {
63 13
        return $this->isInlineKeyboard() ? InlineKeyboardButton::class : KeyboardButton::class;
64
    }
65
66
    /**
67
     * Get the type of keyboard, either "inline_keyboard" or "keyboard".
68
     *
69
     * @return string
70
     */
71 19
    public function getKeyboardType(): string
72
    {
73 19
        return $this->isInlineKeyboard() ? 'inline_keyboard' : 'keyboard';
74
    }
75
76
    /**
77
     * If no explicit keyboard is passed, try to create one from the parameters.
78
     *
79
     * @return array
80
     */
81 19
    protected function createFromParams(): array
82
    {
83 19
        $keyboard_type = $this->getKeyboardType();
84
85 19
        $args = func_get_args();
86
87
        // Force button parameters into individual rows.
88 19
        foreach ($args as &$arg) {
89 19
            !is_array($arg) && $arg = [$arg];
90
        }
91 19
        unset($arg);
92
93 19
        $data = reset($args);
94
95 19
        if ($from_data = array_key_exists($keyboard_type, (array) $data)) {
96 6
            $args = $data[$keyboard_type];
97
98
            // Make sure we're working with a proper row.
99 6
            if (!is_array($args)) {
100 2
                $args = [];
101
            }
102
        }
103
104 19
        $new_keyboard = [];
105 19
        foreach ($args as $row) {
106 16
            $new_keyboard[] = $this->parseRow($row);
107
        }
108
109 19
        if (!empty($new_keyboard)) {
110 16
            if (!$from_data) {
111 13
                $data = [];
112
            }
113 16
            $data[$keyboard_type] = $new_keyboard;
114
        }
115
116
        // If $args was empty, $data still contains `false`
117 19
        return $data ?: [];
118
    }
119
120
    /**
121
     * Create a new row in keyboard and add buttons.
122
     *
123
     * @return Keyboard
124
     */
125 2
    public function addRow(): Keyboard
126
    {
127 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...
128 2
            $this->{$this->getKeyboardType()}[] = $new_row;
129
        }
130
131 2
        return $this;
132
    }
133
134
    /**
135
     * Parse a given row to the correct array format.
136
     *
137
     * @param array|string $row
138
     *
139
     * @return array|null
140
     */
141 16
    protected function parseRow($row): ?array
142
    {
143 16
        if (!is_array($row)) {
144 2
            return null;
145
        }
146
147 14
        $new_row = [];
148 14
        foreach ($row as $button) {
149 13
            if (($new_button = $this->parseButton($button)) !== null) {
150 13
                $new_row[] = $new_button;
151
            }
152
        }
153
154 14
        return $new_row;
155
    }
156
157
    /**
158
     * Parse a given button to the correct KeyboardButton object type.
159
     *
160
     * @param array|string|KeyboardButton $button
161
     *
162
     * @return KeyboardButton|null
163
     */
164 13
    protected function parseButton($button): ?KeyboardButton
165
    {
166 13
        $button_class = $this->getKeyboardButtonClass();
167
168 13
        if ($button instanceof $button_class) {
169 6
            return $button;
170
        }
171
172 7
        if (!$this->isInlineKeyboard() || call_user_func([$button_class, 'couldBe'], $button)) {
173 7
            return new $button_class($button);
174
        }
175
176
        return null;
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182 19
    protected function validate(): void
183
    {
184 19
        $keyboard_type = $this->getKeyboardType();
185 19
        $keyboard      = $this->getProperty($keyboard_type);
186
187 19
        if ($keyboard !== null) {
188 19
            if (!is_array($keyboard)) {
189 2
                throw new TelegramException($keyboard_type . ' field is not an array!');
190
            }
191
192 17
            foreach ($keyboard as $item) {
193 16
                if (!is_array($item)) {
194 2
                    throw new TelegramException($keyboard_type . ' subfield is not an array!');
195
                }
196
            }
197
        }
198
    }
199
200
    /**
201
     * Remove the current custom keyboard and display the default letter-keyboard.
202
     *
203
     * @link https://core.telegram.org/bots/api/#replykeyboardremove
204
     *
205
     * @param array $data
206
     *
207
     * @return Keyboard
208
     */
209 1
    public static function remove(array $data = []): Keyboard
210
    {
211 1
        return new static(array_merge(['keyboard' => [], 'remove_keyboard' => true, 'selective' => false], $data));
212
    }
213
214
    /**
215
     * Display a reply interface to the user (act as if the user has selected the bot's message and tapped 'Reply').
216
     *
217
     * @link https://core.telegram.org/bots/api#forcereply
218
     *
219
     * @param array $data
220
     *
221
     * @return Keyboard
222
     */
223 1
    public static function forceReply(array $data = []): Keyboard
224
    {
225 1
        return new static(array_merge(['keyboard' => [], 'force_reply' => true, 'selective' => false], $data));
226
    }
227
}
228