1<?php
2/**
3 * Squiz_Sniffs_ControlStructures_ForLoopDeclarationSniff.
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 * Squiz_Sniffs_ControlStructures_ForLoopDeclarationSniff.
18 *
19 * Verifies that there is a space between each condition of for loops.
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 Squiz_Sniffs_ControlStructures_ForLoopDeclarationSniff implements PHP_CodeSniffer_Sniff
31{
32
33    /**
34     * How many spaces should follow the opening bracket.
35     *
36     * @var int
37     */
38    public $requiredSpacesAfterOpen = 0;
39
40    /**
41     * How many spaces should precede the closing bracket.
42     *
43     * @var int
44     */
45    public $requiredSpacesBeforeClose = 0;
46
47    /**
48     * A list of tokenizers this sniff supports.
49     *
50     * @var array
51     */
52    public $supportedTokenizers = array(
53                                   'PHP',
54                                   'JS',
55                                  );
56
57
58    /**
59     * Returns an array of tokens this test wants to listen for.
60     *
61     * @return array
62     */
63    public function register()
64    {
65        return array(T_FOR);
66
67    }//end register()
68
69
70    /**
71     * Processes this test, when one of its tokens is encountered.
72     *
73     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
74     * @param int                  $stackPtr  The position of the current token in the
75     *                                        stack passed in $tokens.
76     *
77     * @return void
78     */
79    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
80    {
81        $this->requiredSpacesAfterOpen   = (int) $this->requiredSpacesAfterOpen;
82        $this->requiredSpacesBeforeClose = (int) $this->requiredSpacesBeforeClose;
83        $tokens = $phpcsFile->getTokens();
84
85        $openingBracket = $phpcsFile->findNext(T_OPEN_PARENTHESIS, $stackPtr);
86        if ($openingBracket === false) {
87            $error = 'Possible parse error: no opening parenthesis for FOR keyword';
88            $phpcsFile->addWarning($error, $stackPtr, 'NoOpenBracket');
89            return;
90        }
91
92        $closingBracket = $tokens[$openingBracket]['parenthesis_closer'];
93
94        if ($this->requiredSpacesAfterOpen === 0 && $tokens[($openingBracket + 1)]['code'] === T_WHITESPACE) {
95            $error = 'Space found after opening bracket of FOR loop';
96            $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterOpen');
97            if ($fix === true) {
98                $phpcsFile->fixer->replaceToken(($openingBracket + 1), '');
99            }
100        } else if ($this->requiredSpacesAfterOpen > 0) {
101            $spaceAfterOpen = 0;
102            if ($tokens[($openingBracket + 1)]['code'] === T_WHITESPACE) {
103                $spaceAfterOpen = strlen($tokens[($openingBracket + 1)]['content']);
104            }
105
106            if ($spaceAfterOpen !== $this->requiredSpacesAfterOpen) {
107                $error = 'Expected %s spaces after opening bracket; %s found';
108                $data  = array(
109                          $this->requiredSpacesAfterOpen,
110                          $spaceAfterOpen,
111                         );
112                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterOpen', $data);
113                if ($fix === true) {
114                    $padding = str_repeat(' ', $this->requiredSpacesAfterOpen);
115                    if ($spaceAfterOpen === 0) {
116                        $phpcsFile->fixer->addContent($openingBracket, $padding);
117                    } else {
118                        $phpcsFile->fixer->replaceToken(($openingBracket + 1), $padding);
119                    }
120                }
121            }
122        }//end if
123
124        if ($this->requiredSpacesBeforeClose === 0 && $tokens[($closingBracket - 1)]['code'] === T_WHITESPACE) {
125            $error = 'Space found before closing bracket of FOR loop';
126            $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeClose');
127            if ($fix === true) {
128                $phpcsFile->fixer->replaceToken(($closingBracket - 1), '');
129            }
130        } else if ($this->requiredSpacesBeforeClose > 0) {
131            $spaceBeforeClose = 0;
132            if ($tokens[($closingBracket - 1)]['code'] === T_WHITESPACE) {
133                $spaceBeforeClose = strlen($tokens[($closingBracket - 1)]['content']);
134            }
135
136            if ($this->requiredSpacesBeforeClose !== $spaceBeforeClose) {
137                $error = 'Expected %s spaces before closing bracket; %s found';
138                $data  = array(
139                          $this->requiredSpacesBeforeClose,
140                          $spaceBeforeClose,
141                         );
142                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeClose', $data);
143                if ($fix === true) {
144                    $padding = str_repeat(' ', $this->requiredSpacesBeforeClose);
145                    if ($spaceBeforeClose === 0) {
146                        $phpcsFile->fixer->addContentBefore($closingBracket, $padding);
147                    } else {
148                        $phpcsFile->fixer->replaceToken(($closingBracket - 1), $padding);
149                    }
150                }
151            }
152        }//end if
153
154        $firstSemicolon = $phpcsFile->findNext(T_SEMICOLON, $openingBracket, $closingBracket);
155
156        // Check whitespace around each of the tokens.
157        if ($firstSemicolon !== false) {
158            if ($tokens[($firstSemicolon - 1)]['code'] === T_WHITESPACE) {
159                $error = 'Space found before first semicolon of FOR loop';
160                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeFirst');
161                if ($fix === true) {
162                    $phpcsFile->fixer->replaceToken(($firstSemicolon - 1), '');
163                }
164            }
165
166            if ($tokens[($firstSemicolon + 1)]['code'] !== T_WHITESPACE
167                && $tokens[($firstSemicolon + 1)]['code'] !== T_SEMICOLON
168            ) {
169                $error = 'Expected 1 space after first semicolon of FOR loop; 0 found';
170                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterFirst');
171                if ($fix === true) {
172                    $phpcsFile->fixer->addContent($firstSemicolon, ' ');
173                }
174            } else {
175                if (strlen($tokens[($firstSemicolon + 1)]['content']) !== 1) {
176                    $spaces = strlen($tokens[($firstSemicolon + 1)]['content']);
177                    $error  = 'Expected 1 space after first semicolon of FOR loop; %s found';
178                    $data   = array($spaces);
179                    $fix    = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterFirst', $data);
180                    if ($fix === true) {
181                        $phpcsFile->fixer->replaceToken(($firstSemicolon + 1), ' ');
182                    }
183                }
184            }
185
186            $secondSemicolon = $phpcsFile->findNext(T_SEMICOLON, ($firstSemicolon + 1));
187
188            if ($secondSemicolon !== false) {
189                if ($tokens[($secondSemicolon - 1)]['code'] === T_WHITESPACE
190                    && $tokens[($firstSemicolon + 1)]['code'] !== T_SEMICOLON
191                ) {
192                    $error = 'Space found before second semicolon of FOR loop';
193                    $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeSecond');
194                    if ($fix === true) {
195                        $phpcsFile->fixer->replaceToken(($secondSemicolon - 1), '');
196                    }
197                }
198
199                if (($secondSemicolon + 1) !== $closingBracket
200                    && $tokens[($secondSemicolon + 1)]['code'] !== T_WHITESPACE
201                ) {
202                    $error = 'Expected 1 space after second semicolon of FOR loop; 0 found';
203                    $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'NoSpaceAfterSecond');
204                    if ($fix === true) {
205                        $phpcsFile->fixer->addContent($secondSemicolon, ' ');
206                    }
207                } else {
208                    if (strlen($tokens[($secondSemicolon + 1)]['content']) !== 1) {
209                        $spaces = strlen($tokens[($secondSemicolon + 1)]['content']);
210                        $data   = array($spaces);
211                        if (($secondSemicolon + 2) === $closingBracket) {
212                            $error = 'Expected no space after second semicolon of FOR loop; %s found';
213                            $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterSecondNoThird', $data);
214                            if ($fix === true) {
215                                $phpcsFile->fixer->replaceToken(($secondSemicolon + 1), '');
216                            }
217                        } else {
218                            $error = 'Expected 1 space after second semicolon of FOR loop; %s found';
219                            $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterSecond', $data);
220                            if ($fix === true) {
221                                $phpcsFile->fixer->replaceToken(($secondSemicolon + 1), ' ');
222                            }
223                        }
224                    }
225                }//end if
226            }//end if
227        }//end if
228
229    }//end process()
230
231
232}//end class
233