Issues (16)

src/Console/GenerateCommand.php (8 issues)

Labels
Severity
1
<?php
2
3
/**
4
 * This file is part of PhpUnitGen.
5
 *
6
 * (c) 2017-2018 Paul Thébaud <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE.md
9
 * file that was distributed with this source code.
10
 */
11
12
namespace PhpUnitGen\Console;
13
14
use PhpUnitGen\Configuration\AbstractConsoleConfigFactory;
15
use PhpUnitGen\Configuration\ConfigurationInterface\ConsoleConfigInterface;
16
use PhpUnitGen\Configuration\DefaultConsoleConfigFactory;
17
use PhpUnitGen\Configuration\JsonConsoleConfigFactory;
18
use PhpUnitGen\Configuration\PhpConsoleConfigFactory;
19
use PhpUnitGen\Configuration\YamlConsoleConfigFactory;
20
use PhpUnitGen\Container\ContainerInterface\ConsoleContainerFactoryInterface;
21
use PhpUnitGen\Exception\Exception;
22
use PhpUnitGen\Exception\InvalidConfigException;
23
use PhpUnitGen\Executor\ExecutorInterface\ConsoleExecutorInterface;
24
use Respect\Validation\Validator;
25
use Symfony\Component\Console\Command\Command;
26
use Symfony\Component\Console\Input\InputArgument;
27
use Symfony\Component\Console\Input\InputInterface;
28
use Symfony\Component\Console\Input\InputOption;
29
use Symfony\Component\Console\Output\OutputInterface;
30
use Symfony\Component\Console\Style\SymfonyStyle;
31
use Symfony\Component\Stopwatch\Stopwatch;
32
33
/**
34
 * Class GenerateCommand.
35
 *
36
 * @author     Paul Thébaud <[email protected]>.
37
 * @copyright  2017-2018 Paul Thébaud <[email protected]>.
38
 * @license    https://opensource.org/licenses/MIT The MIT license.
39
 * @link       https://github.com/paul-thebaud/phpunit-generator
40
 * @since      Class available since Release 2.0.0.
41
 */
