Passed
Pull Request — master (#74)
by Peter
08:25 queued 01:31
created

YamlCommand   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 129
Duplicated Lines 0 %

Test Coverage

Coverage 82.09%

Importance

Changes 4
Bugs 2 Features 1
Metric Value
wmc 18
eloc 72
c 4
b 2
f 1
dl 0
loc 129
ccs 55
cts 67
cp 0.8209
rs 10

4 Methods

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