Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NewNowdocQuotedHeredoc: Add sniffing for quoted heredoc identifiers #390

Merged
merged 2 commits into from
Apr 26, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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