Skip to content

Commit 4933983

Browse files
committed
Generic/UnusedFunctionParameter: ignore magic methods
The function signature of magic methods is dictated by PHP and unused parameters cannot be removed, which means that the warnings for these can never be resolved, only ignored via annotations. This commit fixes this by checking whether a function is a magic method and if so, bowing out. Includes unit tests. Note: while not all magic methods take arguments, I'm still including the full list of magic methods in the property as the other magic methods can be ignored anyway (no arguments).
1 parent 9f297f0 commit 4933983

File tree

2 files changed

+84
-2
lines changed

2 files changed

+84
-2
lines changed

src/Standards/Generic/Sniffs/CodeAnalysis/UnusedFunctionParameterSniff.php

+37-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,31 @@ class UnusedFunctionParameterSniff implements Sniff
3030
*/
3131
public $ignoreTypeHints = [];
3232

33+
/**
34+
* A list of all PHP magic methods.
35+
*
36+
* @var array
37+
*/
38+
private $magicMethods = [
39+
'__construct' => true,
40+
'__destruct' => true,
41+
'__call' => true,
42+
'__callstatic' => true,
43+
'__get' => true,
44+
'__set' => true,
45+
'__isset' => true,
46+
'__unset' => true,
47+
'__sleep' => true,
48+
'__wakeup' => true,
49+
'__serialize' => true,
50+
'__unserialize' => true,
51+
'__tostring' => true,
52+
'__invoke' => true,
53+
'__set_state' => true,
54+
'__clone' => true,
55+
'__debuginfo' => true,
56+
];
57+
3358

3459
/**
3560
* Returns an array of tokens this test wants to listen for.
@@ -71,8 +96,18 @@ public function process(File $phpcsFile, $stackPtr)
7196
$extends = false;
7297

7398
if ($token['code'] === T_FUNCTION) {
74-
$classPtr = $phpcsFile->getCondition($stackPtr, T_CLASS);
99+
$classPtr = $phpcsFile->getCondition($stackPtr, T_CLASS);
75100
if ($classPtr !== false) {
101+
// Check for magic methods and ignore these as the method signature cannot be changed.
102+
$methodName = $phpcsFile->getDeclarationName($stackPtr);
103+
if (empty($methodName) === false) {
104+
$methodNameLc = strtolower($methodName);
105+
if (isset($this->magicMethods[$methodNameLc]) === true) {
106+
return;
107+
}
108+
}
109+
110+
// Check for extends/implements and adjust the error code when found.
76111
$implements = $phpcsFile->findImplementedInterfaceNames($classPtr);
77112
$extends = $phpcsFile->findExtendedClassName($classPtr);
78113
if ($extends !== false) {
@@ -81,7 +116,7 @@ public function process(File $phpcsFile, $stackPtr)
81116
$errorCode .= 'InImplementedInterface';
82117
}
83118
}
84-
}
119+
}//end if
85120

86121
$params = [];
87122
$methodParams = $phpcsFile->getMethodParameters($stackPtr);

src/Standards/Generic/Tests/CodeAnalysis/UnusedFunctionParameterUnitTest.inc

+47
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,50 @@ class MyExtendedClass implements SomeInterface {
172172
$fn = fn($c, $d) => $c[2];
173173
}
174174
}
175+
176+
177+
/**
178+
* Magic methods must match the function signature dictated by PHP.
179+
* Flagging unused parameters leads to notices which cannot be solved.
180+
*/
181+
class MagicMethodsWithParams {
182+
public function __set(string $name, mixed $value) {
183+
// Forbid dynamic properties & overloading inaccessible properties.
184+
throw new RuntimeException('Forbidden');
185+
}
186+
187+
public function __get(string $name) {
188+
throw new RuntimeException('Forbidden');
189+
}
190+
191+
public function __isset(string $name) {
192+
throw new RuntimeException('Forbidden');
193+
}
194+
195+
public function __unset(string $name) {
196+
throw new RuntimeException('Forbidden');
197+
}
198+
199+
public function __unserialize( array $data ) {
200+
// Prevent unserializing from a stored representation of the object for security reasons.
201+
$this->instance = new self();
202+
}
203+
204+
public static function __set_state(array $properties) {
205+
return new self();
206+
}
207+
208+
public function __call(string $name, array $arguments) {
209+
if (method_exists($this, $name)) {
210+
// None of the methods which can be called in this class take arguments, so not passing them.
211+
return $this->$name();
212+
}
213+
}
214+
215+
public static function __callStatic(string $name, array $arguments) {
216+
if (method_exists($this, $name)) {
217+
// None of the methods which can be called in this class take arguments, so not passing them.
218+
return self::$name();
219+
}
220+
}
221+
}

0 commit comments

Comments
 (0)