Skip to content

Commit

Permalink
Merge pull request #390 from jrfnl/php5.3/quoted-heredoc-identifier
Browse files Browse the repository at this point in the history
NewNowdocQuotedHeredoc: Add sniffing for quoted heredoc identifiers
  • Loading branch information
wimg committed Apr 26, 2017
2 parents 6e9fa08 + 7463ce1 commit f61ac96
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 75 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/**
* PHPCompatibility_Sniffs_PHP_NewNowdocSniff.
* PHPCompatibility_Sniffs_PHP_NewNowdocQuotedHeredocSniff.
*
* PHP version 5.3
*
Expand All @@ -10,13 +10,15 @@
*/

/**
* PHPCompatibility_Sniffs_PHP_NewNowdocSniff.
* PHPCompatibility_Sniffs_PHP_NewNowdocQuotedHeredocSniff.
*
* PHP 5.3 introduces Nowdoc syntax and (double) quoted identifiers for heredocs.
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class PHPCompatibility_Sniffs_PHP_NewNowdocSniff extends PHPCompatibility_Sniff
class PHPCompatibility_Sniffs_PHP_NewNowdocQuotedHeredocSniff extends PHPCompatibility_Sniff
{

/**
Expand All @@ -39,6 +41,8 @@ public function register()
$targets[] = constant('T_END_NOWDOC');
}

$targets[] = T_START_HEREDOC;

return $targets;

}//end register()
Expand All @@ -51,7 +55,7 @@ public function register()
* @param int $stackPtr The position of the current token in
* the stack passed in $tokens.
*
* @return int|void On older PHP versions passes a pointer to the nowdoc closer
* @return int|void On older PHP versions passes a pointer to the nowdoc/heredoc closer
* to skip passed anything in between in regards to processing
* the file for this sniff.
*/
Expand All @@ -61,18 +65,44 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
return;
}

$tokens = $phpcsFile->getTokens();
$tokens = $phpcsFile->getTokens();
$isNowdoc = false;
$isHeredoc = false;

switch ($tokens[$stackPtr]['type']) {
case 'T_START_NOWDOC':
case 'T_END_NOWDOC':
$isNowdoc = true;
break;

case 'T_START_HEREDOC':
/*
* If we have a heredoc opener, make sure we only report on double quoted identifiers.
* A double quoted identifier will have the opening quote on position 3 in the string:
* `<<<"ID"`
*/
if ($tokens[$stackPtr]['content'][3] !== '"') {
return;
}

$isHeredoc = true;
break;
}

