Skip to content

Commit

Permalink
Add sniff to disallow alternative PHP open tags.
Browse files Browse the repository at this point in the history
Fixes WordPress#580

This should be removed again if the upstream issue squizlabs/PHP_CodeSniffer#1063 would get traction and the associated PR would be merged *and* the minimum PHPCS version for WPCS would become higher than the version in which the PR is merged ;-)
  • Loading branch information
jrfnl committed Jul 20, 2016
1 parent 48d909d commit 961d13c
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 0 deletions.
1 change: 1 addition & 0 deletions WordPress-Core/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

<!-- http://make.wordpress.org/core/handbook/coding-standards/php/#no-shorthand-php-tags -->
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
<rule ref="WordPress.PHP.DisallowAlternativeOpenTag"/>

<!-- important to prevent issues with content being sent before headers -->
<rule ref="Generic.Files.ByteOrderMark"/>
Expand Down
123 changes: 123 additions & 0 deletions WordPress/Sniffs/PHP/DisallowAlternativeOpenTagSniff.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php
/**
* WordPress Coding Standard.
*
* @category PHP
* @package PHP_CodeSniffer
* @link https://make.wordpress.org/core/handbook/best-practices/coding-standards/
*/

/**
* Makes sure that no alternative PHP open tags are used.
*
* @link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/issues/580
*
* @category PHP
* @package PHP_CodeSniffer
* @author Juliette Reinders Folmer <wpplugins_nospam@adviesenzo.nl>
*/
class WordPress_Sniffs_PHP_DisallowAlternativeOpenTagSniff implements PHP_CodeSniffer_Sniff {

/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register() {
$tokens = array(
T_OPEN_TAG,
T_OPEN_TAG_WITH_ECHO,
);

$asp_enabled = (boolean) ini_get( 'asp_tags' );
$short_enabled = (boolean) ini_get( 'short_open_tag' );

if ( false === $asp_enabled || ( true === $asp_enabled && false === $short_enabled ) ) {
$tokens[] = T_INLINE_HTML;
}

return $tokens;

} // end register()

/**
* Processes this test, when one of its tokens is encountered.
*
* @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr ) {
$tokens = $phpcsFile->getTokens();
$openTag = $tokens[ $stackPtr ];
$asp_enabled = (boolean) ini_get( 'asp_tags' );
$short_enabled = (boolean) ini_get( 'short_open_tag' );

if ( T_OPEN_TAG === $openTag['code'] ) {
if ( '<%' === $openTag['content'] ) {
$error = 'ASP style opening tag used; expected "<?php" but found "%s"';
$data = array( $openTag['content'] );
$phpcsFile->addError( $error, $stackPtr, 'ASPOpenTagFound', $data );
}

if ( '<script language="php">' === $openTag['content'] ) {
$error = 'Script style opening tag used; expected "<?php" but found "%s"';
$data = array( $openTag['content'] );
$phpcsFile->addError( $error, $stackPtr, 'ScriptOpenTagFound', $data );
}
}

if ( T_OPEN_TAG_WITH_ECHO === $openTag['code'] && '<%=' === $openTag['content'] ) {
$error = 'ASP style opening tag used with echo; expected "<?php echo %s ..." but found "%s %s ..."';
$nextVar = $tokens[ $phpcsFile->findNext( PHP_CodeSniffer_Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ) ];
$data = array(
$nextVar['content'],
$openTag['content'],
$nextVar['content'],
);
$phpcsFile->addError( $error, $stackPtr, 'ASPShortOpenTagFound', $data );
}

if ( ( true === $asp_enabled && false === $short_enabled ) && ( T_INLINE_HTML === $openTag['code'] && 0 === strpos( $openTag['content'], '<%=' ) ) ) {

$error = 'Possible use of ASP style short opening tags. Needs manual inspection. Found: %s';
$data = array( $this->get_snippet( $openTag['content'] ) );
$phpcsFile->addWarning( $error, $stackPtr, 'MaybeASPShortOpenTagFound', $data );
}

if ( false === $asp_enabled && T_INLINE_HTML === $openTag['code'] ) {

$data = array( $this->get_snippet( $openTag['content'] ) );

if ( 0 === strpos( $openTag['content'], '<%=' ) ) {
$error = 'Possible use of ASP style short opening tags. Needs manual inspection. Found: %s';
$phpcsFile->addWarning( $error, $stackPtr, 'MaybeASPShortOpenTagFound', $data );
} elseif ( 0 === strpos( $openTag['content'], '<%' ) ) {
$error = 'Possible use of ASP style opening tags. Needs manual inspection. Found: %s';
$phpcsFile->addWarning( $error, $stackPtr, 'MaybeASPOpenTagFound', $data );
} elseif ( 0 === strpos( $openTag['content'], '<script language="php">' ) ) {
$error = 'Script style opening tag used; expected "<?php" but found "%s"';
$phpcsFile->addError( $error, $stackPtr, 'ScriptOpenTagFound', $data );
}
}

} // end process()

/**
* Get a snippet from a HTML token.
*
* @param string $content The content of the HTML token.
* @param int $length The target length of the snippet to get. Defaults to 40.
* @return string
*/
private function get_snippet( $content, $length = 40 ) {
$snippet = substr( $content, 0, $length );
if ( strlen( $content ) > $length ) {
$snippet .= '...';
}
return $snippet;
} // end get_snippet()

} // end class
9 changes: 9 additions & 0 deletions WordPress/Tests/PHP/DisallowAlternativeOpenTagUnitTest.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div>
<?php echo $var; ?>
Some content here.
<% echo $var; %>
<%= echo $var . ' and some more text to make sure the snippet works'; %>
<script language="php">
echo $var;
</script>
</div>
77 changes: 77 additions & 0 deletions WordPress/Tests/PHP/DisallowAlternativeOpenTagUnitTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php
/**
* Unit test class for WordPress Coding Standard.
*
* @category PHP
* @package PHP_CodeSniffer
* @link https://make.wordpress.org/core/handbook/best-practices/coding-standards/
*/

