Completed
Push — master ( 328402...1cd18c )
by Julien
587:13 queued 584:54
created

Select   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 87
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 46
dl 0
loc 87
rs 10
c 0
b 0
f 0
wmc 18

3 Methods

Rating   Name   Duplication   Size   Complexity  
A build() 0 8 1
A setItems() 0 4 1
C getDefaultValidator() 0 52 16
1
<?php
2
3
namespace TheAentMachine\Prompt;
4
5
use Symfony\Component\Console\Exception\InvalidArgumentException;
6
use Symfony\Component\Console\Question\ChoiceQuestion;
7
use Symfony\Component\Console\Question\Question;
8
use TheAentMachine\Prompt\Helper\ValidatorHelper;
9
use function Safe\preg_match;
10
use function Safe\sprintf;
11
12
class Select extends Input
13
{
14
    /** @var mixed[] */
15
    protected $items;
16
17
    /** @var bool */
18
    protected $multiselect = false;
19
20
    /**
21
     * @param mixed[] $items
22
     * @return self
23
     */
24
    public function setItems(array $items): self
25
    {
26
        $this->items = $items;
27
        return $this;
28
    }
29
30
    /**
31
     * @return Question
32
     */
33
    protected function build(): Question
34
    {
35
        $question = parent::build();
36
        $message = $question->getQuestion();
37
        $validator = $question->getValidator();
38
        $question = new ChoiceQuestion($message, $this->items, $this->default);
39
        $question->setValidator(ValidatorHelper::merge($validator, $this->getDefaultValidator()));
40
        return $question;
41
    }
42
43
    /**
44
     * Adjusted from Symfony\Component\Console\Question.
45
     * @return callable
46
     */
47
    private function getDefaultValidator(): callable
48
    {
49
        $choices = $this->items;
50
        $multiselect = $this->multiselect;
51
        $errorMessage = 'Value "%s" is invalid';
52
        $isAssoc = (bool)\count(\array_filter(\array_keys($choices), '\is_string'));
53
54
        return function ($selected) use ($choices, $errorMessage, $multiselect, $isAssoc) {
55
            // Collapse all spaces.
56
            $selectedChoices = \str_replace(' ', '', $selected);
57
            if (!empty($this->helpText) && $selectedChoices === '?') {
58
                return '?';
59
            }
60
            if ($multiselect) {
61
                // Check for a separated comma values
62
                if (!preg_match('/^[^,]+(?:,[^,]+)*$/', $selectedChoices, $matches)) {
63
                    throw new InvalidArgumentException(sprintf($errorMessage, $selected));
64
                }
65
                $selectedChoices = \explode(',', $selectedChoices);
66
            } else {
67
                $selectedChoices = array($selected);
68
            }
69
            $multiselectChoices = array();
70
            foreach ($selectedChoices as $value) {
71
                $results = array();
72
                foreach ($choices as $key => $choice) {
73
                    if ($choice === $value) {
74
                        $results[] = $key;
75
                    }
76
                }
77
                if (\count($results) > 1) {
78
                    throw new InvalidArgumentException(sprintf('The provided answer is ambiguous. Value should be one of %s.', \implode(' or ', $results)));
79
                }
80
                $result = \array_search($value, $choices);
81
                if (!$isAssoc) {
82
                    if (false !== $result) {
83
                        $result = $choices[$result];
84
                    } elseif (isset($choices[$value])) {
85
                        $result = $choices[$value];
86
                    }
87
                } elseif (false === $result && isset($choices[$value])) {
88
                    $result = $value;
89
                }
90
                if (false === $result) {
91
                    throw new InvalidArgumentException(sprintf($errorMessage, $value));
92
                }
93
                $multiselectChoices[] = (string) $result;
94
            }
95
            if ($multiselect) {
96
                return $multiselectChoices;
97
            }
98
            return \current($multiselectChoices);
99
        };
100
    }
101
}
102