Completed
Pull Request — master (#231)
by Juliette
02:30
created

process()   C

Complexity

Conditions 11
Paths 23

Size

Total Lines 44
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 44
rs 5.2653
c 0
b 0
f 0
cc 11
eloc 26
nc 23
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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_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...
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 class names.
91
        $keys = array_keys( $this->newInterfaces );
92
        $keys = array_map( 'strtolower', $keys );
93
        $this->newInterfaces = array_combine( $keys, $this->newInterfaces );
94
95
        $keys = array_keys( $this->unsupportedMethods );
96
        $keys = array_map( 'strtolower', $keys );
97
        $this->unsupportedMethods = array_combine( $keys, $this->unsupportedMethods );
98
99
        return array(T_CLASS);
100
101
    }//end register()
102
103
104
    /**
105
     * Processes this test, when one of its tokens is encountered.
106
     *
107
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
108
     * @param int                  $stackPtr  The position of the current token in
109
     *                                        the stack passed in $tokens.
110
     *
111
     * @return void
112
     */
113
    public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
114
    {
115
        $interfaces = $this->findImplementedInterfaceNames($phpcsFile, $stackPtr);
116
117
        if (is_array($interfaces) === false || $interfaces === array()) {
118
            return;
119
        }
120
121
        $tokens       = $phpcsFile->getTokens();
122
        $checkMethods = false;
123
124
        if(isset($tokens[$stackPtr]['scope_closer'])) {
125
            $checkMethods = true;
126
            $scopeCloser = $tokens[$stackPtr]['scope_closer'];
127
        }
128
129
        foreach ($interfaces as $interface) {
130
            $lcInterface = strtolower($interface);
131
            if (isset($this->newInterfaces[$lcInterface]) === true) {
132
                $this->addError($phpcsFile, $stackPtr, $interface);
133
            }
134
135
            if ($checkMethods === true && isset($this->unsupportedMethods[$lcInterface]) === 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 = strtolower($phpcsFile->getDeclarationName($nextFunc));
139
                    if (is_string($funcName) === false) {
140
                        continue;
141
                    }
142
143
                    if (isset($this->unsupportedMethods[$lcInterface][$funcName]) === true) {
144
                        $error = 'Classes that implement interface %s do not support the method %s(). See %s';
145
                        $data  = array(
146
                            $interface,
147
                            $funcName,
148
                            $this->unsupportedMethods[$lcInterface][$funcName],
149
                        );
150
                        $phpcsFile->addError($error, $nextFunc, 'UnsupportedMethod', $data);
151
                    }
152
                }
153
            }
154
        }
155
156
    }//end process()
157
158
159
    /**
160
     * Generates the error or warning for this sniff.
161
     *
162
     * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
163
     * @param int                  $stackPtr  The position of the function
164
     *                                        in the token array.
165
     * @param string               $interface The name of the interface.
166
     * @param string               $pattern   The pattern used for the match.
167
     *
168
     * @return void
169
     */
170
    protected function addError($phpcsFile, $stackPtr, $interface, $pattern=null)
171
    {
172
        if ($pattern === null) {
173
            $pattern = strtolower($interface);
174
        }
175
176
        $error = '';
177
178
        $isError = false;
179
        foreach ($this->newInterfaces[$pattern] as $version => $present) {
180
            if ($this->supportsBelow($version)) {
181
                if ($present === false) {
182
                    $isError = true;
183
                    $error .= 'not present in PHP version ' . $version . ' or earlier';
184
                }
185
            }
186
        }
187
188
        if (strlen($error) > 0) {
189
            $error = 'The built-in interface ' . $interface . ' is ' . $error;
190
191
            if ($isError === true) {
192
                $phpcsFile->addError($error, $stackPtr);
193
            } else {
194
                $phpcsFile->addWarning($error, $stackPtr);
195
            }
196
        }
197
198
    }//end addError()
199
200
}//end class
201