/*
* In PHP 5.2 the T_NOWDOC tokens aren't recognized yet and PHPCS does not
* backfill for it, so we have to sniff for a specific combination of tokens.
* In PHP 5.2 the T_NOWDOC and the quoted T_HEREDOC tokens aren't recognized yet
* and PHPCS does not backfill for it, so we have to sniff for a specific
* combination of tokens.
*/
if ($tokens[$stackPtr]['code'] === T_SL) {
if (isset($tokens[($stackPtr+1)]) === false || $tokens[($stackPtr + 1)]['code'] !== T_LESS_THAN) {
/*
* Check for the right token combination.
*/
if (isset($tokens[($stackPtr + 1)]) === false || $tokens[($stackPtr + 1)]['code'] !== T_LESS_THAN) {
return;
}

if (isset($tokens[($stackPtr+2)]) === false
if (isset($tokens[($stackPtr + 2)]) === false
|| $tokens[($stackPtr + 2)]['code'] !== T_CONSTANT_ENCAPSED_STRING) {
return;
}
Expand All @@ -83,14 +113,30 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
* must start with a non-digit character or underscore"
* @link http://php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc
*/
if (preg_match('`^\'([a-z][a-z0-9_]*)\'$`iD', $tokens[($stackPtr + 2)]['content'], $matches) < 1) {
if (preg_match('`^(["\'])([a-z][a-z0-9_]*)\1$`iD', $tokens[($stackPtr + 2)]['content'], $matches) < 1) {
return;
}

// Remember whether we found a nowdoc or a heredoc.
switch ($matches[1]) {
case "'":
$isNowdoc = true;
break;

case '"':
$isHeredoc = true;
break;

}


/*
* See if we can find the nowdoc/heredoc closer.
*/
$closer = null;

for ($i = ($stackPtr + 3); $i < $phpcsFile->numTokens; $i++) {
$maybeCloser = $phpcsFile->findNext(T_STRING, $i, null, false, $matches[1]);
$maybeCloser = $phpcsFile->findNext(T_STRING, $i, null, false, $matches[2]);
if ($maybeCloser === false) {
return;
}
Expand All @@ -116,11 +162,25 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
}
}

$phpcsFile->addError('Nowdocs are not present in PHP version 5.2 or earlier.', $stackPtr, 'Found');
if ($isNowdoc === true) {
$error = 'Nowdocs are not present in PHP version 5.2 or earlier.';
$phpcsFile->addError($error, $stackPtr, 'Found');

if (isset($closer) !== false) {
// Also throw an error for the closer on older PHP versions and skip forward past it.
// Not needed for newer PHP versions as those will trigger this sniff for the closer token itself.
$phpcsFile->addError($error, $closer, 'Found');
return ($closer + 1);
}
}

if ($isHeredoc === true) {
// Only throw an error for the opener.
$phpcsFile->addError('The Heredoc identifier may not be enclosed in (double) quotes in PHP version 5.2 or earlier.', $stackPtr, 'Found');

if (isset($closer) !== false) {
$phpcsFile->addError('Nowdocs are not present in PHP version 5.2 or earlier.', $closer, 'Found');
return ($closer + 1);
if (isset($closer) !== false) {
return ($closer + 1);
}
}

}//end process()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
<?php
/**
* New nowdoc sniff test file
* New nowdoc and quoted heredoc sniff test file
*
* @package PHPCompatibility
*/


/**
* New nowdoc sniff tests
* New nowdoc and quoted heredoc sniff tests
*
* @group newNowdoc
* @group newNowdocQuotedHeredoc
* @group reservedKeywords
*
* @covers PHPCompatibility_Sniffs_PHP_NewNowdocSniff
* @covers PHPCompatibility_Sniffs_PHP_NewNowdocQuotedHeredocSniff
*
* @uses BaseSniffTest
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class NewNowdocSniffTest extends BaseSniffTest
class NewNowdocQuotedHeredocSniffTest extends BaseSniffTest
{
const TEST_FILE = 'sniff-examples/new_nowdoc.php';
const TEST_FILE = 'sniff-examples/new_nowdoc_quoted_heredoc.php';


/**
Expand Down Expand Up @@ -48,16 +48,50 @@ public function testNowdoc($line)
public function dataNowdoc()
{
return array(
array(11),
array(15),
array(17),
array(19),
array(21),
array(25),
array(28),
array(30),
array(33),
array(36),
array(44),
array(29),
array(32),
array(34),
array(37),
array(40),
array(48),
);
}


/**
* testQuotedHeredoc
*
* @dataProvider dataQuotedHeredoc
*
* @param int $line The line number.
*
* @return void
*/
public function testQuotedHeredoc($line)
{
$file = $this->sniffFile(self::TEST_FILE, '5.2');
$this->assertError($file, $line, 'The Heredoc identifier may not be enclosed in (double) quotes in PHP version 5.2 or earlier.');
}

/**
* Data provider.
*
* @see testQuotedHeredoc()
*
* @return array
*/
public function dataQuotedHeredoc()
{
return array(
array(55),
array(61),
array(69),
array(74),
array(80),
);
}

Expand Down Expand Up @@ -89,14 +123,18 @@ public function dataNoFalsePositives()
$data = array(
array(4),
array(8),
array(26),
array(32),
array(30),
array(36),
array(70),
array(76),
);

// PHPCS 1.x does not support skipping forward.
if (version_compare(PHP_CodeSniffer::VERSION, '2.0', '>=')) {
$data[] = array(38);
$data[] = array(42);
$data[] = array(46);
$data[] = array(82);
$data[] = array(86);
}

return $data;
Expand Down
44 changes: 0 additions & 44 deletions Tests/sniff-examples/new_nowdoc.php

This file was deleted.

Loading

0 comments on commit f61ac96

Please sign in to comment.