Skip to content

Commit

Permalink
Added support for nullable return types (ref squizlabs#1527)
Browse files Browse the repository at this point in the history
  • Loading branch information
gsherwood committed Apr 17, 2018
1 parent 1f60f4f commit aca4d9f
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 93 deletions.
8 changes: 5 additions & 3 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
- The current method for setting array properties in ruleset files has been deprecated and will be removed in version 4
-- Currently, setting an array value uses the string syntax "print=>echo,create_function=>null"
-- Now, invididual array elements are specifed using a new "element" tag with "key" and "value" attributes
-- For example, element key="print" value="echo"
--- For example, element key="print" value="echo"
-- Thanks to Michał Bundyra for the patch

- Config values set using --runtime-set now override any config values set in rulesets or the CodeSniffer.conf file
Expand All @@ -50,7 +50,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
- PHPCS annotations now remain as T_PHPCS_* tokens instead of reverting to comment tokens when --ignore-annotations is used
-- This stops sniffs (especially commenting sniffs) from generating a large number of false errors when ignoring
-- Any custom sniffs that are using the T_PHPCS_* tokens to detect annotations may need to be changed to ignore them
-- Check $phpcsFile->config->annotations to see if annotations are enabled and ignore when false
--- Check $phpcsFile->config->annotations to see if annotations are enabled and ignore when false
- Function return types are now tokenized as T_RETURN_TYPE in all cases
-- Previously, only simple types were tokenized in this way and namespaces were ignored
-- Custom sniffs looking for T_NS_SEPERATOR inside return types will now need to look for T_RETURN_TYPE and check the value directly
Expand All @@ -65,8 +65,10 @@ http://pear.php.net/dtd/package-2.0.xsd">
-- Thanks to Timo Schinkel for the patch
- The return value of File::getMethodProperties() now contains a "return_type" array index
-- This contains the return type of the function or closer, or a blank string if not specified
-- If the return type is nullable, the return type will contain the leading ?
--- A nullable_return_type array index in the return value will also be set to true
-- If the return type contains namespace information, it will be cleaned of whitespace and comments
-- To access the original return value string, use the main tokens array
--- To access the original return value string, use the main tokens array
- The key used for caching PHPCS runs now includes all set config values
-- This fixes a problem where changing config values (e.g., via --runtime-set) used an incorrect cache file
- The "Function opening brace placement" metric has been separated into function and closure metrics in the info report
Expand Down
42 changes: 26 additions & 16 deletions src/Files/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -1427,12 +1427,13 @@ public function getMethodParameters($stackPtr)
* The format of the array is:
* <code>
* array(
* 'scope' => 'public', // public protected or protected
* 'return_type' => '', // the return type of the method.
* 'scope_specified' => true, // true is scope keyword was found.
* 'is_abstract' => false, // true if the abstract keyword was found.
* 'is_final' => false, // true if the final keyword was found.
* 'is_static' => false, // true if the static keyword was found.
* 'scope' => 'public', // public protected or protected
* 'scope_specified' => true, // true is scope keyword was found.
* 'return_type' => '', // the return type of the method.
* 'nullable_return_type' => false, // true if the return type is nullable.
* 'is_abstract' => false, // true if the abstract keyword was found.
* 'is_final' => false, // true if the final keyword was found.
* 'is_static' => false, // true if the static keyword was found.
* );
* </code>
*
Expand Down Expand Up @@ -1474,10 +1475,11 @@ public function getMethodProperties($stackPtr)

$scope = 'public';
$scopeSpecified = false;
$isAbstract = false;
$isFinal = false;
$isStatic = false;
$returnType = '';
$nullableReturnType = false;
$isAbstract = false;
$isFinal = false;
$isStatic = false;

for ($i = ($stackPtr - 1); $i > 0; $i--) {
if (isset($valid[$this->tokens[$i]['code']]) === false) {
Expand Down Expand Up @@ -1523,26 +1525,34 @@ public function getMethodProperties($stackPtr)
break;
}

if ($this->tokens[$i]['code'] === T_NULLABLE) {
$nullableReturnType = true;
}

while ($this->tokens[$i]['code'] === T_RETURN_TYPE) {
$returnType .= $this->tokens[$i]['content'];
$i++;
}
}
}
}//end if

if ($returnType !== '') {
// Cleanup.
$returnType = preg_replace('/\s+/', '', $returnType);
$returnType = preg_replace('/\/\*.*?\*\//', '', $returnType);
if ($nullableReturnType === true) {
$returnType = '?'.$returnType;
}
}

return [
'scope' => $scope,
'return_type' => $returnType,
'scope_specified' => $scopeSpecified,
'is_abstract' => $isAbstract,
'is_final' => $isFinal,
'is_static' => $isStatic,
'scope' => $scope,
'scope_specified' => $scopeSpecified,
'return_type' => $returnType,
'nullable_return_type' => $nullableReturnType,
'is_abstract' => $isAbstract,
'is_final' => $isFinal,
'is_static' => $isStatic,
];

}//end getMethodProperties()
Expand Down
12 changes: 10 additions & 2 deletions src/Tokenizers/PHP.php
Original file line number Diff line number Diff line change
Expand Up @@ -1057,7 +1057,7 @@ protected function tokenize($string)
) {
// Non-empty content.
if (is_array($tokens[$x]) === true && $tokens[$x][0] === T_USE) {
// Search ahead for the closing parenthesis.
// Found a use statements, so search ahead for the closing parenthesis.
for ($x = ($x + 1); $x < $numTokens; $x++) {
if (is_array($tokens[$x]) === false && $tokens[$x] === ')') {
continue(2);
Expand Down Expand Up @@ -1090,7 +1090,15 @@ protected function tokenize($string)
&& is_array($tokens[$x]) === true
&& isset(Util\Tokens::$emptyTokens[$tokens[$x][0]]) === true
) {
// We haven't found the start of the type hint yet.
// Whitespace or coments before the type hint.
continue;
}

if ($typeHintStart === null
&& is_array($tokens[$x]) === false
&& $tokens[$x] === '?'
) {
// Found a nullable operator, so skip it.
continue;
}

Expand Down
9 changes: 9 additions & 0 deletions tests/Core/File/GetMethodPropertiesTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ class MyClass {
/* testPublicReturnMethod */
public function myFunction(): array {}

/* testNullableReturnMethod */
public function myFunction(): ?array {}

/* testMessyNullableReturnMethod */
public function myFunction() /* comment
*/ :
/* comment */ ? //comment
array {}

/* testReturnNamespace */
function myFunction(): \MyNamespace\MyClass {}

Expand Down
Loading

0 comments on commit aca4d9f

Please sign in to comment.