Skip to content

Commit 060222e

Browse files
authored
Merge pull request #644 from PHPCSStandards/feature/getdeclarationname-sync-with-upstream
BCFile::getDeclarationName(): sync with upstream
2 parents 8ef592a + d44b561 commit 060222e

9 files changed

+309
-10
lines changed

PHPCSUtils/BackCompat/BCFile.php

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ final class BCFile
7676
*
7777
* Changelog for the PHPCS native function:
7878
* - Introduced in PHPCS 0.0.5.
79-
* - The upstream method has received no significant updates since PHPCS 3.10.1.
79+
* - PHPCS 3.12.0: hardening to handle unfinished closure better.
8080
*
8181
* @see \PHP_CodeSniffer\Files\File::getDeclarationName() Original source.
8282
* @see \PHPCSUtils\Utils\ObjectDeclarations::getName() PHPCSUtils native improved version.
@@ -98,7 +98,49 @@ final class BCFile
9898
*/
9999
public static function getDeclarationName(File $phpcsFile, $stackPtr)
100100
{
101-
return $phpcsFile->getDeclarationName($stackPtr);
101+
$tokens = $phpcsFile->getTokens();
102+
103+
$tokenCode = $tokens[$stackPtr]['code'];
104+
105+
if ($tokenCode === T_ANON_CLASS || $tokenCode === T_CLOSURE) {
106+
return null;
107+
}
108+
109+
if ($tokenCode !== T_FUNCTION
110+
&& $tokenCode !== T_CLASS
111+
&& $tokenCode !== T_INTERFACE
112+
&& $tokenCode !== T_TRAIT
113+
&& $tokenCode !== T_ENUM
114+
) {
115+
throw new RuntimeException('Token type "' . $tokens[$stackPtr]['type'] . '" is not T_FUNCTION, T_CLASS, T_INTERFACE, T_TRAIT or T_ENUM');
116+
}
117+
118+
if ($tokenCode === T_FUNCTION
119+
&& strtolower($tokens[$stackPtr]['content']) !== 'function'
120+
) {
121+
// This is a function declared without the "function" keyword.
122+
// So this token is the function name.
123+
return $tokens[$stackPtr]['content'];
124+
}
125+
126+
$stopPoint = $phpcsFile->numTokens;
127+
if (isset($tokens[$stackPtr]['parenthesis_opener']) === true) {
128+
// For functions, stop searching at the parenthesis opener.
129+
$stopPoint = $tokens[$stackPtr]['parenthesis_opener'];
130+
} elseif (isset($tokens[$stackPtr]['scope_opener']) === true) {
131+
// For OO tokens, stop searching at the open curly.
132+
$stopPoint = $tokens[$stackPtr]['scope_opener'];
133+
}
134+
135+
$content = null;
136+
for ($i = $stackPtr; $i < $stopPoint; $i++) {
137+
if ($tokens[$i]['code'] === T_STRING) {
138+
$content = $tokens[$i]['content'];
139+
break;
140+
}
141+
}
142+
143+
return $content;
102144
}
103145