42
class GenerateCommand extends Command
43
{
44
    public const STOPWATCH_EVENT = 'command';
45
46
    /**
47
     * @var string[] CONSOLE_CONFIG_FACTORIES Mapping array between file extension and configuration factories.
48
     */
49
    protected const CONSOLE_CONFIG_FACTORIES = [
50
        'yml'  => YamlConsoleConfigFactory::class,
51
        'json' => JsonConsoleConfigFactory::class,
52
        'php'  => PhpConsoleConfigFactory::class
53
    ];
54
55
    /**
56
     * @var ConsoleContainerFactoryInterface $containerFactory A container factory to create container.
57
     */
58
    protected $containerFactory;
59
60
    /**
61
     * @var ConsoleExecutorInterface $consoleExecutor A executor to execute PhpUnitGen task.
62
     */
63
    protected $consoleExecutor;
64
65
    /**
66
     * @var Stopwatch $stopwatch The stopwatch to measure duration and memory usage.
67
     */
68
    protected $stopwatch;
69
70
    /**
71
     * GenerateCommand constructor.
72
     *
73
     * @param ConsoleContainerFactoryInterface $containerFactory A container factory to create container.
74
     * @param Stopwatch                        $stopwatch        The stopwatch component for execution stats.
75
     */
76
    public function __construct(ConsoleContainerFactoryInterface $containerFactory, Stopwatch $stopwatch)
77
    {
78
        parent::__construct();
79
80
        $this->containerFactory = $containerFactory;
81
        $this->stopwatch        = $stopwatch;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    protected function configure()
88
    {
89
        $this->setName('generate')
90
            ->setAliases(['gen'])
91
            ->setDescription('Generate unit tests skeletons.')
92
            ->setHelp(
93
                'Use it to generate your unit tests skeletons. See documentation on ' .
94
                'https://github.com/paul-thebaud/phpunit-generator/blob/master/DOCUMENTATION.md'
95
            )
96
            ->addOption('config', 'c', InputOption::VALUE_REQUIRED, 'The configuration file path.', 'phpunitgen.yml')
97
            ->addOption('file', 'f', InputOption::VALUE_NONE, 'If you want file parsing.')
98
            ->addOption('dir', 'd', InputOption::VALUE_NONE, 'If you want directory parsing.')
99
            ->addOption('default', 'D', InputOption::VALUE_NONE, 'If you want to use the default configuration.')
100
            ->addArgument('source', InputArgument::OPTIONAL, 'The source path (directory if "dir" option set).')
101
            ->addArgument('target', InputArgument::OPTIONAL, 'The target path (directory if "dir" option set).');
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     */
107
    protected function execute(InputInterface $input, OutputInterface $output): int
108
    {
109
        $this->stopwatch->start(GenerateCommand::STOPWATCH_EVENT);
110
111
        $styledIO = $this->getStyledIO($input, $output);
112
        try {
113
            $config = $this->getConfiguration($input);
114
115
            $container = $this->containerFactory->invoke($config, $styledIO, $this->stopwatch);
116
117
            $this->consoleExecutor = $container->get(ConsoleExecutorInterface::class);
118
119
            $this->consoleExecutor->invoke();
120
        } catch (Exception $exception) {
121
            $styledIO->error($exception->getMessage());
122
            return -1;
123
        }
124
125
        return 0;
126
    }
127
128
    /**
129
     * Build a new instance of SymfonyStyle.
130
     *
131
     * @param InputInterface  $input  The input.
132
     * @param OutputInterface $output The output.
133
     *
134
     * @return SymfonyStyle The created symfony style i/o.
135
     */
136
    protected function getStyledIO(InputInterface $input, OutputInterface $output): SymfonyStyle
137
    {
138
        return new SymfonyStyle($input, $output);
139
    }
140
141
    /**
142
     * Build a configuration from a configuration file path.
143
     *
144
     * @param InputInterface $input An input interface to retrieve command argument.
145
     *
146
     * @return ConsoleConfigInterface The created configuration.
147
     *
148
     * @throws Exception If an error occurs during process.
149
     */
150
    protected function getConfiguration(InputInterface $input): ConsoleConfigInterface
151
    {
152
        // If the configuration to use is the default.
153
        if ($input->getOption('default')) {
154
            $this->validatePathsExist($input);
155
            // If it is a directory.
156
            if ($input->getOption('dir')) {
157
                return (new DefaultConsoleConfigFactory())
158
                    ->invokeDir($input->getArgument('source'), $input->getArgument('target'));
0 ignored issues
show
It seems like $input->getArgument('target') can also be of type null and string[]; however, parameter $targetDirectory of PhpUnitGen\Configuration...figFactory::invokeDir() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

158
                    ->invokeDir($input->getArgument('source'), /** @scrutinizer ignore-type */ $input->getArgument('target'));
Loading history...
It seems like $input->getArgument('source') can also be of type null and string[]; however, parameter $sourceDirectory of PhpUnitGen\Configuration...figFactory::invokeDir() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

158
                    ->invokeDir(/** @scrutinizer ignore-type */ $input->getArgument('source'), $input->getArgument('target'));
Loading history...
159
            }
160
            return (new DefaultConsoleConfigFactory())
161
                ->invokeFile($input->getArgument('source'), $input->getArgument('target'));
0 ignored issues
show
It seems like $input->getArgument('source') can also be of type null and string[]; however, parameter $sourceFile of PhpUnitGen\Configuration...igFactory::invokeFile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

161
                ->invokeFile(/** @scrutinizer ignore-type */ $input->getArgument('source'), $input->getArgument('target'));
Loading history...
It seems like $input->getArgument('target') can also be of type null and string[]; however, parameter $targetFile of PhpUnitGen\Configuration...igFactory::invokeFile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

161
                ->invokeFile($input->getArgument('source'), /** @scrutinizer ignore-type */ $input->getArgument('target'));
Loading history...
162
        }
163
164
        $path = $input->getOption('config');
165
        // If we have a "=" at the beginning of the option value
166
        $path = preg_replace('/^\=/', '', $path);
167
168
        $factory = $this->getConfigurationFactory($path);
169
170
        if ($input->getOption('dir')) {
171
            $this->validatePathsExist($input);
172
            return $factory->invokeDir(
173
                $path,
174
                $input->getArgument('source'),
0 ignored issues
show
It seems like $input->getArgument('source') can also be of type null and string[]; however, parameter $sourceDir of PhpUnitGen\Configuration...figFactory::invokeDir() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

174
                /** @scrutinizer ignore-type */ $input->getArgument('source'),
Loading history...
175
                $input->getArgument('target')
0 ignored issues
show
It seems like $input->getArgument('target') can also be of type null and string[]; however, parameter $targetDir of PhpUnitGen\Configuration...figFactory::invokeDir() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

175
                /** @scrutinizer ignore-type */ $input->getArgument('target')
Loading history...
176
            );
177
        }
178
        if ($input->getOption('file')) {
179
            $this->validatePathsExist($input);
180
            return $factory->invokeFile(
181
                $path,
182
                $input->getArgument('source'),
0 ignored issues
show
It seems like $input->getArgument('source') can also be of type null and string[]; however, parameter $sourceFile of PhpUnitGen\Configuration...igFactory::invokeFile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

182
                /** @scrutinizer ignore-type */ $input->getArgument('source'),
Loading history...
183
                $input->getArgument('target')
0 ignored issues
show
It seems like $input->getArgument('target') can also be of type null and string[]; however, parameter $targetFile of PhpUnitGen\Configuration...igFactory::invokeFile() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

183
                /** @scrutinizer ignore-type */ $input->getArgument('target')
Loading history...
184
            );
185
        }
186
        return $factory->invoke($path);
187
    }
188
189
    /**
190
     * Get the configuration factory depending on configuration path.
191
     *
192
     * @param string $path The configuration file path.
193
     *
194
     * @return AbstractConsoleConfigFactory The configuration factory.
195
     *
196
     * @throws InvalidConfigException If the configuration is invalid.
197
     */
198
    protected function getConfigurationFactory(string $path): AbstractConsoleConfigFactory
199
    {
200
        if (! file_exists($path)) {
201
            throw new InvalidConfigException(sprintf('Config file "%s" does not exists', $path));
202
        }
203
204
        $extension = pathinfo($path, PATHINFO_EXTENSION);
205
        if (! Validator::key($extension)->validate(static::CONSOLE_CONFIG_FACTORIES)) {
206
            throw new InvalidConfigException(
207
                sprintf('Config file "%s" must have .yml, .json or .php extension', $path)
208
            );
209
        }
210
211
        /** @var AbstractConsoleConfigFactory $factory */
212
        $factoryClass = static::CONSOLE_CONFIG_FACTORIES[$extension];
213
        return new $factoryClass();
214
    }
215
216
    /**
217
     * Throw an exception if the source or the target path is missing.
218
     *
219
     * @param InputInterface $input The input interface to check.
220
     *
221
     * @throws Exception If the source or the target path is missing.
222
     */
223
    protected function validatePathsExist(InputInterface $input): void
224
    {
225
        if (! is_string($input->getArgument('source'))) {
226
            throw new Exception('Missing the source path');
227
        }
228
        if (! is_string($input->getArgument('target'))) {
229
            throw new Exception('Missing the target path');
230
        }
231
    }
232
}
233