ConsoleManager::execute()   B
last analyzed

Complexity

Conditions 9
Paths 8

Size

Total Lines 65
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 43
nc 8
nop 0
dl 0
loc 65
rs 7.6764
c 0
b 0
f 0

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
 * This file is part of FSConsoleTools
4
 * Copyright (C) 2018 Francesc Pineda Segarra <[email protected]>
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Lesser General Public License as
8
 * published by the Free Software Foundation, either version 3 of the
9
 * License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
20
namespace FacturaScriptsUtils\Console;
21
22
use Symfony\Component\Finder\Finder;
23
24
/**
25
 * This class is a start point for php-cli commands.
26
 *
27
 * @author Francesc Pineda Segarra <[email protected]>
28
 */
29
class ConsoleManager extends ConsoleAbstract
30
{
31
    /**
32
     * ConsoleManager constructor.
33
     *
34
     * @param int   $argc
35
     * @param array $argv
36
     */
37
    public function __construct(int $argc, array $argv)
38
    {
39
        parent::__construct();
40
        $this->argv = $argv;
41
42
        // Check that at least there are 2 params (console & command)
43
        if ($argc >= 2) {
44
            // Check if first param is an option or a command
45
            switch ($this->argv[1]) {
46
                case '-l':
47
                case '--list':
48
                    $this->showMessage($this->getAvailableCommandsMsg());
49
                    break;
50
51
                case '-h':
52
                case '--help':
53
                    $this->showMessage($this->getHelpMsg());
54
                    break;
55
56
                case 0 === \strpos($this->argv[1], '-'):
57
                case 0 === \strpos($this->argv[1], '--'):
58
                    $this->optionNotAvailable($this->argv[0], $this->argv[1]);
59
                    break;
60
                default:
61
                    $this->run();
62
            }
63
        } else {
64
            $this->showMessage($this->getHelpMsg());
65
        }
66
    }
67
68
    /**
69
     * Start point to run the command.
70
     *
71
     * @param array $params
72
     *
73
     * @return int
74
     */
75
    public function run(...$params): int
76
    {
77
        $cmd = $this->argv[1];
78
        if (class_exists(__NAMESPACE__ . '\Command\\' . $cmd)) {
79
            return $this->execute();
80
        }
81
        trigger_error(\PHP_EOL . 'ERROR: Command "' . $cmd . '" not found.' . \PHP_EOL . \PHP_EOL);
82
        $this->showMessage($this->getHelpMsg());
83
        return -1;
84
    }
85
86
    /**
87
     * Return description about this class.
88
     *
89
     * @return string
90
     */
91
    public function getDescription(): string
92
    {
93
        return 'The Console Manager';
94
    }
95
96
    /**
97
     * Print help information to the user.
98
     *
99
     * @return string
100
     */
101
    public function getHelpMsg(): string
102
    {
103
        return 'Use as: php vendor/bin/console [COMMAND] [OPTIONS]' . \PHP_EOL
104
            . 'Available options:' . \PHP_EOL
105
            . '   -h, --help        Show this help.' . \PHP_EOL
106
            . '   -l, --list        Show a list of available commands.' . \PHP_EOL
107
            . \PHP_EOL;
108
    }
109
110
    /**
111
     * Returns an associative array of available methods for the user.
112
     * Add more options if you want to add support for custom methods.
113
     *      [
114
     *          '-h'        => 'getHelpMsg',
115
     *          '--help'    => 'getHelpMsg',
116
     *          '-l'        => 'getAvailableCommandsMsg',
117
     *          '--list'    => 'getAvailableCommandsMsg',
118
     *      ]
119
     *
120
     * @return array
121
     */
122
    public function getUserMethods(): array
123
    {
124
        // Adding extra method
125
        $methods = parent::getUserMethods();
126
        $methods['-l'] = 'getAvailableCommandsMsg';
127
        $methods['--list'] = 'getAvailableCommandsMsg';
128
        return $methods;
129
    }
130
131
    /**
132
     * Exec the command with the given options
133
     *
134
     * @return int
135
     */
136
    public function execute(): int
137
    {
138
        $this->init();
139
        $status = -1;
140
        $params = $this->argv;
141
        \array_shift($params); // Extract console
142
        // command class
143
        $cmd = \array_shift($params); // Extract command
144
        // $params contains adicional parameters if are received
145
146
        switch ($cmd) {
147
            case '-h':
148
            case '--help':
149
                $status = $this->getAvailableOptions($cmd);
150
                break;
151
            default:
152
                $className = __NAMESPACE__ . '\Command\\' . $cmd;
153
                $methods = \call_user_func([new $className(), 'getUserMethods']);
154
                // Forced in ConsoleAbstract, but we don't want to show it to users
155
                $methods['run'] = 'run';
156
157
                // If not alias, we want to directly run
158
                $alias = $params[0] ?? 'run';
159
                // If not method match, show how it works
160
                $method = $methods[$alias[0]] ?? 'getHelpMsg';
161
162
                if (\array_key_exists($alias, $methods)) {
163
                    // Check if method is in class or parent class
164
                    if (\in_array($method, \get_class_methods($className), false) ||
165
                        \in_array($method, \get_class_methods(\get_parent_class($className)), false)
166
                    ) {
167
                        if (\DB_CONNECTION) {
168
                            $params[] = $this->dataBase;
169
                        }
170
                        $status = (int) \call_user_func_array([new $className(), 'run'], $params);
171
                        break;
172
                    }
173
                    // Can be deleted, but starting with this can be helpful
174
                    if (\FS_DEBUG) {
175
                        $msg = '#######################################################################################'
176
                            . \PHP_EOL . '# ERROR: "' . $method . '" not defined in "' . $className . '"' . \PHP_EOL
177
                            . '#    Maybe you have a misspelling on the method name or is a missing declaration?'
178
                            . \PHP_EOL
179
                            . '#######################################################################################'
180
                            . \PHP_EOL;
181
                        trigger_error($msg);
182
                    }
183
                    break;
184
                }
185
186
                // Can be deleted, but starting with this can be helpful
187
                if (\FS_DEBUG) {
188
                    $msg = '#######################################################################################'
189
                        . \PHP_EOL . '# ERROR: "' . $alias . '" not in "getUserMethods" for "' . $className . '"'
190
                        . \PHP_EOL . '#    Maybe you are missing to put it in to getUserMethods?' . \PHP_EOL
191
                        . '#######################################################################################'
192
                        . \PHP_EOL;
193
                    trigger_error($msg);
194
                }
195
196
                $this->optionNotAvailable($cmd, $alias);
197
                $status = $this->getAvailableOptions($cmd);
198
        }
199
        $this->terminate();
200
        return $status;
201
    }
202
203
    /**
204
     * Returns a list of available methods for this command.
205
     *
206
     * @param string $cmd
207
     *
208
     * @return int
209
     */
210
    public function getAvailableOptions(string $cmd): int
211
    {
212
        $this->showMessage('Available options for "' . $cmd . '"' . \PHP_EOL . \PHP_EOL);
213
        $className = __NAMESPACE__ . '\Command\\' . $cmd;
214
        $options = \call_user_func([new $className(), 'getUserMethods']);
215
        foreach ((array) $options as $option => $methods) {
216
            $this->showMessage('   ' . $option . \PHP_EOL);
217
        }
218
        $this->showMessage(\PHP_EOL . 'Use as: php vendor/bin/console ' . $cmd . ' [OPTIONS]' . \PHP_EOL . \PHP_EOL);
219
        return -1;
220
    }
221
222
    /**
223
     * Print help information to the user.
224
     *
225
     * @return string
226
     */
227
    public function getAvailableCommandsMsg(): string
228
    {
229
        $msg = 'Available commands:' . \PHP_EOL;
230
        foreach ($this->getAvailableCommands() as $cmd) {
231
            $className = __NAMESPACE__ . '\Command\\' . $cmd;
232
            if (0 === \strpos($className, __NAMESPACE__ . '\Command\Common\\')) {
233
                continue;
234
            }
235
            $msg .= '   - ' . $cmd . ' : ' . \call_user_func([new $className(), 'getDescription']) . \PHP_EOL;
236
        }
237
        return $msg . \PHP_EOL;
238
    }
239
240
    /**
241
     * Return a list of available commands
242
     *
243
     * @return array
244
     */
245
    public function getAvailableCommands(): array
246
    {
247
        $available = [];
248
        $allClasses = $this->getAllFcqns(__DIR__ . '/Command');
249
        foreach ($allClasses as $class) {
250
            if (0 === \strpos($class, __NAMESPACE__ . '\Command\\')) {
251
                if (0 === \strpos($class, __NAMESPACE__ . '\Command\Common\\')) {
252
                    continue;
253
                }
254
                $available[] = \str_replace(__NAMESPACE__ . '\Command\\', '', $class);
255
            }
256
        }
257
        return $available;
258
    }
259
260
    /**
261
     * Show that this option is not available.
262
     *
263
     * @param string $cmd
264
     * @param string $option
265
     */
266
    private function optionNotAvailable(string $cmd, string $option)
267
    {
268
        $this->showMessage('Option "' . $option . '" not available for "' . $cmd . '".' . \PHP_EOL . \PHP_EOL);
269
    }
270
271
    /**
272
     * Return all FCQNS.
273
     *
274
     * @param string $projectRoot
275
     *
276
     * @return array
277
     */
278
    private function getAllFcqns(string $projectRoot): array
279
    {
280
        $fileNames = $this->getFileNames($projectRoot);
281
        $fcqns = [];
282
        foreach ($fileNames as $fileName) {
283
            $fcqns[] = $this->getFullNameSpace($fileName) . '\\' . $this->getClassName($fileName);
284
        }
285
        return $fcqns;
286
    }
287
288
    /**
289
     * Return files on path.
290
     *
291
     * @param string $path
292
     * @param string $pattern
293
     *
294
     * @return array
295
     */
296
    private function getFileNames(string $path, string $pattern = '*.php'): array
297
    {
298
        $finder = new Finder();
299
        $finder->files()->in($path)->name($pattern);
300
        $fileNames = [];
301
        foreach ($finder as $finderFile) {
302
            $fileNames[] = $finderFile->getRealPath();
303
        }
304
        return $fileNames;
305
    }
306
307
    /**
308
     * Return full namespace of file.
309
     *
310
     * @param string $fileName
311
     *
312
     * @return string
313
     */
314
    private function getFullNameSpace(string $fileName): string
315
    {
316
        $lines = file($fileName);
317
        if (\is_bool($lines)) {
318
            return '';
319
        }
320
        $array = preg_grep('/^namespace /', $lines);
321
        $namespaceLine = array_shift($array);
322
        $matches = [];
323
        preg_match('/^namespace (.*);$/', $namespaceLine, $matches);
324
        return (string) array_pop($matches);
325
    }
326
327
    /**
328
     * Return the class name.
329
     *
330
     * @param string $fileName
331
     *
332
     * @return string
333
     */
334
    private function getClassName(string $fileName): string
335
    {
336
        $dirsAndFile = explode(DIRECTORY_SEPARATOR, $fileName);
337
        $fileName = array_pop($dirsAndFile);
338
        $nameAndExt = explode('.', $fileName);
339
        return (string) array_shift($nameAndExt);
340
    }
341
}
342