Passed
Push — master ( 3ccb1e...6213ef )
by Caen
02:57 queued 12s
created

HydeStan::addActionsMessage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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