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