Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
FullQualifiedClassNameSniff
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 2
272
0.00% covered (danger)
0.00%
0 / 1
 register
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
240
1<?php
2
3namespace MediaWiki\Sniffs\Classes;
4
5use PHP_CodeSniffer\Files\File;
6use PHP_CodeSniffer\Sniffs\Sniff;
7use PHP_CodeSniffer\Util\Tokens;
8
9/**
10 * Custom sniff that disallows full qualified class names outside of the "use …" section.
11 * Exceptions:
12 * - Full qualified class names in the main namespace (e.g. \Title) are allowed, by default. This is
13 *   a very common code style in MediaWiki-related codebases. Removing these backslash characters
14 *   doesn't really make code better readable.
15 * - Full qualified class names in "extends" and "implements" are allowed, by default. This is a
16 *   very common code style, especially in tests. Since these can only appear once per class, and
17 *   are guaranteed to be at the very top, moving them to "use" statements doesn't really make code
18 *   better readable.
19 * - Function calls like \Wikimedia\suppressWarnings() are allowed, by default.
20 *
21 * Note this sniff is disabled by default. You need to increase it's severity in your .phpcs.xml to
22 * enable it.
23 *
24 * Example configuration:
25 * <rule ref="MediaWiki.Classes.FullQualifiedClassName">
26 *     <severity>5</severity>
27 *     <properties>
28 *         <property name="allowMainNamespace" value="false" />
29 *         <property name="allowInheritance" value="false" />
30 *         <property name="allowFunctions" value="false" />
31 *     </properties>
32 * </rule>
33 *
34 * Note this sniff currently does not check class names mentioned in PHPDoc comments.
35 *
36 * @author Thiemo Kreuz
37 */
38class FullQualifiedClassNameSniff implements Sniff {
39
40    /**
41     * @var bool Allows full qualified class names in the main namespace, e.g. \Title
42     */
43    public bool $allowMainNamespace = true;
44
45    /**
46     * @var bool Allows to use full qualified class names in "extends" and "implements"
47     */
48    public bool $allowInheritance = true;
49
50    /**
51     * @var bool Allows function calls like \Wikimedia\suppressWarnings()
52     */
53    public bool $allowFunctions = true;
54
55    /**
56     * @inheritDoc
57     */
58    public function register(): array {
59        return [ T_NS_SEPARATOR ];
60    }
61
62    /**
63     * @inheritDoc
64     */
65    public function process( File $phpcsFile, $stackPtr ) {
66        $tokens = $phpcsFile->getTokens();
67
68        if ( !isset( $tokens[$stackPtr + 2] )
69            // The current backslash is not the last one in the full qualified class name
70            || $tokens[$stackPtr + 2]['code'] === T_NS_SEPARATOR
71            // Some unexpected backslash that's not part of a class name
72            || $tokens[$stackPtr + 1]['code'] !== T_STRING
73        ) {
74            return;
75        }
76
77        if ( $this->allowMainNamespace
78            && $tokens[$stackPtr - 1]['code'] !== T_STRING
79        ) {
80            return;
81        }
82
83        if ( $this->allowFunctions
84            && $tokens[$stackPtr + 2]['code'] === T_OPEN_PARENTHESIS
85        ) {
86            return;
87        }
88
89        $skip = Tokens::$emptyTokens;
90        $skip[] = T_STRING;
91        $skip[] = T_NS_SEPARATOR;
92        $prev = $phpcsFile->findPrevious( $skip, $stackPtr - 2, null, true );
93        if ( !$prev ) {
94            return;
95        }
96        $prev = $tokens[$prev]['code'];
97
98        // "namespace" and "use" statements must use full qualified class names
99        if ( $prev === T_NAMESPACE || $prev === T_USE ) {
100            return;
101        }
102
103        if ( $this->allowInheritance
104            && ( $prev === T_EXTENDS || $prev === T_IMPLEMENTS )
105        ) {
106            return;
107        }
108
109        $phpcsFile->addError(
110            'Full qualified class name "%s\\%s" found, please utilize "use …"',
111            $stackPtr,
112            'Found',
113            [
114                $tokens[$stackPtr - 1]['code'] === T_STRING ? '…' : '',
115                $tokens[$stackPtr + 1]['content'],
116            ]
117        );
118    }
119
120}