1<?php 2/** 3 * Ensures that self and static are not used to call public methods in action classes. 4 * 5 * PHP version 5 6 * 7 * @category PHP 8 * @package PHP_CodeSniffer_MySource 9 * @author Greg Sherwood <gsherwood@squiz.net> 10 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 11 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 12 * @link http://pear.php.net/package/PHP_CodeSniffer 13 */ 14 15/** 16 * Ensures that self and static are not used to call public methods in action classes. 17 * 18 * @category PHP 19 * @package PHP_CodeSniffer_MySource 20 * @author Greg Sherwood <gsherwood@squiz.net> 21 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 22 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 23 * @version Release: @package_version@ 24 * @link http://pear.php.net/package/PHP_CodeSniffer 25 */ 26class MySource_Sniffs_Channels_DisallowSelfActionsSniff implements PHP_CodeSniffer_Sniff 27{ 28 29 30 /** 31 * Returns an array of tokens this test wants to listen for. 32 * 33 * @return array 34 */ 35 public function register() 36 { 37 return array(T_CLASS); 38 39 }//end register() 40 41 42 /** 43 * Processes this sniff, when one of its tokens is encountered. 44 * 45 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 46 * @param int $stackPtr The position of the current token in 47 * the stack passed in $tokens. 48 * 49 * @return void 50 */ 51 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 52 { 53 $tokens = $phpcsFile->getTokens(); 54 55 // We are not interested in abstract classes. 56 $prev = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true); 57 if ($prev !== false && $tokens[$prev]['code'] === T_ABSTRACT) { 58 return; 59 } 60 61 // We are only interested in Action classes. 62 $classNameToken = $phpcsFile->findNext(T_WHITESPACE, ($stackPtr + 1), null, true); 63 $className = $tokens[$classNameToken]['content']; 64 if (substr($className, -7) !== 'Actions') { 65 return; 66 } 67 68 $foundFunctions = array(); 69 $foundCalls = array(); 70 71 // Find all static method calls in the form self::method() in the class. 72 $classEnd = $tokens[$stackPtr]['scope_closer']; 73 for ($i = ($classNameToken + 1); $i < $classEnd; $i++) { 74 if ($tokens[$i]['code'] !== T_DOUBLE_COLON) { 75 if ($tokens[$i]['code'] === T_FUNCTION) { 76 // Cache the function information. 77 $funcName = $phpcsFile->findNext(T_STRING, ($i + 1)); 78 $funcScope = $phpcsFile->findPrevious(PHP_CodeSniffer_Tokens::$scopeModifiers, ($i - 1)); 79 80 $foundFunctions[$tokens[$funcName]['content']] = strtolower($tokens[$funcScope]['content']); 81 } 82 83 continue; 84 } 85 86 $prevToken = $phpcsFile->findPrevious(T_WHITESPACE, ($i - 1), null, true); 87 if ($tokens[$prevToken]['content'] !== 'self' 88 && $tokens[$prevToken]['content'] !== 'static' 89 ) { 90 continue; 91 } 92 93 $funcNameToken = $phpcsFile->findNext(T_WHITESPACE, ($i + 1), null, true); 94 if ($tokens[$funcNameToken]['code'] === T_VARIABLE) { 95 // We are only interested in function calls. 96 continue; 97 } 98 99 $funcName = $tokens[$funcNameToken]['content']; 100 101 // We've found the function, now we need to find it and see if it is 102 // public, private or protected. If it starts with an underscore we 103 // can assume it is private. 104 if ($funcName{0} === '_') { 105 continue; 106 } 107 108 $foundCalls[$i] = array( 109 'name' => $funcName, 110 'type' => strtolower($tokens[$prevToken]['content']), 111 ); 112 }//end for 113 114 $errorClassName = substr($className, 0, -7); 115 116 foreach ($foundCalls as $token => $funcData) { 117 if (isset($foundFunctions[$funcData['name']]) === false) { 118 // Function was not in this class, might have come from the parent. 119 // Either way, we can't really check this. 120 continue; 121 } else if ($foundFunctions[$funcData['name']] === 'public') { 122 $type = $funcData['type']; 123 $error = "Static calls to public methods in Action classes must not use the $type keyword; use %s::%s() instead"; 124 $data = array( 125 $errorClassName, 126 $funcName, 127 ); 128 $phpcsFile->addError($error, $token, 'Found'.ucfirst($funcData['type']), $data); 129 } 130 } 131 132 }//end process() 133 134 135}//end class 136