Passed
Push — master ( 1065ba...625efe )
by Francesc
02:11
created

ConsoleManager::execute()   C

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