104146
/**
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
/* testLiveCoding */
4+
// Intentional parse error. This must be the only test in the file.
5+
function // Comment.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
/**
3+
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
4+
*
5+
* @package PHPCSUtils
6+
* @copyright 2019-2020 PHPCSUtils Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCSStandards/PHPCSUtils
9+
*/
10+
11+
namespace PHPCSUtils\Tests\BackCompat\BCFile;
12+
13+
use PHPCSUtils\BackCompat\BCFile;
14+
use PHPCSUtils\Tests\PolyfilledTestCase;
15+
16+
/**
17+
* Tests for the \PHPCSUtils\BackCompat\BCFile::getDeclarationName() method.
18+
*
19+
* @covers \PHPCSUtils\BackCompat\BCFile::getDeclarationName
20+
*
21+
* @group objectdeclarations
22+
*
23+
* @since 1.0.0
24+
*/
25+
class GetDeclarationNameParseError1Test extends PolyfilledTestCase
26+
{
27+
28+
/**
29+
* Test receiving "null" in case of a parse error.
30+
*
31+
* @dataProvider dataGetDeclarationNameNull
32+
*
33+
* @param string $testMarker The comment which prefaces the target token in the test file.
34+
* @param int|string $targetType Token type of the token to get as stackPtr.
35+
*
36+
* @return void
37+
*/
38+
public function testGetDeclarationNameNull($testMarker, $targetType)
39+
{
40+
$target = $this->getTargetToken($testMarker, $targetType);
41+
$result = BCFile::getDeclarationName(self::$phpcsFile, $target);
42+
$this->assertNull($result);
43+
}
44+
45+
/**
46+
* Data provider.
47+
*
48+
* @see testGetDeclarationNameNull() For the array format.
49+
*
50+
* @return array<string, array<string, int|string>>
51+
*/
52+
public static function dataGetDeclarationNameNull()
53+
{
54+
return [
55+
'unfinished function/live coding' => [
56+
'testMarker' => '/* testLiveCoding */',
57+
'targetType' => \T_FUNCTION,
58+
],
59+
];
60+
}
61+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
/* testLiveCoding */
4+
// Intentional parse error/live coding. This must be the only test in the file.
5+
// Safeguarding that the utility method does not confuse the `string` type with a function name.
6+
$closure = function (string $param) use ($var
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
/**
3+
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
4+
*
5+
* @package PHPCSUtils
6+
* @copyright 2019-2020 PHPCSUtils Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCSStandards/PHPCSUtils
9+
*/
10+
11+
namespace PHPCSUtils\Tests\BackCompat\BCFile;
12+
13+
use PHPCSUtils\BackCompat\BCFile;
14+
use PHPCSUtils\Tests\PolyfilledTestCase;
15+
16+
/**
17+
* Tests for the \PHPCSUtils\BackCompat\BCFile::getDeclarationName() method.
18+
*
19+
* @covers \PHPCSUtils\BackCompat\BCFile::getDeclarationName
20+
*
21+
* @group objectdeclarations
22+
*
23+
* @since 1.1.0
24+
*/
25+
class GetDeclarationNameParseError2Test extends PolyfilledTestCase
26+
{
27+
28+
/**
29+
* Test receiving "null" in case of a parse error.
30+
*
31+
* @dataProvider dataGetDeclarationNameNull
32+
*
33+
* @param string $testMarker The comment which prefaces the target token in the test file.
34+
* @param int|string $targetType Token type of the token to get as stackPtr.
35+
*
36+
* @return void
37+
*/
38+
public function testGetDeclarationNameNull($testMarker, $targetType)
39+
{
40+
$target = $this->getTargetToken($testMarker, $targetType);
41+
$result = BCFile::getDeclarationName(self::$phpcsFile, $target);
42+
$this->assertNull($result);
43+
}
44+
45+
/**
46+
* Data provider.
47+
*
48+
* @see testGetDeclarationNameNull() For the array format.
49+
*
50+
* @return array<string, array<string, int|string>>
51+
*/
52+
public static function dataGetDeclarationNameNull()
53+
{
54+
return [
55+
'unfinished closure/live coding' => [
56+
'testMarker' => '/* testLiveCoding */',
57+
'targetType' => \T_FUNCTION,
58+
],
59+
];
60+
}
61+
}

Tests/BackCompat/BCFile/GetDeclarationNameTest.inc

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,3 @@ function &self() {}
9696

9797
/* testFunctionReturnByRefWithReservedKeywordStatic */
9898
function &static() {}
99-
100-
/* testLiveCoding */
101-
// Intentional parse error. This has to be the last test in the file.
102-
function // Comment.

Tests/BackCompat/BCFile/GetDeclarationNameTest.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,6 @@ public static function dataGetDeclarationNameNull()
8585
'testMarker' => '/* testAnonClassExtendsWithoutParens */',
8686
'targetType' => \T_ANON_CLASS,
8787
],
88-
'live-coding' => [
89-
'testMarker' => '/* testLiveCoding */',
90-
'targetType' => \T_FUNCTION,
91-
],
9288
];
9389
}
9490

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
/**
3+
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
4+
*
5+
* @package PHPCSUtils
6+
* @copyright 2019-2020 PHPCSUtils Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCSStandards/PHPCSUtils
9+
*/
10+
11+
namespace PHPCSUtils\Tests\Utils\ObjectDeclarations;
12+
13+
use PHPCSUtils\Tests\BackCompat\BCFile\GetDeclarationNameParseError1Test as BCFile_GetDeclarationNameParseError1Test;
14+
use PHPCSUtils\Utils\ObjectDeclarations;
15+
16+
/**
17+
* Tests for the \PHPCSUtils\Utils\ObjectDeclarations::getName() method.
18+
*
19+
* @covers \PHPCSUtils\Utils\ObjectDeclarations::getName
20+
*
21+
* @group objectdeclarations
22+
*
23+
* @since 1.0.0
24+
*/
25+
final class GetNameParseError1Test extends BCFile_GetDeclarationNameParseError1Test
26+
{
27+
28+
/**
29+
* Full path to the test case file associated with this test class.
30+
*
31+
* @var string
32+
*/
33+
protected static $caseFile = '';
34+
35+
/**
36+
* Initialize PHPCS & tokenize the test case file.
37+
*
38+
* Overloaded to re-use the `$caseFile` from the BCFile test.
39+
*
40+
* @beforeClass
41+
*
42+
* @return void
43+
*/
44+
public static function setUpTestFile()
45+
{
46+
self::$caseFile = \dirname(\dirname(__DIR__)) . '/BackCompat/BCFile/GetDeclarationNameParseError1Test.inc';
47+
parent::setUpTestFile();
48+
}
49+
50+
/**
51+
* Test receiving "null" in case of a parse error.
52+
*
53+
* @dataProvider dataGetDeclarationNameNull
54+
*
55+
* @param string $testMarker The comment which prefaces the target token in the test file.
56+
* @param int|string $targetType Token type of the token to get as stackPtr.
57+
*
58+
* @return void
59+
*/
60+
public function testGetDeclarationNameNull($testMarker, $targetType)
61+
{
62+
$target = $this->getTargetToken($testMarker, $targetType);
63+
$result = ObjectDeclarations::getName(self::$phpcsFile, $target);
64+
$this->assertNull($result);
65+
}
66+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
/**
3+
* PHPCSUtils, utility functions and classes for PHP_CodeSniffer sniff developers.
4+
*
5+
* @package PHPCSUtils
6+
* @copyright 2019-2020 PHPCSUtils Contributors
7+
* @license https://opensource.org/licenses/LGPL-3.0 LGPL3
8+
* @link https://github.com/PHPCSStandards/PHPCSUtils
9+
*/
10+
11+
namespace PHPCSUtils\Tests\Utils\ObjectDeclarations;
12+
13+
use PHPCSUtils\Tests\BackCompat\BCFile\GetDeclarationNameParseError2Test as BCFile_GetDeclarationNameParseError2Test;
14+
use PHPCSUtils\Utils\ObjectDeclarations;
15+
16+
/**
17+
* Tests for the \PHPCSUtils\Utils\ObjectDeclarations::getName() method.
18+
*
19+
* @covers \PHPCSUtils\Utils\ObjectDeclarations::getName
20+
*
21+
* @group objectdeclarations
22+
*
23+
* @since 1.0.0
24+
*/
25+
final class GetNameParseError2Test extends BCFile_GetDeclarationNameParseError2Test
26+
{
27+
28+
/**
29+
* Full path to the test case file associated with this test class.
30+
*
31+
* @var string
32+
*/
33+
protected static $caseFile = '';
34+
35+
/**
36+
* Initialize PHPCS & tokenize the test case file.
37+
*
38+
* Overloaded to re-use the `$caseFile` from the BCFile test.
39+
*
40+
* @beforeClass
41+
*
42+
* @return void
43+
*/
44+
public static function setUpTestFile()
45+
{
46+
self::$caseFile = \dirname(\dirname(__DIR__)) . '/BackCompat/BCFile/GetDeclarationNameParseError2Test.inc';
47+
parent::setUpTestFile();
48+
}
49+
50+
/**
51+
* Test receiving "null" in case of a parse error.
52+
*
53+
* @dataProvider dataGetDeclarationNameNull
54+
*
55+
* @param string $testMarker The comment which prefaces the target token in the test file.
56+
* @param int|string $targetType Token type of the token to get as stackPtr.
57+
*
58+
* @return void
59+
*/
60+
public function testGetDeclarationNameNull($testMarker, $targetType)
61+
{
62+
$target = $this->getTargetToken($testMarker, $targetType);
63+
$result = ObjectDeclarations::getName(self::$phpcsFile, $target);
64+
$this->assertNull($result);
65+
}
66+
}

0 commit comments

Comments
 (0)