Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4debf53

Browse files
Accept namespaces in sitePassByRefFunctions (#351)
* Add test for using a namespace in sitePassByRefFunctions * Add namespace to function names when looking for pass-by-reference * Make sure cache is not used before it is ready * Make sure we don't try to use null in getPassByReferenceFunction * Init passByRefFunctionsCache to null explicitly
1 parent 5e2f18e commit 4debf53

File tree

4 files changed

+102
-2
lines changed

4 files changed

+102
-2
lines changed

‎Tests/VariableAnalysisSniff/VariableAnalysisTest.php‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@ public function testFunctionWithReferenceWarnings()
298298
77,
299299
81,
300300
98,
301+
106,
301302
];
302303
$this->assertSame($expectedWarnings, $lines);
303304
}
@@ -331,6 +332,36 @@ public function testFunctionWithReferenceWarningsAllowsCustomFunctions()
331332
$this->assertSame($expectedWarnings, $lines);
332333
}
333334

335+
public function testFunctionWithReferenceWarningsAllowsCustomFunctionsNamespaced()
336+
{
337+
$fixtureFile = $this->getFixture('FunctionWithReferenceFixture.php');
338+
$phpcsFile = $this->prepareLocalFileForSniffs($fixtureFile);
339+
$this->setSniffProperty($phpcsFile, 'sitePassByRefFunctions', '\My\Functions\my_reference_function:2,3 another_reference_function:2,...');
340+
$phpcsFile->process();
341+
$lines = $this->getWarningLineNumbersFromFile($phpcsFile);
342+
$expectedWarnings = [
343+
10,
344+
11,
345+
12,
346+
13,
347+
14,
348+
16,
349+
29,
350+
41,
351+
42,
352+
43,
353+
46,
354+
52,
355+
56,
356+
57,
357+
63,
358+
76,
359+
81,
360+
98,
361+
];
362+
$this->assertSame($expectedWarnings, $lines);
363+
}
364+
334365
public function testFunctionWithReferenceWarningsAllowsWordPressFunctionsIfSet()
335366
{
336367
$fixtureFile = $this->getFixture('FunctionWithReferenceFixture.php');
@@ -357,6 +388,7 @@ public function testFunctionWithReferenceWarningsAllowsWordPressFunctionsIfSet()
357388
76,
358389
77,
359390
98,
391+
106,
360392
];
361393
$this->assertSame($expectedWarnings, $lines);
362394
}

‎Tests/VariableAnalysisSniff/fixtures/FunctionWithReferenceFixture.php‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,8 @@ function function_with_foreach_with_reference($derivatives, $base_plugin_definit
100100
}
101101
return $derivatives;
102102
}
103+
104+
function function_with_ignored_reference_call_with_namespace() {
105+
$foo = 'bar';
106+
\My\Functions\my_reference_function($foo, $baz, $bip); // Undefined variable $bar, Undefined variable $bip
107+
}

‎VariableAnalysis/Lib/Helpers.php‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1658,6 +1658,41 @@ public static function isConstructorPromotion(File $phpcsFile, $stackPtr)
16581658
return false;
16591659
}
16601660

1661+
/**
1662+
* If looking at a function call token, return a string for the full function
1663+
* name including any inline namespace.
1664+
*
1665+
* So for example, if the call looks like `\My\Namespace\doSomething($bar)`
1666+
* and `$stackPtr` refers to `doSomething`, this will return
1667+
* `\My\Namespace\doSomething`.
1668+
*
1669+
* @param File $phpcsFile
1670+
* @param int $stackPtr
1671+
*
1672+
* @return string|null
1673+
*/
1674+
public static function getFunctionNameWithNamespace(File $phpcsFile, $stackPtr)
1675+
{
1676+
$tokens = $phpcsFile->getTokens();
1677+
1678+
if (! isset($tokens[$stackPtr])) {
1679+
return null;
1680+
}
1681+
$startOfScope = self::findVariableScope($phpcsFile, $stackPtr);
1682+
$functionName = $tokens[$stackPtr]['content'];
1683+
1684+
// Move backwards from the token, collecting namespace separators and
1685+
// strings, until we encounter whitespace or something else.
1686+
$partOfNamespace = [T_NS_SEPARATOR, T_STRING];
1687+
for ($i = $stackPtr - 1; $i > $startOfScope; $i--) {
1688+
if (! in_array($tokens[$i]['code'], $partOfNamespace, true)) {
1689+
break;
1690+
}
1691+
$functionName = "{$tokens[$i]['content']}{$functionName}";
1692+
}
1693+
return $functionName;
1694+
}
1695+
16611696
/**
16621697
* Return false if the token is definitely not part of a typehint
16631698
*

‎VariableAnalysis/Sniffs/CodeAnalysis/VariableAnalysisSniff.php‎

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,13 @@ class VariableAnalysisSniff implements Sniff
153153
*/
154154
public $allowUnusedVariablesBeforeRequire = false;
155155

156+
/**
157+
* A cache for getPassByReferenceFunctions
158+
*
159+
* @var array<array<int|string>>|null
160+
*/
161+
private $passByRefFunctionsCache = null;
162+
156163
public function __construct()
157164
{
158165
$this->scopeManager = new ScopeManager();
@@ -195,6 +202,18 @@ public function register()
195202
*/
196203
private function getPassByReferenceFunction($functionName)
197204
{
205+
$passByRefFunctions = $this->getPassByReferenceFunctions();
206+
return isset($passByRefFunctions[$functionName]) ? $passByRefFunctions[$functionName] : [];
207+
}
208+
209+
/**
210+
* @return array<array<int|string>>
211+
*/
212+
private function getPassByReferenceFunctions()
213+
{
214+
if (! is_null($this->passByRefFunctionsCache)) {
215+
return $this->passByRefFunctionsCache;
216+
}
198217
$passByRefFunctions = Constants::getPassByReferenceFunctions();
199218
if (!empty($this->sitePassByRefFunctions)) {
200219
$lines = Helpers::splitStringToArray('/\s+/', trim($this->sitePassByRefFunctions));
@@ -206,7 +225,8 @@ private function getPassByReferenceFunction($functionName)
206225
if ($this->allowWordPressPassByRefFunctions) {
207226
$passByRefFunctions = array_merge($passByRefFunctions, Constants::getWordPressPassByReferenceFunctions());
208227
}
209-
return isset($passByRefFunctions[$functionName]) ? $passByRefFunctions[$functionName] : [];
228+
$this->passByRefFunctionsCache = $passByRefFunctions;
229+
return $passByRefFunctions;
210230
}
211231

212232
/**
@@ -1485,7 +1505,15 @@ protected function processVariableAsPassByReferenceFunctionCall(File $phpcsFile,
14851505
$functionName = $tokens[$functionPtr]['content'];
14861506
$refArgs = $this->getPassByReferenceFunction($functionName);
14871507
if (! $refArgs) {
1488-
return false;
1508+
// Check again with the fully namespaced function name.
1509+
$functionName = Helpers::getFunctionNameWithNamespace($phpcsFile, $functionPtr);
1510+
if (! $functionName) {
1511+
return false;
1512+
}
1513+
$refArgs = $this->getPassByReferenceFunction($functionName);
1514+
if (! $refArgs) {
1515+
return false;
1516+
}
14891517
}
14901518

14911519
$argPtrs = Helpers::findFunctionCallArguments($phpcsFile, $stackPtr);

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /