These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * PHPCompatibility_Sniffs_PHP_NewInterfacesSniff. |
||
| 4 | * |
||
| 5 | * @category PHP |
||
| 6 | * @package PHPCompatibility |
||
| 7 | * @author Juliette Reinders Folmer <[email protected]> |
||
| 8 | */ |
||
| 9 | |||
| 10 | /** |
||
| 11 | * PHPCompatibility_Sniffs_PHP_NewInterfacesSniff. |
||
| 12 | * |
||
| 13 | * @category PHP |
||
| 14 | * @package PHPCompatibility |
||
| 15 | * @author Juliette Reinders Folmer <[email protected]> |
||
| 16 | */ |
||
| 17 | class PHPCompatibility_Sniffs_PHP_NewInterfacesSniff extends PHPCompatibility_AbstractNewFeatureSniff |
||
| 18 | { |
||
| 19 | |||
| 20 | /** |
||
| 21 | * A list of new interfaces, not present in older versions. |
||
| 22 | * |
||
| 23 | * The array lists : version number with false (not present) or true (present). |
||
| 24 | * If's sufficient to list the first version where the interface appears. |
||
| 25 | * |
||
| 26 | * @var array(string => array(string => int|string|null)) |
||
| 27 | */ |
||
| 28 | protected $newInterfaces = array( |
||
| 29 | 'Traversable' => array( |
||
| 30 | '4.4' => false, |
||
| 31 | '5.0' => true, |
||
| 32 | ), |
||
| 33 | |||
| 34 | 'Countable' => array( |
||
| 35 | '5.0' => false, |
||
| 36 | '5.1' => true, |
||
| 37 | ), |
||
| 38 | 'OuterIterator' => array( |
||
| 39 | '5.0' => false, |
||
| 40 | '5.1' => true, |
||
| 41 | ), |
||
| 42 | 'RecursiveIterator' => array( |
||
| 43 | '5.0' => false, |
||
| 44 | '5.1' => true, |
||
| 45 | ), |
||
| 46 | 'SeekableIterator' => array( |
||
| 47 | '5.0' => false, |
||
| 48 | '5.1' => true, |
||
| 49 | ), |
||
| 50 | 'Serializable' => array( |
||
| 51 | '5.0' => false, |
||
| 52 | '5.1' => true, |
||
| 53 | ), |
||
| 54 | 'SplObserver' => array( |
||
| 55 | '5.0' => false, |
||
| 56 | '5.1' => true, |
||
| 57 | ), |
||
| 58 | 'SplSubject' => array( |
||
| 59 | '5.0' => false, |
||
| 60 | '5.1' => true, |
||
| 61 | ), |
||
| 62 | |||
| 63 | 'JsonSerializable' => array( |
||
| 64 | '5.3' => false, |
||
| 65 | '5.4' => true, |
||
| 66 | ), |
||
| 67 | 'SessionHandlerInterface' => array( |
||
| 68 | '5.3' => false, |
||
| 69 | '5.4' => true, |
||
| 70 | ), |
||
| 71 | |||
| 72 | 'DateTimeInterface' => array( |
||
| 73 | '5.4' => false, |
||
| 74 | '5.5' => true, |
||
| 75 | ), |
||
| 76 | |||
| 77 | 'Throwable' => array( |
||
| 78 | '5.6' => false, |
||
| 79 | '7.0' => true, |
||
| 80 | ), |
||
| 81 | |||
| 82 | ); |
||
| 83 | |||
| 84 | /** |
||
| 85 | * A list of methods which cannot be used in combination with particular interfaces. |
||
| 86 | * |
||
| 87 | * @var array(string => array(string => string)) |
||
| 88 | */ |
||
| 89 | protected $unsupportedMethods = array( |
||
| 90 | 'Serializable' => array( |
||
| 91 | '__sleep' => 'http://php.net/serializable', |
||
| 92 | '__wakeup' => 'http://php.net/serializable', |
||
| 93 | ), |
||
| 94 | ); |
||
| 95 | |||
| 96 | /** |
||
| 97 | * Returns an array of tokens this test wants to listen for. |
||
| 98 | * |
||
| 99 | * @return array |
||
| 100 | */ |
||
| 101 | public function register() |
||
| 102 | { |
||
| 103 | // Handle case-insensitivity of interface names. |
||
| 104 | $this->newInterfaces = $this->arrayKeysToLowercase($this->newInterfaces); |
||
| 105 | $this->unsupportedMethods = $this->arrayKeysToLowercase($this->unsupportedMethods); |
||
| 106 | |||
| 107 | $targets = array( |
||
| 108 | T_CLASS, |
||
| 109 | T_FUNCTION, |
||
| 110 | T_CLOSURE, |
||
| 111 | ); |
||
| 112 | |||
| 113 | if (defined('T_ANON_CLASS')) { |
||
| 114 | $targets[] = constant('T_ANON_CLASS'); |
||
| 115 | } |
||
| 116 | |||
| 117 | return $targets; |
||
| 118 | |||
| 119 | }//end register() |
||
| 120 | |||
| 121 | |||
| 122 | /** |
||
| 123 | * Processes this test, when one of its tokens is encountered. |
||
| 124 | * |
||
| 125 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. |
||
| 126 | * @param int $stackPtr The position of the current token in |
||
| 127 | * the stack passed in $tokens. |
||
| 128 | * |
||
| 129 | * @return void |
||
| 130 | */ |
||
| 131 | View Code Duplication | public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) |
|
|
0 ignored issues
–
show
|
|||
| 132 | { |
||
| 133 | $tokens = $phpcsFile->getTokens(); |
||
| 134 | |||
| 135 | switch ($tokens[$stackPtr]['type']) { |
||
| 136 | case 'T_CLASS': |
||
| 137 | case 'T_ANON_CLASS': |
||
| 138 | $this->processClassToken($phpcsFile, $stackPtr); |
||
| 139 | break; |
||
| 140 | |||
| 141 | case 'T_FUNCTION': |
||
| 142 | case 'T_CLOSURE': |
||
| 143 | $this->processFunctionToken($phpcsFile, $stackPtr); |
||
| 144 | break; |
||
| 145 | |||
| 146 | default: |
||
| 147 | // Deliberately left empty. |
||
| 148 | break; |
||
| 149 | } |
||
| 150 | |||
| 151 | }//end process() |
||
| 152 | |||
| 153 | |||
| 154 | /** |
||
| 155 | * Processes this test for when a class token is encountered. |
||
| 156 | * |
||
| 157 | * - Detect classes implementing the new interfaces. |
||
| 158 | * - Detect classes implementing the new interfaces with unsupported functions. |
||
| 159 | * |
||
| 160 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. |
||
| 161 | * @param int $stackPtr The position of the current token in |
||
| 162 | * the stack passed in $tokens. |
||
| 163 | * |
||
| 164 | * @return void |
||
| 165 | */ |
||
| 166 | private function processClassToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr) |
||
| 167 | { |
||
| 168 | $interfaces = $this->findImplementedInterfaceNames($phpcsFile, $stackPtr); |
||
| 169 | |||
| 170 | if (is_array($interfaces) === false || $interfaces === array()) { |
||
| 171 | return; |
||
| 172 | } |
||
| 173 | |||
| 174 | $tokens = $phpcsFile->getTokens(); |
||
| 175 | $checkMethods = false; |
||
| 176 | |||
| 177 | if (isset($tokens[$stackPtr]['scope_closer'])) { |
||
| 178 | $checkMethods = true; |
||
| 179 | $scopeCloser = $tokens[$stackPtr]['scope_closer']; |
||
| 180 | } |
||
| 181 | |||
| 182 | foreach ($interfaces as $interface) { |
||
| 183 | $interfaceLc = strtolower($interface); |
||
| 184 | |||
| 185 | if (isset($this->newInterfaces[$interfaceLc]) === true) { |
||
| 186 | $itemInfo = array( |
||
| 187 | 'name' => $interface, |
||
| 188 | 'nameLc' => $interfaceLc, |
||
| 189 | ); |
||
| 190 | $this->handleFeature($phpcsFile, $stackPtr, $itemInfo); |
||
| 191 | } |
||
| 192 | |||
| 193 | if ($checkMethods === true && isset($this->unsupportedMethods[$interfaceLc]) === true) { |
||
| 194 | $nextFunc = $stackPtr; |
||
| 195 | while (($nextFunc = $phpcsFile->findNext(T_FUNCTION, ($nextFunc + 1), $scopeCloser)) !== false) { |
||
| 196 | $funcName = $phpcsFile->getDeclarationName($nextFunc); |
||
| 197 | $funcNameLc = strtolower($funcName); |
||
| 198 | if ($funcNameLc === '') { |
||
| 199 | continue; |
||
| 200 | } |
||
| 201 | |||
| 202 | if (isset($this->unsupportedMethods[$interfaceLc][$funcNameLc]) === true) { |
||
| 203 | $error = 'Classes that implement interface %s do not support the method %s(). See %s'; |
||
| 204 | $errorCode = $this->stringToErrorCode($interface).'UnsupportedMethod'; |
||
| 205 | $data = array( |
||
| 206 | $interface, |
||
| 207 | $funcName, |
||
| 208 | $this->unsupportedMethods[$interfaceLc][$funcNameLc], |
||
| 209 | ); |
||
| 210 | |||
| 211 | $phpcsFile->addError($error, $nextFunc, $errorCode, $data); |
||
| 212 | } |
||
| 213 | } |
||
| 214 | } |
||
| 215 | } |
||
| 216 | }//end processClassToken() |
||
| 217 | |||
| 218 | |||
| 219 | /** |
||
| 220 | * Processes this test for when a function token is encountered. |
||
| 221 | * |
||
| 222 | * - Detect new interfaces when used as a type hint. |
||
| 223 | * |
||
| 224 | * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. |
||
| 225 | * @param int $stackPtr The position of the current token in |
||
| 226 | * the stack passed in $tokens. |
||
| 227 | * |
||
| 228 | * @return void |
||
| 229 | */ |
||
| 230 | View Code Duplication | private function processFunctionToken(PHP_CodeSniffer_File $phpcsFile, $stackPtr) |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 231 | { |
||
| 232 | $typeHints = $this->getTypeHintsFromFunctionDeclaration($phpcsFile, $stackPtr); |
||
| 233 | if (empty($typeHints) || is_array($typeHints) === false) { |
||
| 234 | return; |
||
| 235 | } |
||
| 236 | |||
| 237 | foreach ($typeHints as $hint) { |
||
| 238 | |||
| 239 | $typeHintLc = strtolower($hint); |
||
| 240 | |||
| 241 | if (isset($this->newInterfaces[$typeHintLc]) === true) { |
||
| 242 | $itemInfo = array( |
||
| 243 | 'name' => $hint, |
||
| 244 | 'nameLc' => $typeHintLc, |
||
| 245 | ); |
||
| 246 | $this->handleFeature($phpcsFile, $stackPtr, $itemInfo); |
||
| 247 | } |
||
| 248 | } |
||
| 249 | } |
||
| 250 | |||
| 251 | |||
| 252 | /** |
||
| 253 | * Get the relevant sub-array for a specific item from a multi-dimensional array. |
||
| 254 | * |
||
| 255 | * @param array $itemInfo Base information about the item. |
||
| 256 | * |
||
| 257 | * @return array Version and other information about the item. |
||
| 258 | */ |
||
| 259 | public function getItemArray(array $itemInfo) |
||
| 260 | { |
||
| 261 | return $this->newInterfaces[$itemInfo['nameLc']]; |
||
| 262 | } |
||
| 263 | |||
| 264 | |||
| 265 | /** |
||
| 266 | * Get the error message template for this sniff. |
||
| 267 | * |
||
| 268 | * @return string |
||
| 269 | */ |
||
| 270 | protected function getErrorMsgTemplate() |
||
| 271 | { |
||
| 272 | return 'The built-in interface '.parent::getErrorMsgTemplate(); |
||
| 273 | } |
||
| 274 | |||
| 275 | |||
| 276 | }//end class |
||
| 277 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.