avoid by-reference warnings in filter
This commit is contained in:
parent
71e1b2cbdc
commit
d013a84f90
@ -64,13 +64,17 @@ function callback(callable $callable, int $arity = 1): \Closure
|
||||
$leadingOnly = $target->isInternal()
|
||||
|| (!$target->isVariadic() && $target->getNumberOfParameters() === $arity);
|
||||
|
||||
// Internal functions like array_filter() pass values, not variables, so a closure taking a parameter by reference
|
||||
// would warn on each call. Called from a wrapper, it gets the wrapper's variables instead.
|
||||
$reflection = new \ReflectionFunction($compiled);
|
||||
$byReference = takesByReference($reflection);
|
||||
|
||||
if (!$leadingOnly) {
|
||||
return $compiled;
|
||||
return $byReference ? static fn (...$args) => $compiled(...$args) : $compiled;
|
||||
}
|
||||
|
||||
// A userland closure taking no more than the leading arguments already ignores the rest.
|
||||
$reflection = new \ReflectionFunction($compiled);
|
||||
if (!$reflection->isInternal() && !$reflection->isVariadic() && $reflection->getNumberOfParameters() <= $arity) {
|
||||
if (!$byReference && !$reflection->isInternal() && !$reflection->isVariadic() && $reflection->getNumberOfParameters() <= $arity) {
|
||||
return $compiled;
|
||||
}
|
||||
|
||||
@ -79,6 +83,22 @@ function callback(callable $callable, int $arity = 1): \Closure
|
||||
: static fn ($carry, $value) => $compiled($carry, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a function takes any of its parameters by reference.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function takesByReference(\ReflectionFunction $reflection): bool
|
||||
{
|
||||
foreach ($reflection->getParameters() as $parameter) {
|
||||
if ($parameter->isPassedByReference()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the function a callable ultimately calls: decorators are unwrapped, also when they were turned into
|
||||
* closures, and a pipeline gives its first function, which gets all of its arguments.
|
||||
|
||||
@ -107,6 +107,29 @@ class TransformsTest extends \PHPUnit\Framework\TestCase
|
||||
$this->assertSeq([2], c\filter(fn ($v) => $v % 2 === 0)(self::gen([1, 2, 3])));
|
||||
}
|
||||
|
||||
public function testFilterWithByReferenceCallbackDoesNotWarn()
|
||||
{
|
||||
$warnings = [];
|
||||
set_error_handler(function (int $level, string $message) use (&$warnings) {
|
||||
$warnings[] = $message;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
try {
|
||||
$array = c\filter(fn (&$v) => $v > 1)([1, 2, 3]);
|
||||
$seq = c\filter(fn (&$v) => $v > 1)(new Seq([1, 2, 3]));
|
||||
$both = c\filter(fn (&$v, $k) => $k > 0)([1, 2, 3]);
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
$this->assertSame([], $warnings);
|
||||
$this->assertSame([1 => 2, 2 => 3], $array);
|
||||
$this->assertSeq([2, 3], $seq);
|
||||
$this->assertSame([1 => 2, 2 => 3], $both);
|
||||
}
|
||||
|
||||
public function testFilterWithInternalPredicate()
|
||||
{
|
||||
$this->assertSame([1 => 'a'], c\filter(is_string(...))([1, 'a']));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user