Skip to content

Commit

Permalink
Merge pull request #282 from jrfnl/7.1/nullable-types
Browse files Browse the repository at this point in the history
PHP 7.1: New sniff for nullable types.
  • Loading branch information
wimg committed Oct 20, 2016
2 parents 069994e + 574a68f commit 9e9452b
Show file tree
Hide file tree
Showing 4 changed files with 351 additions and 4 deletions.
22 changes: 18 additions & 4 deletions Sniff.php
Expand Up @@ -787,6 +787,9 @@ public function getDeclaredNamespaceName(PHP_CodeSniffer_File $phpcsFile, $stack
* 'name' => '$var', // The variable name.
* 'pass_by_reference' => false, // Passed by reference.
* 'type_hint' => string, // Type hint for array or custom type
* 'nullable_type' => bool, // Whether the type given in the type hint is nullable
* 'type_hint' => string, // Type hint for array or custom type
* 'raw' => string, // Raw content of the tokens for the parameter
* )
* </code>
*
Expand All @@ -796,6 +799,7 @@ public function getDeclaredNamespaceName(PHP_CodeSniffer_File $phpcsFile, $stack
* {@internal Duplicate of same method as contained in the `PHP_CodeSniffer_File`
* class, but with some improvements which will probably be introduced in
* PHPCS 2.7.1/2.8. {@see https://github.com/squizlabs/PHP_CodeSniffer/pull/1117}
* and {@see https://github.com/squizlabs/PHP_CodeSniffer/pull/1193}
*
* Once the minimum supported PHPCS version for this sniff library goes beyond
* that, this method can be removed and calls to it replaced with
Expand All @@ -804,8 +808,9 @@ public function getDeclaredNamespaceName(PHP_CodeSniffer_File $phpcsFile, $stack
* Last synced with PHPCS version: PHPCS 2.7.}}
*
* @param PHP_CodeSniffer_File $phpcsFile Instance of phpcsFile.
* @param int $stackPtr The position in the stack of the T_FUNCTION token
* to acquire the parameters for.
* @param int $stackPtr The position in the stack of the
* T_FUNCTION token to acquire the
* parameters for.
*
* @return array|false
* @throws PHP_CodeSniffer_Exception If the specified $stackPtr is not of
Expand Down Expand Up @@ -835,6 +840,7 @@ public function getMethodParameters(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
$passByReference = false;
$variableLength = false;
$typeHint = '';
$nullableType = false;

for ($i = $paramStart; $i <= $closer; $i++) {
// Check to see if this token has a parenthesis or bracket opener. If it does
Expand Down Expand Up @@ -873,7 +879,7 @@ public function getMethodParameters(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
case T_PARENT:
case T_STATIC:
// Self is valid, the others invalid, but were probably intended as type hints.
if (isset($defaultStart) === false) {
if ($defaultStart === null) {
$typeHint = $tokens[$i]['content'];
}
break;
Expand Down Expand Up @@ -912,6 +918,12 @@ public function getMethodParameters(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
$typeHint .= $tokens[$i]['content'];
}
break;
case T_INLINE_THEN:
if ($defaultStart === null) {
$nullableType = true;
$typeHint .= $tokens[$i]['content'];
}
break;
case T_CLOSE_PARENTHESIS:
case T_COMMA:
// If it's null, then there must be no parameters for this
Expand All @@ -936,14 +948,16 @@ public function getMethodParameters(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
$vars[$paramCount]['pass_by_reference'] = $passByReference;
$vars[$paramCount]['variable_length'] = $variableLength;
$vars[$paramCount]['type_hint'] = $typeHint;
$vars[$paramCount]['raw'] = $rawContent;
$vars[$paramCount]['nullable_type'] = $nullableType;
$vars[$paramCount]['raw'] = $rawContent;

// Reset the vars, as we are about to process the next parameter.
$defaultStart = null;
$paramStart = ($i + 1);
$passByReference = false;
$variableLength = false;
$typeHint = '';
$nullableType = false;

$paramCount++;
break;
Expand Down
120 changes: 120 additions & 0 deletions Sniffs/PHP/NewNullableTypesSniff.php
@@ -0,0 +1,120 @@
<?php
/**
* PHPCompatibility_Sniffs_PHP_NewNullableTypes.
*
* PHP version 7.1
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/

/**
* PHPCompatibility_Sniffs_PHP_NewNullableTypes.
*
* Nullable type hints and return types are available since PHP 7.1.
*
* PHP version 7.1
*
* @category PHP
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class PHPCompatibility_Sniffs_PHP_NewNullableTypesSniff extends PHPCompatibility_Sniff
{
/**
* Returns an array of tokens this test wants to listen for.
*
* @return array
*/
public function register()
{
$tokens = array(
T_FUNCTION,
);

if (version_compare(PHP_CodeSniffer::VERSION, '2.3.4') >= 0) {
$tokens[] = T_RETURN_TYPE;
}

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)
{
if ($this->supportsBelow('7.0') === false) {
return;
}

$tokens = $phpcsFile->getTokens();
$tokenCode = $tokens[$stackPtr]['code'];

if ($tokenCode === T_FUNCTION) {
$this->processFunctionDeclaration($phpcsFile, $stackPtr);
} else {
$this->processReturnType($phpcsFile, $stackPtr);
}
}//end process()


/**
* Process this test for function tokens.
*
* @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
*/
protected function processFunctionDeclaration(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$params = $this->getMethodParameters($phpcsFile, $stackPtr);

if (empty($params) === false && is_array($params)) {
foreach ($params as $param) {
if ($param['nullable_type'] === true) {
$phpcsFile->addError(
'Nullable type declarations are not supported in PHP 7.0 or earlier. Found: %s',
$stackPtr,
'typeDeclarationFound',
array($param['type_hint'])
);
}
}
}
}


/**
* Process this test for return type tokens.
*
* @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
*/
protected function processReturnType(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();
if ($tokens[($stackPtr - 1)]['code'] === T_INLINE_THEN) {
$phpcsFile->addError(
'Nullable return types are not supported in PHP 7.0 or earlier.',
$stackPtr,
'returnTypeFound'
);
}
}

}//end class
157 changes: 157 additions & 0 deletions Tests/Sniffs/PHP/NewNullableTypesSniffTest.php
@@ -0,0 +1,157 @@
<?php
/**
* New nullable type hints / return types sniff test file
*
* @package PHPCompatibility
*/


/**
* New nullable type hints / return types sniff test file
*
* @uses BaseSniffTest
* @package PHPCompatibility
* @author Juliette Reinders Folmer <phpcompatibility_nospam@adviesenzo.nl>
*/
class NewNullableTypesSniffTest extends BaseSniffTest
{
const TEST_FILE = 'sniff-examples/new_nullable_types.php';

/**
* testNewNullableReturnTypes
*
* @group nullableTypes
*
* @dataProvider dataNewNullableReturnTypes
*
* @param int $line The line number.
*
* @return void
*/
public function testNewNullableReturnTypes($line)
{
// Skip this test for low PHPCS versions.
if (version_compare(PHP_CodeSniffer::VERSION, '2.3.4', '<')) {
$this->markTestSkipped();
}

$file = $this->sniffFile(self::TEST_FILE, '7.0');
$this->assertError($file, $line, 'Nullable return types are not supported in PHP 7.0 or earlier.');

$file = $this->sniffFile(self::TEST_FILE, '7.1');
$this->assertNoViolation($file, $line);
}

/**
* Data provider.
*
* @see testNewNullableReturnTypes()
*
* @return array
*/
public function dataNewNullableReturnTypes()
{
return array(
array(20),
array(21),
array(22),
array(23),
array(24),
array(25),
array(26),
array(27),
);
}


/**
* testNewNullableTypeHints
*
* @group nullableTypes
*
* @dataProvider dataNewNullableTypeHints
*
* @param int $line The line number.
*
* @return void
*/
public function testNewNullableTypeHints($line)
{
$file = $this->sniffFile(self::TEST_FILE, '7.0');
$this->assertError($file, $line, 'Nullable type declarations are not supported in PHP 7.0 or earlier.');

$file = $this->sniffFile(self::TEST_FILE, '7.1');
$this->assertNoViolation($file, $line);
}

/**
* Data provider.
*
* @see testNewNullableTypeHints()
*
* @return array
*/
public function dataNewNullableTypeHints()
{
return array(
array(45),
array(46),
array(47),
array(48),
array(49),
array(50),
array(51),
array(52),

array(55), // Three errors of the same.
);
}


/**
* testNoViolation
*
* @group nullableTypes
*
* @dataProvider dataNoViolation
*
* @param int $line The line number.
*
* @return void
*/
public function testNoViolation($line)
{
$file = $this->sniffFile(self::TEST_FILE, '7.0'); // Arbitrary pre-PHP 7.1 version.
$this->assertNoViolation($file, $line);
}

/**
* Data provider.
*
* @see testNoViolation()
*
* @return array
*/
public function dataNoViolation()
{
return array(
array(8),
array(9),
array(10),
array(11),
array(12),
array(13),
array(14),
array(15),

array(33),
array(34),
array(35),
array(36),
array(37),
array(38),
array(39),
array(40),
);
}
}

0 comments on commit 9e9452b

Please sign in to comment.