1<?php 2/** 3 * Generic_Sniffs_PHP_DisallowShortOpenTagSniff. 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 16/** 17 * Generic_Sniffs_PHP_DisallowShortOpenTagSniff. 18 * 19 * Makes sure that shorthand PHP open tags are not used. 20 * 21 * @category PHP 22 * @package PHP_CodeSniffer 23 * @author Greg Sherwood <gsherwood@squiz.net> 24 * @author Marc McIntyre <mmcintyre@squiz.net> 25 * @copyright 2006-2014 Squiz Pty Ltd (ABN 77 084 670 600) 26 * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence 27 * @version Release: @package_version@ 28 * @link http://pear.php.net/package/PHP_CodeSniffer 29 */ 30class Generic_Sniffs_PHP_DisallowShortOpenTagSniff implements PHP_CodeSniffer_Sniff 31{ 32 33 34 /** 35 * Returns an array of tokens this test wants to listen for. 36 * 37 * @return array 38 */ 39 public function register() 40 { 41 $targets = array( 42 T_OPEN_TAG, 43 T_OPEN_TAG_WITH_ECHO, 44 ); 45 46 $shortOpenTags = (boolean) ini_get('short_open_tag'); 47 if ($shortOpenTags === false) { 48 $targets[] = T_INLINE_HTML; 49 } 50 51 return $targets; 52 53 }//end register() 54 55 56 /** 57 * Processes this test, when one of its tokens is encountered. 58 * 59 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned. 60 * @param int $stackPtr The position of the current token 61 * in the stack passed in $tokens. 62 * 63 * @return void 64 */ 65 public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) 66 { 67 $tokens = $phpcsFile->getTokens(); 68 $token = $tokens[$stackPtr]; 69 70 if ($token['code'] === T_OPEN_TAG && $token['content'] === '<?') { 71 $error = 'Short PHP opening tag used; expected "<?php" but found "%s"'; 72 $data = array($token['content']); 73 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'Found', $data); 74 if ($fix === true) { 75 $correctOpening = '<?php'; 76 if (isset($tokens[($stackPtr + 1)]) === true && $tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { 77 // Avoid creation of invalid open tags like <?phpecho if the original was <?echo . 78 $correctOpening .= ' '; 79 } 80 81 $phpcsFile->fixer->replaceToken($stackPtr, $correctOpening); 82 } 83 84 $phpcsFile->recordMetric($stackPtr, 'PHP short open tag used', 'yes'); 85 } else { 86 $phpcsFile->recordMetric($stackPtr, 'PHP short open tag used', 'no'); 87 } 88 89 if ($token['code'] === T_OPEN_TAG_WITH_ECHO) { 90 $nextVar = $tokens[$phpcsFile->findNext(PHP_CodeSniffer_Tokens::$emptyTokens, ($stackPtr + 1), null, true)]; 91 $error = 'Short PHP opening tag used with echo; expected "<?php echo %s ..." but found "%s %s ..."'; 92 $data = array( 93 $nextVar['content'], 94 $token['content'], 95 $nextVar['content'], 96 ); 97 $fix = $phpcsFile->addFixableError($error, $stackPtr, 'EchoFound', $data); 98 if ($fix === true) { 99 if ($tokens[($stackPtr + 1)]['code'] !== T_WHITESPACE) { 100 $phpcsFile->fixer->replaceToken($stackPtr, '<?php echo '); 101 } else { 102 $phpcsFile->fixer->replaceToken($stackPtr, '<?php echo'); 103 } 104 } 105 } 106 107 if ($token['code'] === T_INLINE_HTML) { 108 $content = $token['content']; 109 $openerFound = strpos($content, '<?'); 110 111 if ($openerFound === false) { 112 return; 113 } 114 115 $closerFound = false; 116 117 // Inspect current token and subsequent inline HTML token to find a close tag. 118 for ($i = $stackPtr; $i < $phpcsFile->numTokens; $i++) { 119 if ($tokens[$i]['code'] !== T_INLINE_HTML) { 120 break; 121 } 122 123 $closerFound = strrpos($tokens[$i]['content'], '?>'); 124 if ($closerFound !== false) { 125 if ($i !== $stackPtr) { 126 break; 127 } else if ($closerFound > $openerFound) { 128 break; 129 } else { 130 $closerFound = false; 131 } 132 } 133 } 134 135 if ($closerFound !== false) { 136 $error = 'Possible use of short open tags detected; found: %s'; 137 $snippet = $this->getSnippet($content, '<?'); 138 $data = array('<?'.$snippet); 139 140 $phpcsFile->addWarning($error, $stackPtr, 'PossibleFound', $data); 141 142 // Skip forward to the token containing the closer. 143 if (($i - 1) > $stackPtr) { 144 return $i; 145 } 146 } 147 }//end if 148 149 }//end process() 150 151 152 /** 153 * Get a snippet from a HTML token. 154 * 155 * @param string $content The content of the HTML token. 156 * @param string $start Partial string to use as a starting point for the snippet. 157 * @param int $length The target length of the snippet to get. Defaults to 40. 158 * 159 * @return string 160 */ 161 protected function getSnippet($content, $start='', $length=40) 162 { 163 $startPos = 0; 164 165 if ($start !== '') { 166 $startPos = strpos($content, $start); 167 if ($startPos !== false) { 168 $startPos += strlen($start); 169 } 170 } 171 172 $snippet = substr($content, $startPos, $length); 173 if ((strlen($content) - $startPos) > $length) { 174 $snippet .= '...'; 175 } 176 177 return $snippet; 178 179 }//end getSnippet() 180 181 182}//end class 183