• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPCSStandards / PHP_CodeSniffer / 14023452056

23 Mar 2025 10:08PM UTC coverage: 78.708% (+0.2%) from 78.556%
14023452056

Pull #848

github

web-flow
Merge 98dc9607e into a4c38a9a5
Pull Request #848: [Docs] Add XML doc for Squiz.Commenting.BlockComment sniff

24845 of 31566 relevant lines covered (78.71%)

66.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

20.59
/autoload.php
1
<?php
2
/**
3
 * Autoloads files for PHP_CodeSniffer and tracks what has been loaded.
4
 *
5
 * Due to different namespaces being used for custom coding standards,
6
 * the autoloader keeps track of what class is loaded after a file is included,
7
 * even if the file is ultimately included by another autoloader (such as composer).
8
 *
9
 * This allows PHP_CodeSniffer to request the class name after loading a class
10
 * when it only knows the filename, without having to parse the file to find it.
11
 *
12
 * @author    Greg Sherwood <gsherwood@squiz.net>
13
 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
14
 * @license   https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
15
 */
16

17
namespace PHP_CodeSniffer;
18

19
use Composer\Autoload\ClassLoader;
20
use Exception;
21

22
if (class_exists('PHP_CodeSniffer\Autoload', false) === false) {
23
    class Autoload
24
    {
25

26
        /**
27
         * The composer autoloader.
28
         *
29
         * @var \Composer\Autoload\ClassLoader|false|null The autoloader object or FALSE if no Composer autoloader could
30
         *                                                be found. NULL when this hasn't been determined yet.
31
         */
32
        private static $composerAutoloader = null;
33

34
        /**
35
         * A mapping of file names to class names.
36
         *
37
         * @var array<string, string>
38
         */
39
        private static $loadedClasses = [];
40

41
        /**
42
         * A mapping of class names to file names.
43
         *
44
         * @var array<string, string>
45
         */
46
        private static $loadedFiles = [];
47

48
        /**
49
         * A list of additional directories to search during autoloading.
50
         *
51
         * This is typically a list of coding standard directories.
52
         *
53
         * @var string[]
54
         */
55
        private static $searchPaths = [];
56

57

58
        /**
59
         * Loads a class.
60
         *
61
         * This method only loads classes that exist in the PHP_CodeSniffer namespace.
62
         * All other classes are ignored and loaded by subsequent autoloaders.
63
         *
64
         * @param string $class The name of the class to load.
65
         *
66
         * @return bool
67
         */
68
        public static function load($class)
×
69
        {
70
            // Include the composer autoloader if there is one, but re-register it
71
            // so this autoloader runs before the composer one as we need to include
72
            // all files so we can figure out what the class/interface/trait name is.
73
            if (self::$composerAutoloader === null) {
×
74
                // Make sure we don't try to load any of Composer's classes
75
                // while the autoloader is being setup.
76
                if (strpos($class, 'Composer\\') === 0) {
×
77
                    return false;
×
78
                }
79

80
                if (strpos(__DIR__, 'phar://') !== 0
×
81
                    && @file_exists(__DIR__.'/../../autoload.php') === true
×
82
                ) {
83
                    self::$composerAutoloader = include __DIR__.'/../../autoload.php';
×
84
                    if (self::$composerAutoloader instanceof ClassLoader) {
×
85
                        self::$composerAutoloader->unregister();
×
86
                        self::$composerAutoloader->register();
×
87
                    } else {
88
                        // Something went wrong, so keep going without the autoloader
89
                        // although namespaced sniffs might error.
90
                        self::$composerAutoloader = false;
×
91
                    }
92
                } else {
93
                    self::$composerAutoloader = false;
×
94
                }
95
            }//end if
96

97
            $ds   = DIRECTORY_SEPARATOR;
×
98
            $path = false;
×
99

100
            if (substr($class, 0, 16) === 'PHP_CodeSniffer\\') {
×
101
                if (substr($class, 0, 22) === 'PHP_CodeSniffer\Tests\\') {
×
102
                    $isInstalled = !is_dir(__DIR__.$ds.'tests');
×
103
                    if ($isInstalled === false) {
×
104
                        $path = __DIR__.$ds.'tests';
×
105
                    } else {
106
                        $path = '@test_dir@'.$ds.'PHP_CodeSniffer'.$ds.'CodeSniffer';
×
107
                    }
108

109
                    $path .= $ds.substr(str_replace('\\', $ds, $class), 22).'.php';
×
110
                } else {
111
                    $path = __DIR__.$ds.'src'.$ds.substr(str_replace('\\', $ds, $class), 16).'.php';
×
112
                }
113
            }
114

115
            // See if the composer autoloader knows where the class is.
116
            if ($path === false && self::$composerAutoloader !== false) {
×
117
                $path = self::$composerAutoloader->findFile($class);
×
118
            }
119

120
            // See if the class is inside one of our alternate search paths.
121
            if ($path === false) {
×
122
                foreach (self::$searchPaths as $searchPath => $nsPrefix) {
×
123
                    $className = $class;
×
124
                    if ($nsPrefix !== '' && substr($class, 0, strlen($nsPrefix)) === $nsPrefix) {
×
125
                        $className = substr($class, (strlen($nsPrefix) + 1));
×
126
                    }
127

128
                    $path = $searchPath.$ds.str_replace('\\', $ds, $className).'.php';
×
129
                    if (is_file($path) === true) {
×
130
                        break;
×
131
                    }
132

133
                    $path = false;
×
134
                }
135
            }
136

137
            if ($path !== false && is_file($path) === true) {
×
138
                self::loadFile($path);
×
139
                return true;
×
140
            }
141

142
            return false;
×
143

144
        }//end load()
145

146

147
        /**
148
         * Includes a file and tracks what class or interface was loaded as a result.
149
         *
150
         * @param string $path The path of the file to load.
151
         *
152
         * @return string The fully qualified name of the class in the loaded file.
153
         */
154
        public static function loadFile($path)
×
155
        {
156
            if (strpos(__DIR__, 'phar://') !== 0) {
×
157
                $path = realpath($path);
×
158
                if ($path === false) {
×
159
                    return false;
×
160
                }
161
            }
162

163
            if (isset(self::$loadedClasses[$path]) === true) {
×
164
                return self::$loadedClasses[$path];
×
165
            }
166

167
            $classesBeforeLoad = [
168
                'classes'    => get_declared_classes(),
×
169
                'interfaces' => get_declared_interfaces(),
×
170
                'traits'     => get_declared_traits(),
×
171
            ];
172

173
            include $path;
×
174

175
            $classesAfterLoad = [
176
                'classes'    => get_declared_classes(),
×
177
                'interfaces' => get_declared_interfaces(),
×
178
                'traits'     => get_declared_traits(),
×
179
            ];
180

181
            $className = self::determineLoadedClass($classesBeforeLoad, $classesAfterLoad);
×
182

183
            self::$loadedClasses[$path]    = $className;
×
184
            self::$loadedFiles[$className] = $path;
×
185
            return self::$loadedClasses[$path];
×
186

187
        }//end loadFile()
188

189

190
        /**
191
         * Determine which class was loaded based on the before and after lists of loaded classes.
192
         *
193
         * @param array $classesBeforeLoad The classes/interfaces/traits before the file was included.
194
         * @param array $classesAfterLoad  The classes/interfaces/traits after the file was included.
195
         *
196
         * @return string The fully qualified name of the class in the loaded file.
197
         */
198
        public static function determineLoadedClass($classesBeforeLoad, $classesAfterLoad)
6✔
199
        {
200
            $className = null;
6✔
201

202
            $newClasses = array_diff($classesAfterLoad['classes'], $classesBeforeLoad['classes']);
6✔
203
            if (PHP_VERSION_ID < 70400) {
6✔
204
                $newClasses = array_reverse($newClasses);
4✔
205
            }
2✔
206

207
            // Since PHP 7.4 get_declared_classes() does not guarantee any order, making
208
            // it impossible to use order to determine which is the parent and which is the child.
209
            // Let's reduce the list of candidates by removing all the classes known to be "parents".
210
            // That way, at the end, only the "main" class just included will remain.
211
            $newClasses = array_reduce(
6✔
212
                $newClasses,
6✔
213
                function ($remaining, $current) {
4✔
214
                    return array_diff($remaining, class_parents($current));
6✔
215
                },
6✔
216
                $newClasses
4✔
217
            );
4✔
218

219
            foreach ($newClasses as $name) {
6✔
220
                if (isset(self::$loadedFiles[$name]) === false) {
6✔
221
                    $className = $name;
6✔
222
                    break;
6✔
223
                }
224
            }
2✔
225

226
            if ($className === null) {
6✔
227
                $newClasses = array_reverse(array_diff($classesAfterLoad['interfaces'], $classesBeforeLoad['interfaces']));
×
228
                foreach ($newClasses as $name) {
×
229
                    if (isset(self::$loadedFiles[$name]) === false) {
×
230
                        $className = $name;
×
231
                        break;
×
232
                    }
233
                }
234
            }
235

236
            if ($className === null) {
6✔
237
                $newClasses = array_reverse(array_diff($classesAfterLoad['traits'], $classesBeforeLoad['traits']));
×
238
                foreach ($newClasses as $name) {
×
239
                    if (isset(self::$loadedFiles[$name]) === false) {
×
240
                        $className = $name;
×
241
                        break;
×
242
                    }
243
                }
244
            }
245

246
            return $className;
6✔
247

248
        }//end determineLoadedClass()
249

250

251
        /**
252
         * Adds a directory to search during autoloading.
253
         *
254
         * @param string $path     The path to the directory to search.
255
         * @param string $nsPrefix The namespace prefix used by files under this path.
256
         *
257
         * @return void
258
         */
259
        public static function addSearchPath($path, $nsPrefix='')
×
260
        {
261
            self::$searchPaths[$path] = rtrim(trim((string) $nsPrefix), '\\');
×
262

263
        }//end addSearchPath()
264

265

266
        /**
267
         * Retrieve the namespaces and paths registered by external standards.
268
         *
269
         * @return array
270
         */
271
        public static function getSearchPaths()
×
272
        {
273
            return self::$searchPaths;
×
274

275
        }//end getSearchPaths()
276

277

278
        /**
279
         * Gets the class name for the given file path.
280
         *
281
         * @param string $path The name of the file.
282
         *
283
         * @throws \Exception If the file path has not been loaded.
284
         * @return string
285
         */
286
        public static function getLoadedClassName($path)
×
287
        {
288
            if (isset(self::$loadedClasses[$path]) === false) {
×
289
                throw new Exception("Cannot get class name for $path; file has not been included");
×
290
            }
291

292
            return self::$loadedClasses[$path];
×
293

294
        }//end getLoadedClassName()
295

296

297
        /**
298
         * Gets the file path for the given class name.
299
         *
300
         * @param string $class The name of the class.
301
         *
302
         * @throws \Exception If the class name has not been loaded.
303
         * @return string
304
         */
305
        public static function getLoadedFileName($class)
×
306
        {
307
            if (isset(self::$loadedFiles[$class]) === false) {
×
308
                throw new Exception("Cannot get file name for $class; class has not been included");
×
309
            }
310

311
            return self::$loadedFiles[$class];
×
312

313
        }//end getLoadedFileName()
314

315

316
        /**
317
         * Gets the mapping of file names to class names.
318
         *
319
         * @return array<string, string>
320
         */
321
        public static function getLoadedClasses()
×
322
        {
323
            return self::$loadedClasses;
×
324

325
        }//end getLoadedClasses()
326

327

328
        /**
329
         * Gets the mapping of class names to file names.
330
         *
331
         * @return array<string, string>
332
         */
333
        public static function getLoadedFiles()
×
334
        {
335
            return self::$loadedFiles;
×
336

337
        }//end getLoadedFiles()
338

339

340
    }//end class
341

342
    // Register the autoloader before any existing autoloaders to ensure
343
    // it gets a chance to hear about every autoload request, and record
344
    // the file and class name for it.
345
    spl_autoload_register(__NAMESPACE__.'\Autoload::load', true, true);
346
}//end if
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc