Completed
Push — master ( 3f4fca...7e281f )
by Дмитрий
02:43
created

CheckCommand   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 154
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Test Coverage

Coverage 12.9%

Importance

Changes 19
Bugs 2 Features 4
Metric Value
c 19
b 2
f 4
dl 0
loc 154
ccs 12
cts 93
cp 0.129
rs 9.1666
wmc 15
lcom 1
cbo 15

4 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 14 1
C execute() 0 119 12
A getMemoryUsage() 0 4 1
A getCompiler() 0 4 1
1
<?php
2
/**
3
 * @author Patsura Dmitry https://github.com/ovr <[email protected]>
4
 */
5
6
namespace PHPSA\Command;
7
8
use PhpParser\ParserFactory;
9
use PHPSA\Analyzer;
10
use PHPSA\Analyzer\EventListener\ExpressionListener;
11
use PHPSA\Analyzer\EventListener\StatementListener;
12
use PHPSA\Application;
13
use PHPSA\Compiler;
14
use PHPSA\Configuration;
15
use PHPSA\ConfigurationLoader;
16
use PHPSA\Context;
17
use PHPSA\Definition\FileParser;
18
use RecursiveDirectoryIterator;
19
use RecursiveIteratorIterator;
20
use SplFileInfo;
21
use FilesystemIterator;
22
use PhpParser\Node;
23
use PhpParser\Parser;
24
use Symfony\Component\Config\Definition\Processor;
25
use Symfony\Component\Config\FileLocator;
26
use Symfony\Component\Config\Loader\LoaderResolver;
27
use Symfony\Component\Console\Command\Command;
28
use Symfony\Component\Console\Input\InputArgument;
29
use Symfony\Component\Console\Input\InputInterface;
30
use Symfony\Component\Console\Input\InputOption;
31
use Symfony\Component\Console\Output\OutputInterface;
32
use Webiny\Component\EventManager\EventManager;
33
use PHPSA\Analyzer\Pass as AnalyzerPass;
34
35
/**
36
 * Class CheckCommand
37
 * @package PHPSA\Command
38
 *
39
 * @method Application getApplication();
40
 */
41
class CheckCommand extends Command
42
{
43 379
    protected function configure()
44
    {
45 379
        $this
46 379
            ->setName('check')
47 379
            ->setDescription('SPA')
48 379
            ->addOption('blame', null, InputOption::VALUE_OPTIONAL, 'Git blame author for bad code ;)', -1)
49 379
            ->addArgument('path', InputArgument::OPTIONAL, 'Path to check file or directory', '.')
50 379
            ->addOption(
51 379
                'report-json',
52 379
                null,
53 379
                InputOption::VALUE_REQUIRED,
54
                'Path to save detailed report in JSON format. Example: /tmp/report.json'
55 379
            );
56 379
    }
57
58
    protected function execute(InputInterface $input, OutputInterface $output)
0 ignored issues
show
Complexity introduced by
This operation has 240 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

You can also find more information in the “Code” section of your repository.

Loading history...
59
    {
60
        $output->writeln('');
61
62
        if (extension_loaded('xdebug')) {
63
            /**
64
             * This will disable only showing stack traces on error conditions.
65
             */
66
            if (function_exists('xdebug_disable')) {
67
                xdebug_disable();
68
            }
69
70
            $output->writeln('<error>It is highly recommended to disable the XDebug extension before invoking this command.</error>');
71
        }
72
73
        $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7, new \PhpParser\Lexer\Emulative(
74
            array(
75
                'usedAttributes' => array(
76
                    'comments',
77
                    'startLine',
78
                    'endLine',
79
                    'startTokenPos',
80
                    'endTokenPos'
81
                )
82
            )
83
        ));
84
85
        /** @var Application $application */
86
        $application = $this->getApplication();
87
        $application->compiler = new Compiler();
88
89
        $loader = new ConfigurationLoader(
90
            new FileLocator(
91
                [
92
                    realpath($input->getArgument('path')) . DIRECTORY_SEPARATOR
93
                ]
94
            )
95
        );
96
97
        $application->configuration = new Configuration(
98
            $loader->load('.phpsa.yml')
99
        );
100
101
        $em = EventManager::getInstance();
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $em. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
102
        Analyzer\Factory::factory($em);
103
        
104
        $context = new Context($output, $application, $em);
105
106
        /**
107
         * Store option's in application's configuration
108
         */
109
        $blame = $input->getOption('blame');
110
        if ($blame === -1) {
111
            $application->configuration->setValue('blame', $blame);
112
        }
113
114
        $fileParser = new FileParser(
115
            $parser,
116
            $this->getCompiler()
117
        );
118
119
        $path = $input->getArgument('path');
120
        if (is_dir($path)) {
121
            $directoryIterator = new RecursiveIteratorIterator(
122
                new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS)
123
            );
124
            $output->writeln('Scanning directory <info>' . $path . '</info>');
125
126
            $count = 0;
127
128
            /** @var SplFileInfo $file */
129
            foreach ($directoryIterator as $file) {
130
                if ($file->getExtension() != 'php') {
131
                    continue;
132
                }
133
134
                $context->debug($file->getPathname());
135
                $count++;
136
            }
137
138
            $output->writeln("Found <info>{$count} files</info>");
139
140
            if ($count > 100) {
141
                $output->writeln('<comment>Caution: You are trying to scan a lot of files; this might be slow. For bigger libraries, consider setting up a dedicated platform or using ci.lowl.io.</comment>');
142
            }
143
144
            $output->writeln('');
145
146
            /** @var SplFileInfo $file */
147
            foreach ($directoryIterator as $file) {
148
                if ($file->getExtension() != 'php') {
149
                    continue;
150
                }
151
152
                $fileParser->parserFile($file->getPathname(), $context);
153
            }
154
        } elseif (is_file($path)) {
155
            $fileParser->parserFile($path, $context);
156
        }
157
158
159
        /**
160
         * Step 2 Recursive check ...
161
         */
162
        $application->compiler->compile($context);
163
164
        $jsonReport = $input->getOption('report-json');
165
        if ($jsonReport) {
166
            file_put_contents(
167
                $jsonReport,
168
                json_encode(
169
                    $this->getApplication()->getIssuesCollector()->getIssues()
170
                )
171
            );
172
        }
173
174
        $output->writeln('');
175
        $output->writeln('Memory usage: ' . $this->getMemoryUsage(false) . ' (peak: ' . $this->getMemoryUsage(true) . ') MB');
176
    }
177
178
    /**
179
     * @param boolean $type
180
     * @return float
181
     */
182
    protected function getMemoryUsage($type)
183
    {
184
        return round(memory_get_usage($type) / 1024 / 1024, 2);
185
    }
186
187
    /**
188
     * @return Compiler
189
     */
190
    protected function getCompiler()
191
    {
192
        return $this->getApplication()->compiler;
193
    }
194
}
195