1<?php 2/** 3 * Squiz_Sniffs_Classes_ClassFileNameSniff. 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 16if (class_exists('PHP_CodeSniffer_Standards_AbstractScopeSniff', true) === false) { 17 $error = 'Class PHP_CodeSniffer_Standards_AbstractScopeSniff not found'; 18 throw new PHP_CodeSniffer_Exception($error); 19} 20 21/** 22 * Tests self member references. 23 * 24 * Verifies that : 25 * <ul> 26 * <li>self:: is used instead of Self::</li> 27 * <li>self:: is used for local static member reference</li> 28 * <li>self:: is used instead of self ::</li> 29 * </ul> 30 * 31 * @category PHP 32 * @package PHP_CodeSniffer 33 * @author Greg Sherwood <gsherwood@squiz.net> 34 * @author Marc McIntyre <mmcintyre@squiz.net> 35 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 36 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 37 * @version Release: @package_version@ 38 * @link http://pear.php.net/package/PHP_CodeSniffer 39 */ 40class Squiz_Sniffs_Classes_SelfMemberReferenceSniff extends PHP_CodeSniffer_Standards_AbstractScopeSniff 41{ 42 43 44 /** 45 * Constructs a Squiz_Sniffs_Classes_SelfMemberReferenceSniff. 46 */ 47 public function __construct() 48 { 49 parent::__construct(array(T_CLASS), array(T_DOUBLE_COLON)); 50 51 }//end __construct() 52 53 54 /** 55 * Processes the function tokens within the class. 56 * 57 * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found. 58 * @param int $stackPtr The position where the token was found. 59 * @param int $currScope The current scope opener token. 60 * 61 * @return void 62 */ 63 protected function processTokenWithinScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr, $currScope) 64 { 65 $tokens = $phpcsFile->getTokens(); 66 67 $calledClassName = ($stackPtr - 1); 68 if ($tokens[$calledClassName]['code'] === T_SELF) { 69 if ($tokens[$calledClassName]['content'] !== 'self') { 70 $error = 'Must use "self::" for local static member reference; found "%s::"'; 71 $data = array($tokens[$calledClassName]['content']); 72 $fix = $phpcsFile->addFixableError($error, $calledClassName, 'IncorrectCase', $data); 73 if ($fix === true) { 74 $phpcsFile->fixer->replaceToken($calledClassName, 'self'); 75 } 76 77 return; 78 } 79 } else if ($tokens[$calledClassName]['code'] === T_STRING) { 80 // If the class is called with a namespace prefix, build fully qualified 81 // namespace calls for both current scope class and requested class. 82 if ($tokens[($calledClassName - 1)]['code'] === T_NS_SEPARATOR) { 83 $declarationName = $this->getDeclarationNameWithNamespace($tokens, $calledClassName); 84 $declarationName = substr($declarationName, 1); 85 $fullQualifiedClassName = $this->getNamespaceOfScope($phpcsFile, $currScope); 86 if ($fullQualifiedClassName === '\\') { 87 $fullQualifiedClassName = ''; 88 } else { 89 $fullQualifiedClassName .= '\\'; 90 } 91 92 $fullQualifiedClassName .= $phpcsFile->getDeclarationName($currScope); 93 } else { 94 $declarationName = $phpcsFile->getDeclarationName($currScope); 95 $fullQualifiedClassName = $tokens[$calledClassName]['content']; 96 } 97 98 if ($declarationName === $fullQualifiedClassName) { 99 // Class name is the same as the current class, which is not allowed 100 // except if being used inside a closure. 101 if ($phpcsFile->hasCondition($stackPtr, T_CLOSURE) === false) { 102 $error = 'Must use "self::" for local static member reference'; 103 $fix = $phpcsFile->addFixableError($error, $calledClassName, 'NotUsed'); 104 105 if ($fix === true) { 106 $prev = $phpcsFile->findPrevious(array(T_NS_SEPARATOR, T_STRING), ($stackPtr - 1), null, true); 107 $phpcsFile->fixer->beginChangeset(); 108 for ($i = ($prev + 1); $i < $stackPtr; $i++) { 109 $phpcsFile->fixer->replaceToken($i, ''); 110 } 111 112 $phpcsFile->fixer->replaceToken($stackPtr, 'self::'); 113 $phpcsFile->fixer->endChangeset(); 114 } 115 116 return; 117 } 118 }//end if 119 }//end if 120 121 if ($tokens[($stackPtr - 1)]['code'] === T_WHITESPACE) { 122 $found = strlen($tokens[($stackPtr - 1)]['content']); 123 $error = 'Expected 0 spaces before double colon; %s found'; 124 $data = array($found); 125 $fix = $phpcsFile->addFixableError($error, $calledClassName, 'SpaceBefore', $data); 126 127 if ($fix === true) { 128 $phpcsFile->fixer->replaceToken(($stackPtr - 1), ''); 129 } 130 } 131 132 if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) { 133 $found = strlen($tokens[($stackPtr + 1)]['content']); 134 $error = 'Expected 0 spaces after double colon; %s found'; 135 $data = array($found); 136 $fix = $phpcsFile->addFixableError($error, $calledClassName, 'SpaceAfter', $data); 137 138 if ($fix === true) { 139 $phpcsFile->fixer->replaceToken(($stackPtr + 1), ''); 140 } 141 } 142 143 }//end processTokenWithinScope() 144 145 146 /** 147 * Returns the declaration names for classes/interfaces/functions with a namespace. 148 * 149 * @param array $tokens Token stack for this file 150 * @param int $stackPtr The position where the namespace building will start. 151 * 152 * @return string 153 */ 154 protected function getDeclarationNameWithNamespace(array $tokens, $stackPtr) 155 { 156 $nameParts = array(); 157 $currentPointer = $stackPtr; 158 while ($tokens[$currentPointer]['code'] === T_NS_SEPARATOR 159 || $tokens[$currentPointer]['code'] === T_STRING 160 ) { 161 $nameParts[] = $tokens[$currentPointer]['content']; 162 $currentPointer--; 163 } 164 165 $nameParts = array_reverse($nameParts); 166 return implode('', $nameParts); 167 168 }//end getDeclarationNameWithNamespace() 169 170 171 /** 172 * Returns the namespace declaration of a file. 173 * 174 * @param PHP_CodeSniffer_File $phpcsFile The file where this token was found. 175 * @param int $stackPtr The position where the search for the 176 * namespace declaration will start. 177 * 178 * @return string 179 */ 180 protected function getNamespaceOfScope(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 181 { 182 $namespace = '\\'; 183 $namespaceDeclaration = $phpcsFile->findPrevious(T_NAMESPACE, $stackPtr); 184 185 if ($namespaceDeclaration !== false) { 186 $endOfNamespaceDeclaration = $phpcsFile->findNext(T_SEMICOLON, $namespaceDeclaration); 187 $namespace = $this->getDeclarationNameWithNamespace( 188 $phpcsFile->getTokens(), 189 ($endOfNamespaceDeclaration - 1) 190 ); 191 } 192 193 return $namespace; 194 195 }//end getNamespaceOfScope() 196 197 198}//end class 199