Completed
Pull Request — master (#391)
by Juliette
02:23
created

NewHeredocInitializeSniff   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 148
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 3
dl 0
loc 148
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A register() 0 4 1
D process() 0 84 17
B throwError() 0 29 4
1
<?php
2
/**
3
 * PHPCompatibility_Sniffs_PHP_NewHeredocInitializeSniff.
4
 *
5
 * PHP version 5.3
6
 *
7
 * @category PHP
8
 * @package  PHPCompatibility
9
 * @author   Juliette Reinders Folmer <[email protected]>
10
 */
11
12
/**
13
 * PHPCompatibility_Sniffs_PHP_NewHeredocInitializeSniff.
14
 *
15
 * As of PHP 5.3.0, it's possible to initialize static variables and class
16
 * properties/constants using the Heredoc syntax.
17
 *
18
 * PHP version 5.3
19
 *
20
 * @category PHP
21
 * @package  PHPCompatibility
22
 * @author   Juliette Reinders Folmer <[email protected]>
23
 */
24
class PHPCompatibility_Sniffs_PHP_NewHeredocInitializeSniff extends PHPCompatibility_Sniff
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
25
{
26
27
    /**
28
     * Returns an array of tokens this test wants to listen for.
29
     *
30
     * @return array
31
     */
32
    public function register()
33
    {
34
        return array(T_START_HEREDOC);
35
    }
36
37
    /**
38
     * Processes this test, when one of its tokens is encountered.
39
     *
40
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
41
     * @param int                  $stackPtr  The position of the current token in the
42
     *                                        stack passed in $tokens.
43
     *
44
     * @return void
45
     */
46
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
47
    {
48
        if ($this->supportsBelow('5.2') !== true) {
49
            return;
50
        }
51
52
        $tokens = $phpcsFile->getTokens();
53
54
        $equalSign = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr - 1), null, true, null, true);
55
        if ($equalSign === false || $tokens[$equalSign]['code'] !== T_EQUAL) {
56
            // Not an assignment.
57
            return;
58
        }
59
60
        $prevNonEmpty = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($equalSign - 1), null, true, null, true);
61
        if ($prevNonEmpty === false ||
62
            ($tokens[$prevNonEmpty]['code'] !== T_VARIABLE && $tokens[$prevNonEmpty]['code'] !== T_STRING)
63
        ) {
64
            // Not a variable or constant assignment.
65
            return;
66
        }
67
68
        if ($this->inClassScope($phpcsFile, $stackPtr, false) === true) {
69
70
            switch ($tokens[$prevNonEmpty]['type']) {
71
72
                /*
73
                 * Check class constant assignments.
74
                 */
75
                case 'T_STRING':
76
                    // Walk back to check for the const keyword.
77
                    $constPtr = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($prevNonEmpty - 1), null, true, null, true);
78
                    if ($constPtr === false || $tokens[$constPtr]['code'] !== T_CONST) {
79
                        // Not a constant assignment.
80
                        return;
81
                    }
82
83
                    if ($phpcsFile->hasCondition($stackPtr, array(T_FUNCTION, T_CLOSURE)) === true) {
84
                        // Constant within a function, i.e. not a class constant.
85
                        return;
86
                    }
87
88
                    $this->throwError($phpcsFile, $stackPtr, 'const');
89
                    return;
90
91
92
                /*
93
                 * Check class property assignments.
94
                 */
95
                case 'T_VARIABLE':
96
                    $properties = array();
97
                    try {
98
                        $properties = $phpcsFile->getMemberProperties($prevNonEmpty);
0 ignored issues
show
Bug introduced by
It seems like $prevNonEmpty defined by $phpcsFile->findPrevious...null, true, null, true) on line 60 can also be of type boolean; however, PHP_CodeSniffer_File::getMemberProperties() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
99
                    } catch ( PHP_CodeSniffer_Exception $e) {
100
                        // If we get an exception, this is not a class property.
101
                        return;
102
                    }
103
104
                    if (isset($properties['scope']) === false) {
105
                        // Not a class property.
106
                        return;
107
                    }
108
109
                    $this->throwError($phpcsFile, $stackPtr, 'property');
110
                    break;
111
            }
112
        }
113
114
115
        /*
116
         * Check static variable assignments.
117
         */
118
119
        // Walk back to check this is a static variable `static $var =`.
120
        $staticPtr = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$emptyTokens, ($prevNonEmpty - 1), null, true, null, true);
121
        if ($staticPtr === false || $tokens[$staticPtr]['code'] !== T_STATIC) {
122
            // Not a static variable assignment.
123
            return;
124
        }
125
126
        // Still here ? Then we have a static variable assignment
127
        $this->throwError($phpcsFile, $stackPtr, 'staticvar');
128
129
    }
130
131
132
    /**
133
     * Throw an error if a non-static value is found.
134
     *
135
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
136
     * @param int                  $stackPtr  The position of the token to link the error to.
137
     * @param string               $type      Type of usage found.
138
     *
139
     * @return void
140
     */
141
    protected function throwError(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $type)
142
    {
143
        switch($type) {
144
            case 'const':
145
                $phrase = 'class constants';
146
                break;
147
148
            case 'property':
149
                $phrase = 'class properties';
150
                break;
151
152
            case 'staticvar':
153
                $phrase = 'static variables';
154
                break;
155
156
            default:
157
                $phrase = '';
158
                break;
159
        }
160
161
        $errorCode = $this->stringToErrorCode($type) . 'Found';
162
163
        $phpcsFile->addError(
164
            'Initializing %s using the Heredoc syntax was not supported in PHP 5.2 or earlier',
165
            $stackPtr,
166
            $errorCode,
167
            array($phrase)
168
        );
169
    }
170
171
}
172