Completed
Push — master ( 0a4926...d192d3 )
by Jan Philipp
24s queued 11s
created

Application::prepare()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.6666
c 0
b 0
f 0
cc 3
nc 2
nop 1
1
<?php declare(strict_types=1);
2
3
namespace Shopware\Psh\Application;
4
5
use Khill\Duration\Duration;
6
use League\CLImate\CLImate;
7
use Shopware\Psh\Config\Config;
8
use Shopware\Psh\Config\RequiredValue;
9
use Shopware\Psh\Listing\Script;
10
use Shopware\Psh\Listing\ScriptFinder;
11
use Shopware\Psh\Listing\ScriptNotFound;
12
use Shopware\Psh\PshErrorMessage;
13
use Shopware\Psh\ScriptRuntime\Execution\ExecutionError;
14
use function array_key_exists;
15
use function array_map;
16
use function count;
17
use function implode;
18
use function max;
19
use function mb_strlen;
20
use function sprintf;
21
22
/**
23
 * Main application entry point. moves the requested data around and outputs user information.
24
 */
25
class Application
26
{
27
    private const MIN_PADDING_SIZE = 30;
28
29
    /**
30
     * @var CLImate
31
     */
32
    public $cliMate;
33
34
    /**
35
     * @var string
36
     */
37
    private $rootDirectory;
38
39
    /**
40
     * @var ApplicationFactory
41
     */
42
    private $applicationFactory;
43
44
    /**
45
     * @var Duration
46
     */
47
    private $duration;
48
49
    public function __construct(string $rootDirectory)
50
    {
51
        $this->rootDirectory = $rootDirectory;
52
        $this->applicationFactory = new ApplicationFactory();
53
        $this->cliMate = new CLImate();
54
        $this->duration = new Duration();
55
    }
56
57
    /**
58
     * Main entry point to execute the application.
59
     *
60
     * @return int exit code
61
     */
62
    public function run(array $inputArgs): int
63
    {
64
        try {
65
            $config = $this->prepare($inputArgs);
66
67
            $scriptFinder = $this->applicationFactory->createScriptFinder($config);
68
69
            $this->executeScript($scriptFinder, $config);
70
71
            $this->showListing($scriptFinder->getAllVisibleScripts());
72
73
            throw ExitSignal::success();
74
        } catch (PshErrorMessage $error) {
75
            $this->notifyError("\n" . $error->getMessage() . "\n");
76
77
            return ExitSignal::error()->signal();
78
        } catch (ExitSignal $signal) {
79
            return $signal->signal();
80
        }
81
    }
82
83
    /**
84
     * @param Script[] $scripts
85
     */
86
    private function showListing(array $scripts): void
87
    {
88
        $this->cliMate->green()->bold('Available commands:')->br();
89
90
        if (!count($scripts)) {
91
            $this->cliMate->yellow()->bold("-> Currently no scripts available\n");
92
93
            return;
94
        }
95
96
        $paddingSize = $this->getPaddingSize($scripts);
97
        $padding = $this->cliMate->padding($paddingSize)->char(' ');
98
99
        $scriptEnvironment = 'default';
100
        foreach ($scripts as $script) {
101
            if ($script->getEnvironment() !== $scriptEnvironment) {
102
                $scriptEnvironment = $script->getEnvironment();
103
                $this->cliMate->green()->br()->bold(($scriptEnvironment ?? 'default') . ':');
104
            }
105
106
            $padding
107
                ->label(sprintf('<bold> - %s</bold>', $script->getName()))
108
                ->result(sprintf('<dim>%s</dim>', $script->getDescription()));
109
        }
110
111
        $this->cliMate->green()->bold(sprintf("\n %s script(s) available\n", count($scripts)));
112
    }
113
114
    private function execute(Script $script, Config $config, ScriptFinder $scriptFinder): void
115
    {
116
        $commands = $this->applicationFactory
117
            ->createCommands($script, $scriptFinder);
118
119
        $logger = new ClimateLogger($this->cliMate, $this->duration);
120
        $executor = $this->applicationFactory
121
            ->createProcessExecutor($script, $config, $logger);
122
123
        try {
124
            $executor->execute($script, $commands);
125
        } catch (ExecutionError $e) {
126
            $this->notifyError("\nExecution aborted, a subcommand failed!\n");
127
128
            throw ExitSignal::error();
129
        }
130
131
        $this->notifySuccess("All commands successfully executed!\n");
132
    }
133
134
    /**
135
     * @param $string
136
     */
137
    private function notifySuccess(string $string): void
138
    {
139
        $this->cliMate->bold()->green($string);
140
    }
141
142
    /**
143
     * @param $string
144
     */
145
    private function notifyError(string $string): void
146
    {
147
        $this->cliMate->bold()->red($string);
148
    }
149
150
    private function getPaddingSize(array $scripts): int
151
    {
152
        return self::MIN_PADDING_SIZE + max(array_map(static function (Script $script) {
153
            return mb_strlen($script->getName());
154
        }, $scripts));
155
    }
156
157
    private function showAutocompleteListing(Config $config): void
158
    {
159
        $scriptFinder = $this->applicationFactory
160
            ->createScriptFinder($config);
161
162
        $scripts = $scriptFinder->getAllVisibleScripts();
163
164
        $commands = array_map(static function (Script $script) {
165
            return $script->getName();
166
        }, $scripts);
167
168
        $this->cliMate->out(implode(' ', $commands));
169
    }
170
171
    private function showScriptNotFoundListing(string $scriptName, ScriptFinder $scriptFinder): void
172
    {
173
        $this->notifyError(sprintf("Script with name %s not found\n", $scriptName));
174
175
        $scripts = $scriptFinder->findScriptsByPartialName($scriptName);
176
177
        if (count($scripts) > 0) {
178
            $this->cliMate->yellow()->bold('Have you been looking for this?');
179
        }
180
181
        $this->showListing($scripts);
182
    }
183
184
    private function printHead(Config $config, ApplicationConfigLogger $logger): void
185
    {
186
        if ($config->hasOption(ApplicationOptions::FLAG_NO_HEADER)) {
187
            return;
188
        }
189
190
        $this->cliMate->green()->bold()->out("\n################### PSH@" . PSH_VERSION);
191
192
        if ($config->getHeader()) {
193
            $this->cliMate->out("\n" . $config->getHeader());
194
        }
195
196
        $logger->printOut($this->cliMate);
197
    }
198
199
    private function validateConfig(Config $config, ?string $environment = null): void
200
    {
201
        $allPlaceholders = $config->getAllPlaceholders($environment);
202
203
        $missing = [];
204
        foreach ($config->getRequiredVariables($environment) as $requiredVariable) {
205
            if (!array_key_exists($requiredVariable->getName(), $allPlaceholders)) {
206
                $missing[] = $requiredVariable;
207
                $this->printMissingRequiredVariable($requiredVariable);
208
            }
209
        }
210
211
        if (count($missing)) {
212
            $this->cliMate->error("\n<bold>Please define the missing value(s) first</bold>\n");
213
            throw ExitSignal::error();
214
        }
215
    }
216
217
    private function printMissingRequiredVariable(RequiredValue $requiredVariable): void
218
    {
219
        if ($requiredVariable->hasDescription()) {
220
            $this->cliMate->error(sprintf(
221
                "\t - <bold>Missing required const or var named <underline>%s</underline></bold> <dim>(%s)</dim>",
222
                $requiredVariable->getName(),
223
                $requiredVariable->getDescription()
224
            ));
225
        } else {
226
            $this->cliMate->error(sprintf(
227
                "\t - <bold>Missing required const or var named <underline>%s</underline></bold>",
228
                $requiredVariable->getName()
229
            ));
230
        }
231
    }
232
233
    private function prepare(array $inputArgs): Config
234
    {
235
        $configLogger = new ApplicationConfigLogger($this->rootDirectory, $this->cliMate);
0 ignored issues
show
Unused Code introduced by
The call to ApplicationConfigLogger::__construct() has too many arguments starting with $this->cliMate.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
236
237
        $config = $this->applicationFactory
238
            ->createConfig($configLogger, $this->rootDirectory, $inputArgs);
239
240
        if (count($inputArgs) > 1 && $inputArgs[1] === 'bash_autocompletion_dump') {
241
            $this->showAutocompleteListing($config);
242
243
            throw ExitSignal::success();
244
        }
245
246
        $this->printHead($config, $configLogger);
247
        $this->validateConfig($config);
248
249
        return $config;
250
    }
251
252
    private function executeScript(ScriptFinder $scriptFinder, Config $config): void
253
    {
254
        $scriptNames = $config->getScriptNames();
255
256
        if (!count($scriptNames)) {
257
            return;
258
        }
259
260
        try {
261
            $scripts = $scriptFinder->findScriptsInOrder($scriptNames);
262
        } catch (ScriptNotFound $e) {
263
            $this->showScriptNotFoundListing($e->getScriptName(), $scriptFinder);
264
            throw ExitSignal::error();
265
        }
266
267
        foreach ($scripts as $script) {
268
            $this->execute($script, $config, $scriptFinder);
269
        }
270
271
        throw ExitSignal::success();
272
    }
273
}
274