1<?php
2/**
3 * Squiz_Sniffs_ControlStructures_InlineControlStructureSniff.
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_InlineIfDeclarationSniff.
18 *
19 * Tests the spacing of shorthand IF statements.
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_InlineIfDeclarationSniff 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_INLINE_THEN);
42
43    }//end register()
44
45
46    /**
47     * Processes this sniff, 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
59        $openBracket  = null;
60        $closeBracket = null;
61        if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
62            $parens       = $tokens[$stackPtr]['nested_parenthesis'];
63            $openBracket  = array_pop($parens);
64            $closeBracket = $tokens[$openBracket]['parenthesis_closer'];
65        }
66
67        // Find the beginning of the statement. If we don't find a
68        // semicolon (end of statement) or comma (end of array value)
69        // then assume the content before the closing parenthesis is the end.
70        $else         = $phpcsFile->findNext(T_INLINE_ELSE, ($stackPtr + 1));
71        $statementEnd = $phpcsFile->findNext(array(T_SEMICOLON, T_COMMA), ($else + 1), $closeBracket);
72        if ($statementEnd === false) {
73            $statementEnd = $phpcsFile->findPrevious(T_WHITESPACE, ($closeBracket - 1), null, true);
74        }
75
76        // Make sure it's all on the same line.
77        if ($tokens[$statementEnd]['line'] !== $tokens[$stackPtr]['line']) {
78            $error = 'Inline shorthand IF statement must be declared on a single line';
79            $phpcsFile->addError($error, $stackPtr, 'NotSingleLine');
80            return;
81        }
82
83        // Make sure there are spaces around the question mark.
84        $contentBefore = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
85        $contentAfter  = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
86        if ($tokens[$contentBefore]['code'] !== T_CLOSE_PARENTHESIS) {
87            $error = 'Inline shorthand IF statement requires brackets around comparison';
88            $phpcsFile->addError($error, $stackPtr, 'NoBrackets');
89        }
90
91        $spaceBefore = ($tokens[$stackPtr]['column'] - ($tokens[$contentBefore]['column'] + $tokens[$contentBefore]['length']));
92        if ($spaceBefore !== 1) {
93            $error = 'Inline shorthand IF statement requires 1 space before THEN; %s found';
94            $data  = array($spaceBefore);
95            $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingBeforeThen', $data);
96            if ($fix === true) {
97                if ($spaceBefore === 0) {
98                    $phpcsFile->fixer->addContentBefore($stackPtr, ' ');
99                } else {
100                    $phpcsFile->fixer->replaceToken(($stackPtr - 1), ' ');
101                }
102            }
103        }
104
105        // If there is no content between the ? and the : operators, then they are
106        // trying to replicate an elvis operator, even though PHP doesn't have one.
107        // In this case, we want no spaces between the two operators so ?: looks like
108        // an operator itself.
109        $next = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true);
110        if ($tokens[$next]['code'] === T_INLINE_ELSE) {
111            $inlineElse = $next;
112            if ($inlineElse !== ($stackPtr + 1)) {
113                $error = 'Inline shorthand IF statement without THEN statement requires 0 spaces between THEN and ELSE';
114                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'ElvisSpacing');
115                if ($fix === true) {
116                    $phpcsFile->fixer->replaceToken(($stackPtr + 1), '');
117                }
118            }
119        } else {
120            $spaceAfter = (($tokens[$contentAfter]['column']) - ($tokens[$stackPtr]['column'] + 1));
121            if ($spaceAfter !== 1) {
122                $error = 'Inline shorthand IF statement requires 1 space after THEN; %s found';
123                $data  = array($spaceAfter);
124                $fix   = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterThen', $data);
125                if ($spaceAfter === 0) {
126                    $phpcsFile->fixer->addContent($stackPtr, ' ');
127                } else {
128                    $phpcsFile->fixer->replaceToken(($stackPtr + 1), ' ');
129                }
130            }
131
132            // Make sure the ELSE has the correct spacing.
133            $inlineElse    = $phpcsFile->findNext(T_INLINE_ELSE, ($stackPtr + 1), $statementEnd, false);
134            $contentBefore = $phpcsFile->findPrevious(T_WHITESPACE, ($inlineElse - 1), null, true);
135            $spaceBefore   = ($tokens[$inlineElse]['column'] - ($tokens[$contentBefore]['column'] + $tokens[$contentBefore]['length']));
136            if ($spaceBefore !== 1) {
137                $error = 'Inline shorthand IF statement requires 1 space before ELSE; %s found';
138                $data  = array($spaceBefore);
139                $fix   = $phpcsFile->addFixableError($error, $inlineElse, 'SpacingBeforeElse', $data);
140                if ($fix === true) {
141                    if ($spaceBefore === 0) {
142                        $phpcsFile->fixer->addContentBefore($inlineElse, ' ');
143                    } else {
144                        $phpcsFile->fixer->replaceToken(($inlineElse - 1), ' ');
145                    }
146                }
147            }
148        }//end if
149
150        $contentAfter = $phpcsFile->findNext(T_WHITESPACE, ($inlineElse + 1), null, true);
151        $spaceAfter   = (($tokens[$contentAfter]['column']) - ($tokens[$inlineElse]['column'] + 1));
152        if ($spaceAfter !== 1) {
153            $error = 'Inline shorthand IF statement requires 1 space after ELSE; %s found';
154            $data  = array($spaceAfter);
155            $fix   = $phpcsFile->addFixableError($error, $inlineElse, 'SpacingAfterElse', $data);
156            if ($spaceAfter === 0) {
157                $phpcsFile->fixer->addContent($inlineElse, ' ');
158            } else {
159                $phpcsFile->fixer->replaceToken(($inlineElse + 1), ' ');
160            }
161        }
162
163    }//end process()
164
165
166}//end class
167