Passed
Push — master ( 7ecda6...2093cf )
by Armando
04:22 queued 02:31
created

Keyboard   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 190
Duplicated Lines 0 %

Test Coverage

Coverage 98.41%

Importance

Changes 0
Metric Value
eloc 51
dl 0
loc 190
ccs 62
cts 63
cp 0.9841
rs 9.92
c 0
b 0
f 0
wmc 31

11 Methods

Rating   Name   Duplication   Size   Complexity  
A remove() 0 3 1
B createFromParams() 0 36 8
A getKeyboardButtonClass() 0 3 2
A parseRow() 0 14 4
A addRow() 0 7 2
A forceReply() 0 3 1
A getKeyboardType() 0 3 2
A isInlineKeyboard() 0 3 1
A parseButton() 0 13 4
A __construct() 0 7 1
A validate() 0 13 5
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 18
        return $data;
113
    }
114
115
    /**
116
     * Create a new row in keyboard and add buttons.
117
     *
118
     * @return Keyboard
119
     */
120 2
    public function addRow(): Keyboard
121
    {
122 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...
123 2
            $this->{$this->getKeyboardType()}[] = $new_row;
124
        }
125
126 2
        return $this;
127
    }
128
129
    /**
130
     * Parse a given row to the correct array format.
131
     *
132
     * @param array|string $row
133
     *
134
     * @return array|null
135
     */
136 15
    protected function parseRow($row): ?array
137
    {
138 15
        if (!is_array($row)) {
139 2
            return null;
140
        }
141
142 13
        $new_row = [];
143 13
        foreach ($row as $button) {
144 12
            if (($new_button = $this->parseButton($button)) !== null) {
145 12
                $new_row[] = $new_button;
146
            }
147
        }
148
149 13
        return $new_row;
150
    }
151
152
    /**
153
     * Parse a given button to the correct KeyboardButton object type.
154
     *
155
     * @param array|string|KeyboardButton $button
156
     *
157
     * @return KeyboardButton|null
158
     */
159 12
    protected function parseButton($button): ?KeyboardButton
160
    {
161 12
        $button_class = $this->getKeyboardButtonClass();
162
163 12
        if ($button instanceof $button_class) {
164 6
            return $button;
165
        }
166
167 6
        if (!$this->isInlineKeyboard() || call_user_func([$button_class, 'couldBe'], $button)) {
168 6
            return new $button_class($button);
169
        }
170
171
        return null;
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177 18
    protected function validate(): void
178
    {
179 18
        $keyboard_type = $this->getKeyboardType();
180 18
        $keyboard      = $this->getProperty($keyboard_type);
181
182 18
        if ($keyboard !== null) {
183 18
            if (!is_array($keyboard)) {
184 2
                throw new TelegramException($keyboard_type . ' field is not an array!');
185
            }
186
187 16
            foreach ($keyboard as $item) {
188 15
                if (!is_array($item)) {
189 2
                    throw new TelegramException($keyboard_type . ' subfield is not an array!');
190
                }
191
            }
192
        }
193 14
    }
194
195
    /**
196
     * Remove the current custom keyboard and display the default letter-keyboard.
197
     *
198
     * @link https://core.telegram.org/bots/api/#replykeyboardremove
199
     *
200
     * @param array $data
201
     *
202
     * @return Keyboard
203
     */
204 1
    public static function remove(array $data = []): Keyboard
205
    {
206 1
        return new static(array_merge(['keyboard' => [], 'remove_keyboard' => true, 'selective' => false], $data));
207
    }
208
209
    /**
210
     * Display a reply interface to the user (act as if the user has selected the bot's message and tapped 'Reply').
211
     *
212
     * @link https://core.telegram.org/bots/api#forcereply
213
     *
214
     * @param array $data
215
     *
216
     * @return Keyboard
217
     */
218 1
    public static function forceReply(array $data = []): Keyboard
219
    {
220 1
        return new static(array_merge(['keyboard' => [], 'force_reply' => true, 'selective' => false], $data));
221
    }
222
}
223