Completed
Push — master ( a32632...c637b7 )
by Juliette
9s
created

PHPCompatibility_Sniffs_PHP_NewInterfacesSniff::addError()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
rs 8.439
c 0
b 0
f 0
cc 6
eloc 15
nc 12
nop 3

1 Method

Rating   Name   Duplication   Size   Complexity  
A PHPCompatibility_Sniffs_PHP_NewInterfacesSniff::getErrorMsgTemplate() 0 4 1
1
<?php
2
/**
3
 * PHPCompatibility_Sniffs_PHP_NewInterfacesSniff.
4
 *
5
 * PHP version 5.5
6
 *
7
 * @category  PHP
8
 * @package   PHPCompatibility
9
 * @author    Juliette Reinders Folmer <[email protected]>
10
 */
11
12
/**
13
 * PHPCompatibility_Sniffs_PHP_NewInterfacesSniff.
14
 *
15
 * @category  PHP
16
 * @package   PHPCompatibility
17
 * @author    Juliette Reinders Folmer <[email protected]>
18
 */
19
class PHPCompatibility_Sniffs_PHP_NewInterfacesSniff extends PHPCompatibility_AbstractNewFeatureSniff
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...
20
{
21
22
    /**
23
     * A list of new interfaces, not present in older versions.
24
     *
25
     * The array lists : version number with false (not present) or true (present).
26
     * If's sufficient to list the first version where the interface appears.
27
     *
28
     * @var array(string => array(string => int|string|null))
29
     */
30
    protected $newInterfaces = array(
31
                                'Countable' => array(
32
                                    '5.0' => false,
33
                                    '5.1' => true
34
                                ),
35
                                'OuterIterator' => array(
36
                                    '5.0' => false,
37
                                    '5.1' => true
38
                                ),
39
                                'RecursiveIterator' => array(
40
                                    '5.0' => false,
41
                                    '5.1' => true
42
                                ),
43
                                'SeekableIterator' => array(
44
                                    '5.0' => false,
45
                                    '5.1' => true
46
                                ),
47
                                'Serializable' => array(
48
                                    '5.0' => false,
49
                                    '5.1' => true,
50
                                ),
51
                                'SplObserver' => array(
52
                                    '5.0' => false,
53
                                    '5.1' => true
54
                                ),
55
                                'SplSubject' => array(
56
                                    '5.0' => false,
57
                                    '5.1' => true
58
                                ),
59
60
                                'JsonSerializable' => array(
61
                                    '5.3' => false,
62
                                    '5.4' => true
63
                                ),
64
                                'SessionHandlerInterface' => array(
65
                                    '5.3' => false,
66
                                    '5.4' => true
67
                                ),
68
69
                               );
70
71
    /**
72
     * A list of methods which cannot be used in combination with particular interfaces.
73
     *
74
     * @var array(string => array(string => string))
75
     */
76
    protected $unsupportedMethods = array(
77
                                     'Serializable' => array(
78
                                         '__sleep'  => 'http://php.net/serializable',
79
                                         '__wakeup' => 'http://php.net/serializable',
80
                                     ),
81
                                    );
82
83
    /**
84
     * Returns an array of tokens this test wants to listen for.
85
     *
86
     * @return array
87
     */
88
    public function register()
89
    {
90
        // Handle case-insensitivity of interface names.
91
        $this->newInterfaces      = $this->arrayKeysToLowercase($this->newInterfaces);
92
        $this->unsupportedMethods = $this->arrayKeysToLowercase($this->unsupportedMethods);
93
94
        return array(T_CLASS);
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
        $interfaces = $this->findImplementedInterfaceNames($phpcsFile, $stackPtr);
111
112
        if (is_array($interfaces) === false || $interfaces === array()) {
113
            return;
114
        }
115
116
        $tokens       = $phpcsFile->getTokens();
117
        $checkMethods = false;
118
119
        if(isset($tokens[$stackPtr]['scope_closer'])) {
120
            $checkMethods = true;
121
            $scopeCloser = $tokens[$stackPtr]['scope_closer'];
122
        }
123
124
        foreach ($interfaces as $interface) {
125
            $interfaceLc = strtolower($interface);
126
127
            if (isset($this->newInterfaces[$interfaceLc]) === true) {
128
                $itemInfo = array(
129
                    'name'   => $interface,
130
                    'nameLc' => $interfaceLc,
131
                );
132
                $this->handleFeature($phpcsFile, $stackPtr, $itemInfo);
133
            }
134
135
            if ($checkMethods === true && isset($this->unsupportedMethods[$interfaceLc]) === true) {
136
                $nextFunc = $stackPtr;
137
                while (($nextFunc = $phpcsFile->findNext(T_FUNCTION, ($nextFunc + 1), $scopeCloser)) !== false) {
0 ignored issues
show
Bug introduced by
The variable $scopeCloser does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
138
                    $funcName   = $phpcsFile->getDeclarationName($nextFunc);
139
                    $funcNameLc = strtolower($funcName);
140
                    if ($funcNameLc === '') {
141
                        continue;
142
                    }
143
144
                    if (isset($this->unsupportedMethods[$interfaceLc][$funcNameLc]) === true) {
145
                        $error     = 'Classes that implement interface %s do not support the method %s(). See %s';
146
                        $errorCode = $this->stringToErrorCode($interface).'UnsupportedMethod';
147
                        $data      = array(
148
                            $interface,
149
                            $funcName,
150
                            $this->unsupportedMethods[$interfaceLc][$funcNameLc],
151
                        );
152
153
                        $phpcsFile->addError($error, $nextFunc, $errorCode, $data);
154
                    }
155
                }
156
            }
157
        }
158
159
    }//end process()
160
161
162
    /**
163
     * Get the relevant sub-array for a specific item from a multi-dimensional array.
164
     *
165
     * @param array $itemInfo Base information about the item.
166
     *
167
     * @return array Version and other information about the item.
168
     */
169
    public function getItemArray(array $itemInfo)
170
    {
171
        return $this->newInterfaces[$itemInfo['nameLc']];
172
    }
173
174
175
    /**
176
     * Get the error message template for this sniff.
177
     *
178
     * @return string
179
     */
180
    protected function getErrorMsgTemplate()
181
    {
182
        return 'The built-in interface '.parent::getErrorMsgTemplate();
183
    }
184
185
186
}//end class
187