RegisterSecretCommand::execute()   B
last analyzed

Complexity

Conditions 7
Paths 7

Size

Total Lines 57
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 33
c 1
b 0
f 0
dl 0
loc 57
rs 8.4586
cc 7
nc 7
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace BenTools\Shh\Command;
4
5
use BenTools\Shh\SecretStorage\SecretStorageInterface;
6
use Symfony\Component\Console\Attribute\AsCommand;
7
use Symfony\Component\Console\Command\Command;
8
use Symfony\Component\Console\Input\InputArgument;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Output\OutputInterface;
12
use Symfony\Component\Console\Style\SymfonyStyle;
13
14
#[AsCommand(
15
    name: 'shh:register:secret',
16
)]
17
final class RegisterSecretCommand extends Command
18
{
19
    /**
20
     * @var SecretStorageInterface
21
     */
22
    private $storage;
23
24
    public function __construct(SecretStorageInterface $storage)
25
    {
26
        parent::__construct();
27
        $this->storage = $storage;
28
    }
29
30
    protected function configure(): void
31
    {
32
        $this->setDescription('Register a new secret.')
33
            ->addArgument('key', InputArgument::REQUIRED, 'The secret\'s key name.')
34
            ->addArgument('value', InputArgument::REQUIRED, 'The secret\'s value.')
35
            ->addOption('no-encrypt', null, InputOption::VALUE_NONE, 'If the value should not be encrypted.')
36
            ->setAliases([
37
                'shh:register-secret', // Avoid BC breaks
38
            ])
39
        ;
40
    }
41
42
    protected function interact(InputInterface $input, OutputInterface $output): void
43
    {
44
        $io = new SymfonyStyle($input, $output);
45
46
        if (null === $input->getArgument('key')) {
47
            $input->setArgument(
48
                'key',
49
                $io->ask(
50
                    'Enter the secret\'s key:',
51
                    null,
52
                    function ($key) {
53
                        return $this->validateKey($key);
54
                    }
55
                )
56
            );
57
        }
58
59
        if (null === $input->getArgument('value')) {
60
            $input->setArgument(
61
                'value',
62
                $io->askHidden(
63
                    'Enter the secret\'s value:',
64
                    function ($key) {
65
                        return $this->validateValue($key);
66
                    }
67
                )
68
            );
69
        }
70
    }
71
72
    protected function execute(InputInterface $input, OutputInterface $output): int
73
    {
74
        $io = new SymfonyStyle($input, $output);
75
76
        try {
77
            $key = $this->validateKey($input->getArgument('key'));
78
        } catch (\Exception $e) {
79
            $io->error($e->getMessage());
80
81
            return 1;
82
        }
83
84
        try {
85
            $value = $this->validateValue($input->getArgument('value'));
86
        } catch (\Exception $e) {
87
            $io->error($e->getMessage());
88
89
            return 1;
90
        }
91
92
        try {
93
            if (
94
                $this->storage->has($key) && false === $io->confirm(
95
                    sprintf('Key "%s" already exists. Overwrite?', $key)
96
                )
97
            ) {
98
                $io->success('Your secrets file was left intact.');
99
100
                return 0;
101
            }
102
        } catch (\Exception $e) {
103
            $io->error($e->getMessage());
104
105
            return 1;
106
        }
107
108
        try {
109
            $this->storage->store($key, $value, !$input->getOption('no-encrypt'));
110
        } catch (\Exception $e) {
111
            $io->error($e->getMessage());
112
113
            return 1;
114
        }
115
116
        $io->success('Your secrets file has been successfully updated!');
117
        $io->comment('Tip: you can use your new secret as a parameter:');
118
119
        $io->writeln(
120
            <<<EOF
121
# config/services.yaml
122
parameters:
123
    {$key}: '%env(shh:key:{$key}:json:file:SHH_SECRETS_FILE)%'
124
    
125
EOF
126
        );
127
128
        return 0;
129
    }
130
131
    /**
132
     * @param string $key
133
     * @return string
134
     */
135
    private function validateKey(?string $key): string
136
    {
137
        if (!isset($key[0])) {
138
            throw new \InvalidArgumentException('Key cannot be empty.');
139
        }
140
141
        if (false !== \filter_var($key, FILTER_VALIDATE_REGEXP, ['options' => ['regexp' => '/[^a-z_\-0-9]/i']])) {
142
            throw new \InvalidArgumentException('Key can only contain alphanumeric and underscore characters.');
143
        }
144
145
        return $key;
146
    }
147
148
    /**
149
     * @param string $value
150
     * @return string
151
     */
152
    private function validateValue(string $value): string
153
    {
154
        if (!isset($value[0])) {
155
            throw new \InvalidArgumentException('Value cannot be empty.');
156
        }
157
158
        return $value;
159
    }
160
}
161