1<?php
2/**
3 * Squiz_Sniffs_CSS_ForbiddenStylesSniff.
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_ForbiddenStylesSniff.
17 *
18 * Bans the use of some styles, such as deprecated or browser-specific styles.
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_ForbiddenStylesSniff 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     * A list of forbidden styles with their alternatives.
40     *
41     * The value is NULL if no alternative exists. i.e., the
42     * function should just not be used.
43     *
44     * @var array(string => string|null)
45     */
46    protected $forbiddenStyles = array(
47                                  '-moz-border-radius'             => 'border-radius',
48                                  '-webkit-border-radius'          => 'border-radius',
49                                  '-moz-border-radius-topleft'     => 'border-top-left-radius',
50                                  '-moz-border-radius-topright'    => 'border-top-right-radius',
51                                  '-moz-border-radius-bottomright' => 'border-bottom-right-radius',
52                                  '-moz-border-radius-bottomleft'  => 'border-bottom-left-radius',
53                                  '-moz-box-shadow'                => 'box-shadow',
54                                  '-webkit-box-shadow'             => 'box-shadow',
55                                 );
56
57    /**
58     * A cache of forbidden style names, for faster lookups.
59     *
60     * @var array(string)
61     */
62    protected $forbiddenStyleNames = array();
63
64    /**
65     * If true, forbidden styles will be considered regular expressions.
66     *
67     * @var bool
68     */
69    protected $patternMatch = false;
70
71    /**
72     * If true, an error will be thrown; otherwise a warning.
73     *
74     * @var bool
75     */
76    public $error = true;
77
78
79    /**
80     * Returns an array of tokens this test wants to listen for.
81     *
82     * @return array
83     */
84    public function register()
85    {
86        $this->forbiddenStyleNames = array_keys($this->forbiddenStyles);
87
88        if ($this->patternMatch === true) {
89            foreach ($this->forbiddenStyleNames as $i => $name) {
90                $this->forbiddenStyleNames[$i] = '/'.$name.'/i';
91            }
92        }
93
94        return array(T_STYLE);
95
96    }//end register()
97
98
99    /**
100     * Processes this test, when one of its tokens is encountered.
101     *
102     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
103     * @param int                  $stackPtr  The position of the current token in
104     *                                        the stack passed in $tokens.
105     *
106     * @return void
107     */
108    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
109    {
110        $tokens  = $phpcsFile->getTokens();
111        $style   = strtolower($tokens[$stackPtr]['content']);
112        $pattern = null;
113
114        if ($this->patternMatch === true) {
115            $count   = 0;
116            $pattern = preg_replace(
117                $this->forbiddenStyleNames,
118                $this->forbiddenStyleNames,
119                $style,
120                1,
121                $count
122            );
123
124            if ($count === 0) {
125                return;
126            }
127
128            // Remove the pattern delimiters and modifier.
129            $pattern = substr($pattern, 1, -2);
130        } else {
131            if (in_array($style, $this->forbiddenStyleNames) === false) {
132                return;
133            }
134        }//end if
135
136        $this->addError($phpcsFile, $stackPtr, $style, $pattern);
137
138    }//end process()
139
140
141    /**
142     * Generates the error or warning for this sniff.
143     *
144     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
145     * @param int                  $stackPtr  The position of the forbidden style
146     *                                        in the token array.
147     * @param string               $style     The name of the forbidden style.
148     * @param string               $pattern   The pattern used for the match.
149     *
150     * @return void
151     */
152    protected function addError($phpcsFile, $stackPtr, $style, $pattern=null)
153    {
154        $data  = array($style);
155        $error = 'The use of style %s is ';
156        if ($this->error === true) {
157            $type   = 'Found';
158            $error .= 'forbidden';
159        } else {
160            $type   = 'Discouraged';
161            $error .= 'discouraged';
162        }
163
164        if ($pattern === null) {
165            $pattern = $style;
166        }
167
168        if ($this->forbiddenStyles[$pattern] !== null) {
169            $data[] = $this->forbiddenStyles[$pattern];
170            if ($this->error === true) {
171                $fix = $phpcsFile->addFixableError($error.'; use %s instead', $stackPtr, $type.'WithAlternative', $data);
172            } else {
173                $fix = $phpcsFile->addFixableWarning($error.'; use %s instead', $stackPtr, $type.'WithAlternative', $data);
174            }
175
176            if ($fix === true) {
177                $phpcsFile->fixer->replaceToken($stackPtr, $this->forbiddenStyles[$pattern]);
178            }
179        } else {
180            if ($this->error === true) {
181                $phpcsFile->addError($error, $stackPtr, $type, $data);
182            } else {
183                $phpcsFile->addWarning($error, $stackPtr, $type, $data);
184            }
185        }
186
187    }//end addError()
188
189
190}//end class
191