Passed
Branch master (0511c6)
by Caen
03:10
created

NoFixMeAnalyser   A

Complexity

Total Complexity 3

Size/Duplication

Total Lines 29
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 14
dl 0
loc 29
rs 10
c 0
b 0
f 0
wmc 3
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * @internal
7
 */
8
class HydeStan
9
{
10
    const VERSION = '0.0.0-dev';
11
12
    protected array $files;
13
    protected array $errors = [];
14
    protected int $scannedLines = 0;
15
    protected Console $console;
16
    protected static array $warnings = [];
17
18
    public function __construct(protected bool $debug = false)
19
    {
20
        $this->console = new Console();
21
22
        $this->console->info(sprintf('HydeStan v%s is running!', self::VERSION));
23
        $this->console->newline();
24
    }
25
26
    public function __destruct()
27
    {
28
        $this->console->newline();
29
        $this->console->info(sprintf('HydeStan has exited after scanning %s total lines in %s files.',
30
            number_format($this->scannedLines),
31
            number_format(count($this->files)))
32
        );
33
34
        if (count(self::$warnings) > 0) {
35
            // Forward warnings to GitHub Actions
36
            $this->console->line(sprintf("\n%s", implode("\n", self::$warnings)));
37
        }
38
    }
39
40
    public function run(): void
41
    {
42
        $time = microtime(true);
43
44
        $this->files = $this->getFiles();
45
46
        foreach ($this->files as $file) {
47
            if ($this->debug) {
48
                $this->console->debug('Analysing file: '.$file);
49
            }
50
51
            $this->analyseFile($file, $this->getFileContents($file));
52
        }
53
54
        $endTime = microtime(true) - $time;
55
        $this->console->info(sprintf('HydeStan has finished in %s seconds (%sms) using %s KB RAM',
56
            number_format($endTime, 2),
57
            number_format($endTime * 1000, 2),
58
            number_format(memory_get_peak_usage(true) / 1024, 2))
59
        );
60
61
        if ($this->hasErrors()) {
62
            $this->console->error(sprintf('HydeStan has found %s errors!', count($this->errors)));
63
64
            foreach ($this->errors as $error) {
65
                $this->console->warn($error);
66
            }
67
        } else {
68
            $this->console->info('HydeStan has found no errors!');
69
        }
70
    }
71
72
    public function getErrors(): array
73
    {
74
        return $this->errors;
75
    }
76
77
    private function getFiles(): array
78
    {
79
        $files = [];
80
81
        $directory = new RecursiveDirectoryIterator(BASE_PATH.'/src');
82
        $iterator = new RecursiveIteratorIterator($directory);
83
        $regex = new RegexIterator($iterator, '/^.+\.php$/i', RecursiveRegexIterator::GET_MATCH);
84
85
        foreach ($regex as $file) {
86
            $files[] = substr($file[0], strlen(BASE_PATH) + 1);
87
        }
88
89
        return $files;
90
    }
91
92
    private function analyseFile(string $file, string $contents): void
93
    {
94
        foreach ($this->analysers() as $analyser) {
95
            if ($this->debug) {
96
                $this->console->debugComment('Running  '.$analyser::class);
97
            }
98
99
            $result = $analyser->run($file, $contents);
100
            foreach ($result as $error) {
101
                if ($this->debug) {
102
                    $this->console->debugComment('Adding error: '.$error);
103
                }
104
                $this->errors[] = $error;
105
            }
106
        }
107
108
        $this->scannedLines += substr_count($contents, "\n");
109
    }
110
111
    private function getFileContents(string $file): string
112
    {
113
        return file_get_contents(BASE_PATH.'/'.$file);
114
    }
115
116
    private function analysers(): array
117
    {
118
        return [
119
            new NoFixMeAnalyser(),
120
        ];
121
    }
122
123
    public function hasErrors(): bool
124
    {
125
        return count($this->errors) > 0;
126
    }
127
128
    public static function addActionsMessage(string $level, string $file, int $lineNumber, string $title, string $message): void
129
    {
130
        // https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-warning-message
131
        // $template = '::warning file={name},line={line},endLine={endLine},title={title}::{message}';
132
        self::$warnings[] = sprintf("::$level file=%s,line=%s,endLine=%s,title=%s::%s", 'packages/framework/'.str_replace('\\', '/', $file), $lineNumber, $lineNumber, $title, $message);
133
    }
134
}
135
136
class NoFixMeAnalyser
137
{
138
    public function run(string $file, string $contents): array
139
    {
140
        $errors = [];
141
142
        $searches = [
143
            'fixme',
144
            'fix me',
145
            'fix-me',
146
        ];
147
148
        $contents = strtolower($contents);
149
150
        foreach ($searches as $search) {
151
            if (str_contains($contents, $search)) {
152
                // Get line number of marker by counting new \n tags before it
153
                $stringBeforeMarker = substr($contents, 0, strpos($contents, $search));
154
                $lineNumber = substr_count($stringBeforeMarker, "\n") + 1;
155
156
                $errors[] = "Found $search in $file on line $lineNumber";
157
158
                HydeStan::addActionsMessage('warning', $file, $lineNumber, 'HydeStan: NoFixMeError', 'This line has been marked as needing fixing. Please fix it before merging.');
159
160
                // Todo we might want to check for more errors after the first marker
161
            }
162
        }
163
164
        return $errors;
165
    }
166
}
167