Passed
Pull Request — master (#79)
by Peter
14:15 queued 07:00
created

YamlCommand   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Test Coverage

Coverage 82.61%

Importance

Changes 5
Bugs 2 Features 1
Metric Value
wmc 19
eloc 75
c 5
b 2
f 1
dl 0
loc 132
ccs 57
cts 69
cp 0.8261
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 10 1
A printOutput() 0 16 4
A isFileExcluded() 0 7 2
C execute() 0 68 12
1
<?php
2
3
declare(strict_types=1);
4
5
namespace YamlStandards\Command;
6
7
use Symfony\Component\Console\Command\Command;
8
use Symfony\Component\Console\Input\InputArgument;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Input\InputOption;
11
use Symfony\Component\Console\Output\NullOutput;
12
use Symfony\Component\Console\Output\OutputInterface;
13
use Symfony\Component\Console\Style\SymfonyStyle;
14
use Symfony\Component\Yaml\Exception\ParseException;
15
use YamlStandards\Command\Service\ResultService;
16
use YamlStandards\Model\Component\Cache\NativeCache;
17
use YamlStandards\Model\Component\Cache\NoCache;
18
use YamlStandards\Model\Config\YamlStandardConfigLoader;
19
use YamlStandards\Result\Result;
20
21
class YamlCommand extends Command
22
{
23
    private const COMMAND_NAME = 'yaml-standards';
24
25
    public const ARGUMENT_PATH_TO_CONFIG_FILE = 'pathToConfigFile';
26
    public const OPTION_FIX = 'fix';
27
    public const OPTION_PATH_TO_CACHE_DIR = 'path-to-cache-dir';
28
    public const OPTION_DISABLE_CACHE = 'no-cache';
29
    public const OPTION_DISABLE_PROGRESS_BAR = 'no-progress-bar';
30
31
    protected static $defaultName = self::COMMAND_NAME;
32
33 10
    protected function configure(): void
34
    {
35 10
        $this
36 10
            ->setName(self::COMMAND_NAME) // set command name for symfony/console lower version as 3.4
37 10
            ->setDescription('Check yaml files respect standards')
38 10
            ->addArgument(self::ARGUMENT_PATH_TO_CONFIG_FILE, InputArgument::OPTIONAL, 'Path to configuration file. By default configuration file is looking in root directory', './yaml-standards.yaml')
39 10
            ->addOption(self::OPTION_FIX, null, InputOption::VALUE_NONE, 'Automatically fix problems')
40 10
            ->addOption(self::OPTION_PATH_TO_CACHE_DIR, null, InputOption::VALUE_REQUIRED, 'Custom path to cache dir', sys_get_temp_dir() . '/')
41 10
            ->addOption(self::OPTION_DISABLE_CACHE, null, InputOption::VALUE_NONE, 'Disable cache functionality')
42 10
            ->addOption(self::OPTION_DISABLE_PROGRESS_BAR, null, InputOption::VALUE_NONE, 'Disable progress bar. Useful e.g. for nicer CI output.');
43
    }
44
45
    /**
46
     * @inheritDoc
47
     */
48 10
    protected function execute(InputInterface $input, OutputInterface $output): int
49
    {
50 10
        $inputSettingData = new InputSettingData($input);
51 10
        $yamlStandardConfigLoader = new YamlStandardConfigLoader();
52 10
        $pathToConfigFile = $inputSettingData->getPathToConfigFile();
53 10
        $pathToCacheDir = $inputSettingData->getPathToCacheDir();
54 10
        $yamlStandardConfigTotalData = $yamlStandardConfigLoader->loadFromYaml($pathToConfigFile);
55 9
        $cache = $inputSettingData->isCacheDisabled() ? new NoCache() : new NativeCache();
56 9
        $cache->deleteCacheFileIfConfigFileWasChanged($pathToConfigFile, $pathToCacheDir);
57
58 9
        $symfonyStyle = new SymfonyStyle($input, $inputSettingData->isProgressBarDisabled() ? new NullOutput() : $output);
59 9
        $progressBar = $symfonyStyle->createProgressBar($yamlStandardConfigTotalData->getTotalCountOfFiles());
60 9
        $progressBar->setFormat('debug');
61 9
        $results = [[]];
62
63 9
        foreach ($yamlStandardConfigTotalData->getYamlStandardConfigsSingleData() as $configNumber => $yamlStandardConfigSingleData) {
64
            // config number 0 is reserved for config file
65 9
            ++$configNumber;
66
67 9
            $filesToCache = [];
68 9
            $cachedPathToFiles = $cache->getCachedPathToFiles($yamlStandardConfigSingleData->getPathToFiles(), $configNumber, $pathToCacheDir);
69 9
            foreach ($cachedPathToFiles as $pathToFile) {
70 9
                $fileResults = [];
71 9
                if ($this->isFileExcluded($pathToFile, $yamlStandardConfigSingleData->getPathToExcludedFiles())) {
72 5
                    $filesToCache[] = $pathToFile;
73 5
                    $progressBar->advance();
74 5
                    continue;
75
                }
76
77 8
                if (is_readable($pathToFile) === false) {
78
                    $message = 'File is not readable.';
79
                    $results[] = [new Result($pathToFile, Result::RESULT_CODE_GENERAL_ERROR, $message)];
80
                    $progressBar->advance();
81
                    continue;
82
                }
83
84
                try {
85 8
                    foreach ($yamlStandardConfigSingleData->getYamlStandardConfigsSingleStandardData() as $yamlStandardConfigSingleCheckerData) {
86 8
                        $standardParametersData = $yamlStandardConfigSingleCheckerData->getStandardParametersData();
87 8
                        $checker = $yamlStandardConfigSingleCheckerData->getChecker();
88 8
                        $fixer = $yamlStandardConfigSingleCheckerData->getFixer();
89
90 8
                        if ($fixer !== null && $inputSettingData->isFixEnabled()) {
91 1
                            $result = $fixer->runFix($pathToFile, $pathToFile, $standardParametersData);
92
                        } else {
93 8
                            $result = $checker->runCheck($pathToFile, $standardParametersData);
94
                        }
95 8
                        $fileResults[] = $result;
96
                    }
97
                } catch (ParseException $e) {
98
                    $message = sprintf('Unable to parse the YAML string: %s', $e->getMessage());
99
                    $fileResults[] = new Result($pathToFile, Result::RESULT_CODE_GENERAL_ERROR, $message);
100
                }
101
102 8
                if (ResultService::getResultCodeByResults($fileResults) === Result::RESULT_CODE_OK_AS_INTEGER) {
103 8
                    $filesToCache[] = $pathToFile;
104
                }
105 8
                $results[] = $fileResults;
106 8
                $progressBar->advance();
107
            }
108
109 9
            $cache->cacheFiles($filesToCache, $configNumber, $pathToCacheDir);
110
        }
111 9
        $progressBar->finish();
112
        /** @var \YamlStandards\Result\Result[] $mergedResult */
113 9
        $mergedResult = array_merge(...$results); // add all results to one array instead of multidimensional array with results for every file
114
115 9
        return $this->printOutput($output, $mergedResult);
116
    }
117
118
    /**
119
     * @param \Symfony\Component\Console\Output\OutputInterface $output
120
     * @param \YamlStandards\Result\Result[] $results
121
     * @return int
122
     */
123 9
    private function printOutput(OutputInterface $output, array $results): int
124
    {
125 9
        $output->writeln(PHP_EOL);
126 9
        foreach ($results as $result) {
127 8
            if ($result->getResultCode() !== Result::RESULT_CODE_OK) {
128
                $output->writeln(sprintf('FILE: %s', $result->getPathToFile()));
129
                $output->writeln('-------------------------------------------------');
130
                $output->writeln($result->getMessage() . PHP_EOL);
131
132
                if ($result->canBeFixedByFixer()) {
133
                    $output->writeln('<fg=red>This can be fixed by `--fix` option</fg=red>' . PHP_EOL);
134
                }
135
            }
136
        }
137
138 9
        return ResultService::getResultCodeByResults($results);
139
    }
140
141
    /**
142
     * @param string $pathToFile
143
     * @param string[] $pathToExcludedFiles
144
     * @return bool
145
     */
146 9
    private function isFileExcluded(string $pathToFile, array $pathToExcludedFiles): bool
147
    {
148 9
        if (in_array($pathToFile, $pathToExcludedFiles, true)) {
149 5
            return true;
150
        }
151
152 8
        return false;
153
    }
154
}
155