Input::acceptableFormatted()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 4
cts 5
cp 0.8
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2.032
1
<?php
2
3
namespace League\CLImate\TerminalObject\Dynamic;
4
5
use League\CLImate\Util\Reader\ReaderInterface;
6
use League\CLImate\Util\Reader\Stdin;
7
8
class Input extends InputAbstract
9
{
10
    /**
11
     * An array of acceptable responses
12
     *
13
     * @var array|object $acceptable
14
     */
15
    protected $acceptable;
16
17
    /**
18
     * Whether we should be strict about the response given
19
     *
20
     * @var boolean $strict
21
     */
22
    protected $strict = false;
23
24
    /**
25
     * Whether to accept multiple lines of input
26
     *
27
     * Terminated by ^D
28
     *
29
     * @var boolean $multiLine
30
     */
31
    protected $multiLine = false;
32
33
    /**
34
     * Whether we should display the acceptable responses to the user
35
     *
36
     * @var boolean $show_acceptable
37
     */
38
    protected $show_acceptable = false;
39
40
    /**
41
     * A default answer in the case of no user response,
42
     * prevents re-prompting
43
     *
44
     * @var string
45
     */
46
    protected $default = '';
47
48 68
    public function __construct($prompt, ReaderInterface $reader = null)
49
    {
50 68
        $this->prompt = $prompt;
51 68
        $this->reader = $reader ?: new Stdin();
52 68
    }
53
54
    /**
55
     * Do it! Prompt the user for information!
56
     *
57
     * @return string
58
     */
59 64
    public function prompt()
60
    {
61 64
        $this->writePrompt();
62
63 64
        $response = $this->valueOrDefault($this->getUserInput());
64
65 64
        if ($this->isValidResponse($response)) {
66 64
            return $response;
67
        }
68
69 24
        return $this->prompt();
70
    }
71
72
    /**
73
     * Define the acceptable responses and whether or not to
74
     * display them to the user
75
     *
76
     * @param  array|object $acceptable
77
     * @param  boolean $show
78
     *
79
     * @return \League\CLImate\TerminalObject\Dynamic\Input
80
     */
81 48
    public function accept($acceptable, $show = false)
82
    {
83 48
        $this->acceptable      = $acceptable;
84 48
        $this->show_acceptable = $show;
85
86 48
        return $this;
87
    }
88
89
    /**
90
     * Define whether we should be strict about exact responses
91
     *
92
     * @return \League\CLImate\TerminalObject\Dynamic\Input
93
     */
94 24
    public function strict()
95
    {
96 24
        $this->strict = true;
97
98 24
        return $this;
99
    }
100
101
    /**
102
     * Set a default response
103
     *
104
     * @param string $default
105
     *
106
     * @return \League\CLImate\TerminalObject\Dynamic\Input
107
     */
108 12
    public function defaultTo($default)
109
    {
110 12
        $this->default = $default;
111
112 12
        return $this;
113
    }
114
115
    /**
116
     * Set multiline input to true
117
     *
118
     * @return \League\CLImate\TerminalObject\Dynamic\Input
119
     */
120 8
    public function multiLine()
121
    {
122 8
        $this->multiLine = true;
123
124 8
        return $this;
125
    }
126
127
    /**
128
     * @return string
129
     */
130 64
    protected function getUserInput()
131
    {
132 64
        if ($this->multiLine) {
133 8
            return $this->reader->multiLine();
134
        }
135
136 56
        return $this->reader->line();
137
    }
138
139
    /**
140
     * Write out the formatted prompt
141
     */
142 68
    protected function writePrompt()
143
    {
144 68
        $prompt = $this->parser->apply($this->promptFormatted());
145
146 68
        $this->output->sameLine()->write($prompt);
147 68
    }
148
149
    /**
150
     * If no response was given and there is a default, return it,
151
     * otherwise return response
152
     *
153
     * @param string $response
154
     *
155
     * @return string
156
     */
157 64
    protected function valueOrDefault($response)
158
    {
159 64
        if (strlen($response) == 0 && strlen($this->default)) {
160 8
            return $this->default;
161
        }
162
163 56
        return $response;
164
    }
165
166
    /**
167
     * Format the acceptable responses as options
168
     *
169
     * @return string
170
     */
171 28
    protected function acceptableFormatted()
172
    {
173 28
        if (!is_array($this->acceptable)) {
174
            return '';
175
        }
176
177 28
        $acceptable = array_map([$this, 'acceptableItemFormatted'], $this->acceptable);
178
179 28
        return '[' . implode('/', $acceptable) . ']';
180
    }
181
182
    /**
183
     * Format the acceptable item depending on whether it is the default or not
184
     *
185
     * @param string $item
186
     *
187
     * @return string
188
     */
189 28
    protected function acceptableItemFormatted($item)
190
    {
191 28
        if ($item == $this->default) {
192 8
            return '<bold>' . $item . '</bold>';
193
        }
194
195 28
        return $item;
196
    }
197
198
    /**
199
     * Format the prompt incorporating spacing and any acceptable options
200
     *
201
     * @return string
202
     */
203 68
    protected function promptFormatted()
204
    {
205 68
        $prompt = $this->prompt . ' ';
206
207 68
        if ($this->show_acceptable) {
208 28
            $prompt .= $this->acceptableFormatted() . ' ';
209 28
        }
210
211 68
        return $prompt;
212
    }
213
214
    /**
215
     * Apply some string manipulation functions for normalization
216
     *
217
     * @param string|array $var
218
     * @return array
219
     */
220 20
    protected function levelPlayingField($var)
221
    {
222 20
        $levelers = ['trim', 'mb_strtolower'];
223
224 20
        foreach ($levelers as $leveler) {
225 20
            if (is_array($var)) {
226 20
                $var = array_map($leveler, $var);
227 20
            } else {
228 20
                $var = $leveler($var);
229
            }
230 20
        }
231
232 20
        return $var;
233
    }
234
235
    /**
236
     * Determine whether or not the acceptable property is of type closure
237
     *
238
     * @return boolean
239
     */
240 48
    protected function acceptableIsClosure()
241
    {
242 48
        return (is_object($this->acceptable) && $this->acceptable instanceof \Closure);
243
    }
244
245
    /**
246
     * Determine if the user's response is in the acceptable responses array
247
     *
248
     * @param string $response
249
     *
250
     * @return boolean $response
251
     */
252 40
    protected function isAcceptableResponse($response)
253
    {
254 40
        if ($this->strict) {
255 20
            return in_array($response, $this->acceptable);
256
        }
257
258 20
        $acceptable = $this->levelPlayingField($this->acceptable);
259 20
        $response   = $this->levelPlayingField($response);
260
261 20
        return in_array($response, $acceptable);
262
    }
263
264
    /**
265
     * Determine if the user's response is valid based on the current settings
266
     *
267
     * @param string $response
268
     *
269
     * @return boolean $response
270
     */
271 64
    protected function isValidResponse($response)
272
    {
273 64
        if (empty($this->acceptable)) {
274 16
            return true;
275
        }
276
277 48
        if ($this->acceptableIsClosure()) {
278 8
            return call_user_func($this->acceptable, $response);
279
        }
280
281 40
        return $this->isAcceptableResponse($response);
282
    }
283
}
284