/**
* Unit test class for the DisallowAlternativeOpenTag sniff.
*
* A sniff unit test checks a .inc file for expected violations of a single
* coding standard. Expected errors and warnings are stored in this class.
*
* @category PHP
* @package PHP_CodeSniffer
* @author Juliette Reinders Folmer <wpplugins_nospam@adviesenzo.nl>
*/
class WordPress_Tests_PHP_DisallowAlternativeOpenTagUnitTest extends AbstractSniffUnitTest {

/**
* Returns the lines where errors should occur.
*
* The key of the array should represent the line number and the value
* should represent the number of errors that should occur on that line.
*
* @return array<int, int>
*/
public function getErrorList() {
$asp_enabled = (boolean) ini_get( 'asp_tags' );
$short_enabled = (boolean) ini_get( 'short_open_tag' );

$errors = array(
6 => 1,
);

if ( true === $asp_enabled ) {
$errors[4] = 1;
}
if ( true === $asp_enabled && ( true === $short_enabled || defined( 'HHVM_VERSION' ) === true ) ) {
$errors[5] = 1;
}

return $errors;
} // end getErrorList()

/**
* Returns the lines where warnings should occur.
*
* The key of the array should represent the line number and the value
* should represent the number of warnings that should occur on that line.
*
* @return array<int, int>
*/
public function getWarningList() {
$asp_enabled = (boolean) ini_get( 'asp_tags' );
$short_enabled = (boolean) ini_get( 'short_open_tag' );

$warnings = array();

if ( false === $asp_enabled ) {
$warnings = array(
4 => 1,
5 => 1,
);
} elseif ( false === $short_enabled && false === defined( 'HHVM_VERSION' ) ) {
$warnings = array(
5 => 1,
);
}

return $warnings;

} // end getWarningList()

} // end class

0 comments on commit 961d13c

Please sign in to comment.