GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Issues (4)

src/Markdown/LinksChecker.php (1 issue)

Labels
Severity
1
<?php
2
3
declare(strict_types=1);
4
5
namespace EdmondsCommerce\PHPQA\Markdown;
6
7
use EdmondsCommerce\PHPQA\Helper;
8
use Exception;
9
use RecursiveDirectoryIterator;
10
use RecursiveIteratorIterator;
11
use RecursiveRegexIterator;
12
use RegexIterator;
13
use RuntimeException;
14
use Throwable;
15
16
use function current;
17
use function dirname;
18
use function explode;
19
use function preg_match;
20
use function preg_replace;
21
use function realpath;
22
use function rtrim;
23
use function sprintf;
24
use function str_repeat;
25
use function str_replace;
26
use function strlen;
27
use function strpos;
28
use function trim;
29
30
final class LinksChecker
31
{
32
    /**
33
     * @throws Exception
34
     * @SuppressWarnings(PHPMD.StaticAccess)
35
     */
36 4
    public static function main(string $projectRootDirectory = null): int
37
    {
38 4
        $return               = 0;
39 4
        $projectRootDirectory = $projectRootDirectory ?? Helper::getProjectRootDirectory();
40 4
        $files                = static::getFiles($projectRootDirectory);
41 3
        foreach ($files as $file) {
42 3
            $relativeFile = str_replace($projectRootDirectory, '', $file);
43 3
            $title        = "\n{$relativeFile}\n" . str_repeat('-', strlen($relativeFile)) . "\n";
44 3
            $errors       = [];
45 3
            $links        = static::getLinks($file);
46 3
            foreach ($links as $link) {
47 3
                static::checkLink($projectRootDirectory, $link, $file, $errors, $return);
48
            }
49 3
            if ([] !== $errors) {
50 2
                echo $title . implode('', $errors);
51
            }
52
        }
53
54 3
        return $return;
55
    }
56
57
    /**
58
     * @return string[]
59
     * @SuppressWarnings(PHPMD.StaticAccess)
60
     */
61 4
    private static function getFiles(string $projectRootDirectory): array
62
    {
63 4
        $files   = self::getDocsFiles($projectRootDirectory);
64 4
        $files[] = self::getMainReadme($projectRootDirectory);
65
66 3
        return $files;
67
    }
68
69
    /**
70
     * @SuppressWarnings(PHPMD.StaticAccess)
71
     *
72
     * @return string[]
73
     */
74 4
    private static function getDocsFiles(string $projectRootDirectory): array
75
    {
76 4
        $files = [];
77 4
        $dir   = $projectRootDirectory . '/docs';
78 4
        if (!is_dir($dir)) {
79 2
            return $files;
80
        }
81 2
        $directory = new RecursiveDirectoryIterator($dir);
82 2
        $recursive = new RecursiveIteratorIterator($directory);
83 2
        $regex     = new RegexIterator(
84 2
            $recursive,
85 2
            '/^.+\.md/i',
86 2
            RecursiveRegexIterator::GET_MATCH
87
        );
88 2
        foreach ($regex as $file) {
89 2
            if ($file[0] !== '') {
90 2
                $files[] = $file[0];
91
            }
92
        }
93
94 2
        return $files;
95
    }
96
97
    /**
98
     * @SuppressWarnings(PHPMD.StaticAccess)
99
     */
100 4
    private static function getMainReadme(string $projectRootDirectory): string
101
    {
102 4
        $path = $projectRootDirectory . '/README.md';
103 4
        if (!is_file($path)) {
104 1
            throw new RuntimeException(
105
                "\n\nYou have no README.md file in your project"
106 1
                . "\n\nAs the bear minimum you need to have this file to pass QA"
107
            );
108
        }
109
110 3
        return $path;
111
    }
112
113
    /**
114
     * @SuppressWarnings(PHPMD.StaticAccess)
115
     *
116
     * @return array<array<string>>
117
     */
118 3
    private static function getLinks(string $file): array
119
    {
120 3
        $links    = [];
121 3
        $contents = (string)file_get_contents($file);
122 3
        $matches  = null;
123
        if (
124 3
            preg_match_all(
125 3
                '/\[(.+?)\].*?\((.+?)\)/',
126 3
                $contents,
127 3
                $matches,
128 3
                PREG_SET_ORDER
129 3
            ) !== false
130
        ) {
131 3
            $links = array_merge($links, $matches);
132
        }
133
134 3
        return $links;
135
    }
136
137
    /**
138
     * @SuppressWarnings(PHPMD.StaticAccess)
139
     *
140
     * @param string[] $link
141
     * @param string[] $errors
142
     */
143 3
    private static function checkLink(
144
        string $projectRootDirectory,
145
        array $link,
146
        string $file,
147
        array &$errors,
148
        int &$return
149
    ): void {
150 3
        $path = trim($link[2]);
151 3
        if (strpos($path, '#') === 0) {
152 1
            return;
153
        }
154 3
        if (preg_match('%^(http|//)%', $path) === 1) {
155 1
            self::validateHttpLink($link, $errors, $return);
156
157 1
            return;
158
        }
159
160 2
        $path  = current(explode('#', $path, 2));
161 2
        $start = rtrim($projectRootDirectory, '/');
162 2
        if ($path[0] !== '/' || strpos($path, './') === 0) {
163 2
            $relativeSubdirs = preg_replace(
164 2
                '%^' . $projectRootDirectory . '%',
165 2
                '',
166 2
                dirname($file)
167
            );
168 2
            if ($relativeSubdirs !== null) {
169 2
                $start .= '/' . rtrim($relativeSubdirs, '/');
170
            }
171
        }
172 2
        $realpath = realpath($start . '/' . $path);
173 2
        if ($realpath === false) {
174 1
            $errors[] = sprintf("\nBad link for \"%s\" to \"%s\"\n", $link[1], $link[2]);
175 1
            $return   = 1;
176
        }
177 2
    }
178
179
    /**
180
     * @param string[] $link
181
     * @param string[] $errors
182
     * @SuppressWarnings(PHPMD.UndefinedVariable) - seems to not understand the static variable
183
     */
184 1
    private static function validateHttpLink(array $link, array &$errors, int &$return): void
185
    {
186 1
        static $checked        = [];
187 1
        list(, $anchor, $href) = $link;
188 1
        $hashPos               = (int)strpos($href, '#');
189 1
        if ($hashPos > 0) {
190
            $href = substr($href, 0, $hashPos);
191
        }
192 1
        if (isset($checked[$href])) {
193
            return;
194
        }
195 1
        $checked[$href] = true;
196 1
        $context        = stream_context_create(
197
            [
198 1
                'http' => [
199
                    'method'           => 'HEAD',
200
                    'protocol_version' => 1.1,
201
                    'header'           => [
202
                        'Connection: close',
203
                    ],
204
                ],
205
            ]
206
        );
207 1
        $result         = null;
208
        try {
209 1
            $headers = get_headers($href, 0, $context);
0 ignored issues
show
$context of type resource is incompatible with the type null|resource expected by parameter $context of get_headers(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

209
            $headers = get_headers($href, 0, /** @scrutinizer ignore-type */ $context);
Loading history...
210 1
            if ($headers === false) {
211
                throw new RuntimeException('Failed getting headers for href ' . $href);
212
            }
213 1
            foreach ($headers as $header) {
214 1
                if (strpos($header, ' 200 ') !== false) {
215
                    //$time = round(microtime(true) - $start, 2);
216
                    //fwrite(STDERR, "\n".'OK ('.$time.' seconds): '.$href);
217
218 1
                    return;
219
                }
220
            }
221
        } catch (Throwable $e) {
222
            throw new RuntimeException('Unexpected error ' . $e->getMessage(), $e->getCode(), $e);
223
        }
224
225 1
        $errors[] = sprintf(
226 1
            "\nBad link for \"%s\" to \"%s\"\nresult: %s\n",
227 1
            $anchor,
228 1
            $href,
229 1
            var_export($result, true)
230
        );
231 1
        $return   = 1;
232
        //$time     = round(microtime(true) - $start, 2);
233
        //fwrite(STDERR, "\n".'Failed ('.$time.' seconds): '.$href);
234 1
    }
235
}
236