Completed
Push — master ( d92f40...ed4867 )
by Jan Philipp
12s
created

Application::getPaddingSize()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 1
1
<?php declare(strict_types=1);
2
3
4
namespace Shopware\Psh\Application;
5
6
use Khill\Duration\Duration;
7
use League\CLImate\CLImate;
8
use Shopware\Psh\Config\Config;
9
use Shopware\Psh\Listing\Script;
10
use Shopware\Psh\Listing\ScriptFinder;
11
use Shopware\Psh\Listing\ScriptNotFoundException;
12
use Shopware\Psh\Listing\ScriptPathNotValidException;
13
use Shopware\Psh\ScriptRuntime\ExecutionErrorException;
14
use Shopware\Psh\ScriptRuntime\TemplateNotValidException;
15
16
/**
17
 * Main application entry point. moves the requested data around and outputs user information.
18
 */
19
class Application
20
{
21
    const RESULT_SUCCESS = 0;
22
23
    const RESULT_ERROR = 1;
24
25
    const MIN_PADDING_SIZE = 30;
26
27
    /**
28
     * @var CLImate
29
     */
30
    public $cliMate;
31
32
    /**
33
     * @var string
34
     */
35
    private $rootDirectory;
36
37
    /**
38
     * @var ApplicationFactory
39
     */
40
    private $applicationFactory;
41
42
    /**
43
     * @var Duration
44
     */
45
    private $duration;
46
47
    /**
48
     * @param string $rootDirectory
49
     */
50
    public function __construct(string $rootDirectory)
51
    {
52
        $this->rootDirectory = $rootDirectory;
53
        $this->applicationFactory = new ApplicationFactory();
54
        $this->cliMate = new CLImate();
55
        $this->duration = new Duration();
56
    }
57
58
    /**
59
     * Main entry point to execute the application.
60
     *
61
     * @param array $inputArgs
62
     * @return int exit code
63
     */
64
    public function run(array $inputArgs): int
65
    {
66
        try {
67
            $config = $this->applicationFactory
68
                ->createConfig($this->rootDirectory, $inputArgs);
69
        } catch (InvalidParameterException $e) {
70
            $this->notifyError($e->getMessage() . "\n");
71
            return self::RESULT_ERROR;
72
        }
73
74
        if (count($inputArgs) > 1 && $inputArgs[1] === 'bash_autocompletion_dump') {
75
            $this->showAutocompleteListing($config);
76
            return self::RESULT_SUCCESS;
77
        }
78
79
        $this->printHeader($config);
80
81
        $configFiles = $this->applicationFactory->getConfigFiles($this->rootDirectory);
82
        $this->printConfigFiles($configFiles);
83
84
        $scriptNames = $this->extractScriptNames($inputArgs);
85
        $scriptFinder = $this->applicationFactory->createScriptFinder($config);
86
87
        try {
88
            foreach ($scriptNames as $scriptName) {
89
                $executionExitCode = $this->execute($scriptFinder->findScriptByName($scriptName), $config);
90
91
                if ($executionExitCode !== self::RESULT_SUCCESS) {
92
                    return $executionExitCode;
93
                }
94
            }
95
96
            if (count($scriptNames)) {
97
                return self::RESULT_SUCCESS;
98
            }
99
        } catch (ScriptNotFoundException $e) {
100
            $this->showScriptNotFoundListing($inputArgs, $scriptNames, $scriptFinder);
101
            return self::RESULT_ERROR;
102
        }
103
104
        try {
105
            $this->showListing($scriptFinder->getAllScripts());
106
        } catch (ScriptPathNotValidException $e) {
107
            $this->notifyError($e->getMessage() . "\n");
108
            return self::RESULT_ERROR;
109
        }
110
111
        return self::RESULT_SUCCESS;
112
    }
113
114
    /**
115
     * @param Script[] $scripts
116
     */
117
    public function showListing(array $scripts)
118
    {
119
        $this->cliMate->green()->bold('Available commands:')->br();
120
121
        if (!count($scripts)) {
122
            $this->cliMate->yellow()->bold('-> Currently no scripts available');
123
        }
124
125
        $paddingSize = $this->getPaddingSize($scripts);
126
        $padding = $this->cliMate->padding($paddingSize)->char(' ');
127
128
        $scriptEnvironment = false;
129
130
        foreach ($scripts as $script) {
131
            if ($scriptEnvironment !== $script->getEnvironment()) {
132
                $scriptEnvironment = $script->getEnvironment();
133
                $this->cliMate->green()->br()->bold(($scriptEnvironment ?? 'default') . ':');
134
            }
135
136
            $padding
137
                ->label('<bold> - ' . $script->getName() . '</bold>')
138
                ->result('<dim>' . $script->getDescription() . '</dim>');
139
        }
140
141
        $this->cliMate->green()->bold("\n" . count($scripts) . " script(s) available\n");
142
    }
143
144
    /**
145
     * @param array $inputArgs
146
     * @return array
147
     */
148
    protected function extractScriptNames(array $inputArgs): array
149
    {
150
        if (!isset($inputArgs[1])) {
151
            return [];
152
        }
153
154
        return explode(',', $inputArgs[1]);
155
    }
156
157
    /**
158
     * @param Script $script
159
     * @param Config $config
160
     * @return int
161
     */
162
    protected function execute(Script $script, Config $config): int
163
    {
164
        $commands = $this->applicationFactory
165
            ->createCommands($script);
166
167
        $logger = new ClimateLogger($this->cliMate, $this->duration);
168
        $executor = $this->applicationFactory
169
            ->createProcessExecutor($script, $config, $logger, $this->rootDirectory);
170
171
        try {
172
            $executor->execute($script, $commands);
173
        } catch (ExecutionErrorException $e) {
174
            $this->notifyError("\nExecution aborted, a subcommand failed!\n");
175
            return self::RESULT_ERROR;
176
        } catch (TemplateNotValidException $e) {
177
            $this->notifyError("\n" . $e->getMessage() . "\n");
178
            return self::RESULT_ERROR;
179
        }
180
181
        $this->notifySuccess("All commands successfully executed!\n");
182
183
        return self::RESULT_SUCCESS;
184
    }
185
186
    /**
187
     * @param $string
188
     */
189
    public function notifySuccess($string)
190
    {
191
        $this->cliMate->bold()->green($string);
192
    }
193
194
    /**
195
     * @param $string
196
     */
197
    public function notifyError($string)
198
    {
199
        $this->cliMate->bold()->red($string);
200
    }
201
202
    /**
203
     * @param $config
204
     */
205
    protected function printHeader(Config $config)
206
    {
207
        $this->cliMate->green()->bold()->out("\n###################");
208
209
        if ($config->getHeader()) {
210
            $this->cliMate->out("\n" . $config->getHeader());
211
        }
212
    }
213
214
    protected function printConfigFiles(array $configFiles)
215
    {
216
        for ($i = 0; $i < count($configFiles); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
217
            $configFiles[$i] = str_replace($this->rootDirectory."/", "", $configFiles[$i]);
218
        }
219
220
        if (count($configFiles) == 1) {
221
            $this->cliMate->yellow()->out(sprintf("Using %s \n", $configFiles[0]));
222
            return;
223
        }
224
225
        $this->cliMate->yellow()->out(sprintf("Using %s extended by %s \n", $configFiles[0], $configFiles[1]));
226
    }
227
228
    /**
229
     * @param Script[] $scripts
230
     * @return Int
231
     */
232
    private function getPaddingSize(array $scripts): Int
233
    {
234
        $maxScriptNameLength = 0;
235
        foreach ($scripts as $script) {
236
            if (strlen($script->getName()) > $maxScriptNameLength) {
237
                $maxScriptNameLength = strlen($script->getName());
238
            }
239
        }
240
        return $maxScriptNameLength + self::MIN_PADDING_SIZE;
241
    }
242
243
    /**
244
     * @param $config
245
     */
246
    private function showAutocompleteListing(Config $config)
247
    {
248
        $scriptFinder = $this->applicationFactory
249
            ->createScriptFinder($config);
250
251
        $scripts = $scriptFinder->getAllScripts();
252
253
        $commands = array_map(function (Script $script) {
254
            return $script->getName();
255
        }, $scripts);
256
257
        $this->cliMate->out(implode(' ', $commands));
258
    }
259
260
    /**
261
     * @param array $inputArgs
262
     * @param array $scriptNames
263
     * @param ScriptFinder $scriptFinder
264
     */
265
    private function showScriptNotFoundListing(array $inputArgs, array $scriptNames, ScriptFinder $scriptFinder)
266
    {
267
        $this->notifyError("Script with name {$inputArgs[1]} not found\n");
268
269
        $scripts = [];
270
        foreach ($scriptNames as $scriptName) {
271
            $newScripts = $scriptFinder->findScriptsByPartialName($scriptName);
272
            $scripts = array_merge($scripts, $newScripts);
273
        }
274
275
        if (count($scripts) > 0) {
276
            $this->cliMate->yellow()->bold('Have you been looking for this?');
277
            $this->showListing($scripts);
278
        }
279
    }
280
}
281