1<?php
2/**
3 * Generic_Sniffs_NamingConventions_UpperCaseConstantNameSniff.
4 *
5 * PHP version 5
6 *
7 * @category  PHP
8 * @package   PHP_CodeSniffer
9 * @author    Greg Sherwood <gsherwood@squiz.net>
10 * @author    Marc McIntyre <mmcintyre@squiz.net>
11 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
12 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
13 * @link      http://pear.php.net/package/PHP_CodeSniffer
14 */
15
16/**
17 * Generic_Sniffs_NamingConventions_UpperCaseConstantNameSniff.
18 *
19 * Ensures that constant names are all uppercase.
20 *
21 * @category  PHP
22 * @package   PHP_CodeSniffer
23 * @author    Greg Sherwood <gsherwood@squiz.net>
24 * @author    Marc McIntyre <mmcintyre@squiz.net>
25 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
26 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
27 * @version   Release: @package_version@
28 * @link      http://pear.php.net/package/PHP_CodeSniffer
29 */
30class Generic_Sniffs_NamingConventions_UpperCaseConstantNameSniff implements PHP_CodeSniffer_Sniff
31{
32
33
34    /**
35     * Returns an array of tokens this test wants to listen for.
36     *
37     * @return array
38     */
39    public function register()
40    {
41        return array(T_STRING);
42
43    }//end register()
44
45
46    /**
47     * Processes this test, when one of its tokens is encountered.
48     *
49     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
50     * @param int                  $stackPtr  The position of the current token in the
51     *                                        stack passed in $tokens.
52     *
53     * @return void
54     */
55    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
56    {
57        $tokens    = $phpcsFile->getTokens();
58        $constName = $tokens[$stackPtr]['content'];
59
60        // If this token is in a heredoc, ignore it.
61        if ($phpcsFile->hasCondition($stackPtr, T_START_HEREDOC) === true) {
62            return;
63        }
64
65        // Special case for PHP 5.5 class name resolution.
66        if (strtolower($constName) === 'class'
67            && $tokens[($stackPtr - 1)]['code'] === T_DOUBLE_COLON
68        ) {
69            return;
70        }
71
72        // Special case for PHPUnit.
73        if ($constName === 'PHPUnit_MAIN_METHOD') {
74            return;
75        }
76
77        // If the next non-whitespace token after this token
78        // is not an opening parenthesis then it is not a function call.
79        for ($openBracket = ($stackPtr + 1); $openBracket < $phpcsFile->numTokens; $openBracket++) {
80            if ($tokens[$openBracket]['code'] !== T_WHITESPACE) {
81                break;
82            }
83        }
84
85        if ($openBracket === $phpcsFile->numTokens) {
86            return;
87        }
88
89        if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) {
90            $functionKeyword = $phpcsFile->findPrevious(
91                array(
92                 T_WHITESPACE,
93                 T_COMMA,
94                 T_COMMENT,
95                 T_STRING,
96                 T_NS_SEPARATOR,
97                ),
98                ($stackPtr - 1),
99                null,
100                true
101            );
102
103            if ($tokens[$functionKeyword]['code'] !== T_CONST) {
104                return;
105            }
106
107            // This is a class constant.
108            if (strtoupper($constName) !== $constName) {
109                if (strtolower($constName) === $constName) {
110                    $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'lower');
111                } else {
112                    $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'mixed');
113                }
114
115                $error = 'Class constants must be uppercase; expected %s but found %s';
116                $data  = array(
117                          strtoupper($constName),
118                          $constName,
119                         );
120                $phpcsFile->addError($error, $stackPtr, 'ClassConstantNotUpperCase', $data);
121            } else {
122                $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'upper');
123            }
124
125            return;
126        }//end if
127
128        if (strtolower($constName) !== 'define') {
129            return;
130        }
131
132        /*
133            This may be a "define" function call.
134        */
135
136        // Make sure this is not a method call.
137        $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
138        if ($tokens[$prev]['code'] === T_OBJECT_OPERATOR
139            || $tokens[$prev]['code'] === T_DOUBLE_COLON
140        ) {
141            return;
142        }
143
144        // The next non-whitespace token must be the constant name.
145        $constPtr = $phpcsFile->findNext(T_WHITESPACE, ($openBracket + 1), null, true);
146        if ($tokens[$constPtr]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
147            return;
148        }
149
150        $constName = $tokens[$constPtr]['content'];
151
152        // Check for constants like self::CONSTANT.
153        $prefix   = '';
154        $splitPos = strpos($constName, '::');
155        if ($splitPos !== false) {
156            $prefix    = substr($constName, 0, ($splitPos + 2));
157            $constName = substr($constName, ($splitPos + 2));
158        }
159
160        // Strip namesspace from constant like /foo/bar/CONSTANT.
161        $splitPos = strrpos($constName, '\\');
162        if ($splitPos !== false) {
163            $prefix    = substr($constName, 0, ($splitPos + 1));
164            $constName = substr($constName, ($splitPos + 1));
165        }
166
167        if (strtoupper($constName) !== $constName) {
168            if (strtolower($constName) === $constName) {
169                $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'lower');
170            } else {
171                $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'mixed');
172            }
173
174            $error = 'Constants must be uppercase; expected %s but found %s';
175            $data  = array(
176                      $prefix.strtoupper($constName),
177                      $prefix.$constName,
178                     );
179            $phpcsFile->addError($error, $stackPtr, 'ConstantNotUpperCase', $data);
180        } else {
181            $phpcsFile->recordMetric($stackPtr, 'Constant name case', 'upper');
182        }
183
184    }//end process()
185
186
187}//end class
188