1<?php
2/**
3 * Squiz_Sniffs_CSS_DuplicateClassDefinitionSniff.
4 *
5 * PHP version 5
6 *
7 * @category  PHP
8 * @package   PHP_CodeSniffer
9 * @author    Greg Sherwood <gsherwood@squiz.net>
10 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
11 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
12 * @link      http://pear.php.net/package/PHP_CodeSniffer
13 */
14
15/**
16 * Squiz_Sniffs_CSS_DuplicateClassDefinitionSniff.
17 *
18 * Check for duplicate class definitions that can be merged into one.
19 *
20 * @category  PHP
21 * @package   PHP_CodeSniffer
22 * @author    Greg Sherwood <gsherwood@squiz.net>
23 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600)
24 * @license   https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
25 * @version   Release: @package_version@
26 * @link      http://pear.php.net/package/PHP_CodeSniffer
27 */
28class Squiz_Sniffs_CSS_DuplicateClassDefinitionSniff implements PHP_CodeSniffer_Sniff
29{
30
31    /**
32     * A list of tokenizers this sniff supports.
33     *
34     * @var array
35     */
36    public $supportedTokenizers = array('CSS');
37
38
39    /**
40     * Returns the token types that this sniff is interested in.
41     *
42     * @return int[]
43     */
44    public function register()
45    {
46        return array(T_OPEN_TAG);
47
48    }//end register()
49
50
51    /**
52     * Processes the tokens that this sniff is interested in.
53     *
54     * @param PHP_CodeSniffer_File $phpcsFile The file where the token was found.
55     * @param int                  $stackPtr  The position in the stack where
56     *                                        the token was found.
57     *
58     * @return void
59     */
60    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
61    {
62        $tokens = $phpcsFile->getTokens();
63
64        // Find the content of each class definition name.
65        $classNames = array();
66        $next       = $phpcsFile->findNext(T_OPEN_CURLY_BRACKET, ($stackPtr + 1));
67        if ($next === false) {
68            // No class definitions in the file.
69            return;
70        }
71
72        // Save the class names in a "scope",
73        // to prevent false positives with @media blocks.
74        $scope = 'main';
75
76        $find = array(
77                 T_CLOSE_CURLY_BRACKET,
78                 T_OPEN_CURLY_BRACKET,
79                 T_COMMENT,
80                 T_OPEN_TAG,
81                );
82
83        while ($next !== false) {
84            $prev = $phpcsFile->findPrevious($find, ($next - 1));
85
86            // Check if an inner block was closed.
87            $beforePrev = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($prev - 1), null, true);
88            if ($beforePrev !== false
89                && $tokens[$beforePrev]['code'] === T_CLOSE_CURLY_BRACKET
90            ) {
91                $scope = 'main';
92            }
93
94            // Create a sorted name for the class so we can compare classes
95            // even when the individual names are all over the place.
96            $name = '';
97            for ($i = ($prev + 1); $i < $next; $i++) {
98                $name .= $tokens[$i]['content'];
99            }
100
101            $name = trim($name);
102            $name = str_replace("\n", ' ', $name);
103            $name = preg_replace('|[\s]+|', ' ', $name);
104            $name = str_replace(', ', ',', $name);
105
106            $names = explode(',', $name);
107            sort($names);
108            $name = implode(',', $names);
109
110            if ($name{0} === '@') {
111                // Media block has its own "scope".
112                $scope = $name;
113            } else if (isset($classNames[$scope][$name]) === true) {
114                $first = $classNames[$scope][$name];
115                $error = 'Duplicate class definition found; first defined on line %s';
116                $data  = array($tokens[$first]['line']);
117                $phpcsFile->addError($error, $next, 'Found', $data);
118            } else {
119                $classNames[$scope][$name] = $next;
120            }
121
122            $next = $phpcsFile->findNext(T_OPEN_CURLY_BRACKET, ($next + 1));
123        }//end while
124
125    }//end process()
126
127
128}//end class
129