Select::setItems()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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