Passed
Push — master ( 527ce7...dff99b )
by Nils
02:47
created

CommandAddCommand::validateDescription()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 8
rs 10
1
<?php
2
3
namespace Startwind\Inventorio\Command;
4
5
use Symfony\Component\Console\Command\Command;
6
use Symfony\Component\Console\Input\ArrayInput;
7
use Symfony\Component\Console\Input\InputArgument;
8
use Symfony\Component\Console\Input\InputInterface;
9
use Symfony\Component\Console\Output\NullOutput;
10
use Symfony\Component\Console\Output\OutputInterface;
11
use Symfony\Component\Console\Question\Question;
12
use Symfony\Component\Process\ExecutableFinder;
13
14
class CommandAddCommand extends InventorioCommand
15
{
16
    protected static $defaultName = 'command:add';
17
    protected static $defaultDescription = 'Add a command to the remote console';
18
19
    /**
20
     * @inheritDoc
21
     */
22
    protected function configure(): void
23
    {
24
        parent::configure();
25
26
        $this
27
            ->addOption('commandId', null, InputArgument::OPTIONAL, 'The ID of the command')
28
            ->addOption('command', null, InputArgument::OPTIONAL, 'The command to execute (e.g., "ls")')
29
            ->addOption('description', null, InputArgument::OPTIONAL, 'Description of the command');
30
    }
31
32
    protected function execute(InputInterface $input, OutputInterface $output): int
33
    {
34
        $this->initConfiguration($input->getOption('configFile'));
35
36
        $helper = $this->getHelper('question');
37
38
        $commands = $this->config->getCommands();
39
40
        $commandId = $input->getOption('commandId');
41
        $command = $input->getOption('command');
42
        $description = $input->getOption('description');
43
44
        if ($commandId) {
45
            $validationResult = $this->validateCommandId($commandId, $commands, $output);
46
            if ($validationResult !== true) {
47
                return $validationResult;
48
            }
49
        } else {
50
            $commandId = $this->askCommandId($input, $output, $helper, $commands);
51
        }
52
53
        if ($command) {
54
            $validationResult = $this->validateCommand($command, $output);
55
            if ($validationResult !== true) {
56
                return $validationResult;
57
            }
58
        } else {
59
            $command = $this->askCommand($input, $output, $helper);
60
        }
61
62
        if ($description) {
63
            $validationResult = $this->validateDescription($description, $output);
64
            if ($validationResult !== true) {
65
                return $validationResult;
66
            }
67
        } else {
68
            $description = $this->askDescription($input, $output, $helper);
69
        }
70
71
        $this->config->addCommand($commandId, $command, $description);
72
73
        $output->writeln(['', '']);
74
75
        $output->writeln('- Command successfully added');
76
        $output->writeln('- Running the collect command to sync with inventorio.cloud');
77
78
        $this->getApplication()->find('collect')->run(new ArrayInput([]), new NullOutput());
79
80
        $output->writeln(['', '']);
81
        $output->writeln('To run this command via CLI, use the following:');
82
        $output->writeln('<info>inventorio command:add --commandId="' . $commandId . '" --command="' . $command . '" --description="' . $description . '"</info>');
83
84
        return Command::SUCCESS;
85
    }
86
87
    // Validierung für commandId
88
    private function validateCommandId(string $commandId, array $commands, OutputInterface $output)
89
    {
90
        if (array_key_exists($commandId, array_keys($commands))) {
91
            $output->writeln('<error>There is already a command registered with this command ID. Please try again.</error>');
92
            return Command::FAILURE;
93
        }
94
95
        if (!preg_match('/^[a-zA-Z0-9\-]+$/', $commandId)) {
96
            $output->writeln('<error>The Command ID must be alphanumeric and may contain hyphens. Please try again.</error>');
97
            return Command::FAILURE;
98
        }
99
100
        return true; // Validierung erfolgreich
101
    }
102
103
    // Validierung für command
104
    private function validateCommand(string $command, OutputInterface $output)
105
    {
106
        $finder = new ExecutableFinder();
107
        $commandParts = explode(' ', $command);
108
        $mainCommand = $commandParts[0];
109
110
        if ($mainCommand && !$finder->find($mainCommand)) {
111
            $output->writeln("<error>The command '$mainCommand' does not exist. Please try again.</error>");
112
            return Command::FAILURE;
113
        }
114
115
        return true; // Validierung erfolgreich
116
    }
117
118
    // Validierung für description
119
    private function validateDescription(string $description, OutputInterface $output)
120
    {
121
        if ($this->containsHtmlOrJs($description)) {
122
            $output->writeln('<error>The description must not contain HTML or JavaScript. Please try again.</error>');
123
            return Command::FAILURE;
124
        }
125
126
        return true; // Validierung erfolgreich
127
    }
128
129
    private function askCommandId(InputInterface $input, OutputInterface $output, $helper, array $commands): string
130
    {
131
        while (true) {
132
            $commandIdQuestion = new Question('Please enter the Command ID (alphanumeric, hyphens allowed): ');
133
            $commandId = $helper->ask($input, $output, $commandIdQuestion);
134
135
            if (array_key_exists($commandId, array_keys($commands))) {
136
                $output->writeln('<error>There is already a command registered with this command ID. Please try again.</error>');
137
                continue;
138
            }
139
140
            if ($commandId !== null && preg_match('/^[a-zA-Z0-9\-]+$/', $commandId)) {
141
                return $commandId;
142
            } else {
143
                $output->writeln('<error>The Command ID must be alphanumeric and may contain hyphens. Please try again.</error>');
144
            }
145
        }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
146
    }
147
148
    private function askCommand(InputInterface $input, OutputInterface $output, $helper): string
149
    {
150
        $finder = new ExecutableFinder();
151
152
        while (true) {
153
            $commandQuestion = new Question('Please enter the command (e.g., "ls"): ');
154
            $command = $helper->ask($input, $output, $commandQuestion);
155
156
            // Überprüfen, ob der Befehl nicht null oder leer ist
157
            if ($command === null || trim($command) === '') {
158
                $output->writeln('<error>The command cannot be empty. Please try again.</error>');
159
                continue;
160
            }
161
162
            // Separate the command from its parameters
163
            $commandParts = explode(' ', $command);
164
            $mainCommand = $commandParts[0];
165
166
            // Ensure the command is not null and the main command exists
167
            if ($mainCommand !== null && $finder->find($mainCommand)) {
168
                return $command;
169
            } else {
170
                $output->writeln("<error>The command '$mainCommand' does not exist. Please try again.</error>");
171
            }
172
        }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
173
    }
174
175
    private function askDescription(InputInterface $input, OutputInterface $output, $helper): string
176
    {
177
        while (true) {
178
            $descriptionQuestion = new Question('Please enter a description (no HTML/JS allowed): ');
179
            $description = $helper->ask($input, $output, $descriptionQuestion);
180
181
            // Ensure the description is not null and doesn't contain HTML/JS
182
            if ($description !== null && !$this->containsHtmlOrJs($description)) {
183
                return $description;
184
            } else {
185
                $output->writeln('<error>The description must not contain HTML or JavaScript. Please try again.</error>');
186
            }
187
        }
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
188
    }
189
190
    private function containsHtmlOrJs(string $text): bool
191
    {
192
        return preg_match('/<[^>]+>/', $text) || preg_match('/<script.*?>.*?<\/script>/is', $text);
193
    }
194
}
195