CommandAddCommand   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 180
Duplicated Lines 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
eloc 91
c 2
b 1
f 0
dl 0
loc 180
rs 9.68
wmc 34

9 Methods

Rating   Name   Duplication   Size   Complexity  
A validateCommandId() 0 13 3
B execute() 0 53 7
A configure() 0 8 1
A askCommand() 0 23 6
A askDescription() 0 11 4
A askCommandId() 0 15 5
A containsHtmlOrJs() 0 3 2
A validateDescription() 0 8 2
A validateCommand() 0 14 4
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
        $pseudoCommands = ['cd'];
111
112
        if (!in_array($mainCommand, $pseudoCommands) && $mainCommand && !$finder->find($mainCommand)) {
113
            $output->writeln("<error>The command '$mainCommand' does not exist. Please try again.</error>");
114
            return Command::FAILURE;
115
        }
116
117
        return true;
118
    }
119
120
    private function validateDescription(string $description, OutputInterface $output)
121
    {
122
        if ($this->containsHtmlOrJs($description)) {
123
            $output->writeln('<error>The description must not contain HTML or JavaScript. Please try again.</error>');
124
            return Command::FAILURE;
125
        }
126
127
        return true; // Validierung erfolgreich
128
    }
129
130
    private function askCommandId(InputInterface $input, OutputInterface $output, $helper, array $commands): string
131
    {
132
        while (true) {
133
            $commandIdQuestion = new Question('Please enter the Command ID (alphanumeric, hyphens allowed): ');
134
            $commandId = $helper->ask($input, $output, $commandIdQuestion);
135
136
            if (array_key_exists($commandId, array_keys($commands))) {
137
                $output->writeln('<error>There is already a command registered with this command ID. Please try again.</error>');
138
                continue;
139
            }
140
141
            if ($commandId !== null && preg_match('/^[a-zA-Z0-9\-]+$/', $commandId)) {
142
                return $commandId;
143
            } else {
144
                $output->writeln('<error>The Command ID must be alphanumeric and may contain hyphens. Please try again.</error>');
145
            }
146
        }
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...
147
    }
148
149
    private function askCommand(InputInterface $input, OutputInterface $output, $helper): string
150
    {
151
        $finder = new ExecutableFinder();
152
153
        while (true) {
154
            $commandQuestion = new Question('Please enter the command (e.g., "ls"): ');
155
            $command = $helper->ask($input, $output, $commandQuestion);
156
157
            // Überprüfen, ob der Befehl nicht null oder leer ist
158
            if ($command === null || trim($command) === '') {
159
                $output->writeln('<error>The command cannot be empty. Please try again.</error>');
160
                continue;
161
            }
162
163
            // Separate the command from its parameters
164
            $commandParts = explode(' ', $command);
165
            $mainCommand = $commandParts[0];
166
167
            // Ensure the command is not null and the main command exists
168
            if ($mainCommand !== null && $finder->find($mainCommand)) {
169
                return $command;
170
            } else {
171
                $output->writeln("<error>The command '$mainCommand' does not exist. Please try again.</error>");
172
            }
173
        }
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...
174
    }
175
176
    private function askDescription(InputInterface $input, OutputInterface $output, $helper): string
177
    {
178
        while (true) {
179
            $descriptionQuestion = new Question('Please enter a description (no HTML/JS allowed): ');
180
            $description = $helper->ask($input, $output, $descriptionQuestion);
181
182
            // Ensure the description is not null and doesn't contain HTML/JS
183
            if ($description !== null && !$this->containsHtmlOrJs($description)) {
184
                return $description;
185
            } else {
186
                $output->writeln('<error>The description must not contain HTML or JavaScript. Please try again.</error>');
187
            }
188
        }
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...
189
    }
190
191
    private function containsHtmlOrJs(string $text): bool
192
    {
193
        return preg_match('/<[^>]+>/', $text) || preg_match('/<script.*?>.*?<\/script>/is', $text);
194
    }
195
}
196