Skip to content
7 changes: 7 additions & 0 deletions bin/functionMetadata_original.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
'apcu_key_info' => ['hasSideEffects' => true],
'apcu_sma_info' => ['hasSideEffects' => true],
'apcu_store' => ['hasSideEffects' => true],
'array_all' => ['hasSideEffects' => false],
'array_any' => ['hasSideEffects' => false],
'array_change_key_case' => ['hasSideEffects' => false],
'array_chunk' => ['hasSideEffects' => false],
'array_column' => ['hasSideEffects' => false],
Expand All @@ -32,6 +34,9 @@
'array_diff_ukey' => ['hasSideEffects' => false],
'array_fill' => ['hasSideEffects' => false],
'array_fill_keys' => ['hasSideEffects' => false],
'array_filter' => ['hasSideEffects' => false],
'array_find' => ['hasSideEffects' => false],
'array_find_key' => ['hasSideEffects' => false],
'array_flip' => ['hasSideEffects' => false],
'array_intersect' => ['hasSideEffects' => false],
'array_intersect_assoc' => ['hasSideEffects' => false],
Expand All @@ -42,13 +47,15 @@
'array_key_last' => ['hasSideEffects' => false],
'array_key_exists' => ['hasSideEffects' => false],
'array_keys' => ['hasSideEffects' => false],
'array_map' => ['hasSideEffects' => false],
'array_merge' => ['hasSideEffects' => false],
'array_merge_recursive' => ['hasSideEffects' => false],
'array_pad' => ['hasSideEffects' => false],
'array_pop' => ['hasSideEffects' => true],
'array_product' => ['hasSideEffects' => false],
'array_push' => ['hasSideEffects' => true],
'array_rand' => ['hasSideEffects' => false],
'array_reduce' => ['hasSideEffects' => false],
'array_replace' => ['hasSideEffects' => false],
'array_replace_recursive' => ['hasSideEffects' => false],
'array_reverse' => ['hasSideEffects' => false],
Expand Down
7 changes: 7 additions & 0 deletions resources/functionMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,8 @@
'apcu_key_info' => ['hasSideEffects' => true],
'apcu_sma_info' => ['hasSideEffects' => true],
'apcu_store' => ['hasSideEffects' => true],
'array_all' => ['hasSideEffects' => false],
'array_any' => ['hasSideEffects' => false],
'array_change_key_case' => ['hasSideEffects' => false],
'array_chunk' => ['hasSideEffects' => false],
'array_column' => ['hasSideEffects' => false],
Expand All @@ -737,6 +739,9 @@
'array_diff_ukey' => ['hasSideEffects' => false],
'array_fill' => ['hasSideEffects' => false],
'array_fill_keys' => ['hasSideEffects' => false],
'array_filter' => ['hasSideEffects' => false],
'array_find' => ['hasSideEffects' => false],
'array_find_key' => ['hasSideEffects' => false],
'array_first' => ['hasSideEffects' => false],
'array_flip' => ['hasSideEffects' => false],
'array_intersect' => ['hasSideEffects' => false],
Expand All @@ -750,13 +755,15 @@
'array_key_last' => ['hasSideEffects' => false],
'array_keys' => ['hasSideEffects' => false],
'array_last' => ['hasSideEffects' => false],
'array_map' => ['hasSideEffects' => false],
'array_merge' => ['hasSideEffects' => false],
'array_merge_recursive' => ['hasSideEffects' => false],
'array_pad' => ['hasSideEffects' => false],
'array_pop' => ['hasSideEffects' => true],
'array_product' => ['hasSideEffects' => false],
'array_push' => ['hasSideEffects' => true],
'array_rand' => ['hasSideEffects' => false],
'array_reduce' => ['hasSideEffects' => false],
'array_replace' => ['hasSideEffects' => false],
'array_replace_recursive' => ['hasSideEffects' => false],
'array_reverse' => ['hasSideEffects' => false],
Expand Down
24 changes: 1 addition & 23 deletions tests/PHPStan/Analyser/nsrt/array-find-key.php
Original file line number Diff line number Diff line change
@@ -1,26 +1,4 @@
<?php

namespace {

if (!function_exists('array_find_key')) {
/**
* @param array<mixed> $array
* @param callable(mixed, array-key=): mixed $callback
* @return ?array-key
*/
function array_find_key(array $array, callable $callback)
{
foreach ($array as $key => $value) {
if ($callback($value, $key)) { // @phpstan-ignore if.condNotBoolean
return $key;
}
}

return null;
}
}

}
<?php // lint >= 8.4

namespace ArrayFindKey
{
Expand Down
24 changes: 1 addition & 23 deletions tests/PHPStan/Analyser/nsrt/array-find.php
Original file line number Diff line number Diff line change
@@ -1,26 +1,4 @@
<?php

namespace {

if (!function_exists('array_find')) {
/**
* @param array<mixed> $array
* @param callable(mixed, array-key=): mixed $callback
* @return mixed
*/
function array_find(array $array, callable $callback)
{
foreach ($array as $key => $value) {
if ($callback($value, $key)) { // @phpstan-ignore if.condNotBoolean
return $value;
}
}

return null;
}
}

}
<?php // lint >= 8.4

namespace ArrayFind
{
Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Command/data/file-without-errors.php
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<?php

array_filter([0, 1, 2]);
$a = array_filter([0, 1, 2]);
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,69 @@ public function testFirstClassCallables(): void
]);
}

public function testBug11101(): void
{
$this->analyse([__DIR__ . '/data/bug-11101.php'], [
[
'Call to function array_filter() on a separate line has no effect.',
13,
],
[
'Call to function array_map() on a separate line has no effect.',
14,
],
[
'Call to function array_reduce() on a separate line has no effect.',
15,
],
]);
}

#[RequiresPhp('>= 8.4.0')]
public function testBug11101Php84(): void
{
$this->analyse([__DIR__ . '/data/bug-11101-php84.php'], [
[
'Call to function array_find() on a separate line has no effect.',
13,
],
[
'Call to function array_find_key() on a separate line has no effect.',
14,
],
[
'Call to function array_any() on a separate line has no effect.',
15,
],
[
'Call to function array_all() on a separate line has no effect.',
16,
],
]);
}

public function testBug11101CustomPure(): void
{
$this->analyse([__DIR__ . '/data/bug-11101-custom-pure.php'], [
[
'Call to function Bug11101CustomPure\pureWithImmediateCallback() on a separate line has no effect.',
38,
],
[
'Call to function Bug11101CustomPure\pureWithLaterCallback() on a separate line has no effect.',
56,
],
[
'Call to function Bug11101CustomPure\pureWithLaterCallback() on a separate line has no effect.',
59,
],
[
'Call to function Bug11101CustomPure\pureWithLaterCallback() on a separate line has no effect.',
65,
],
]);
}

#[RequiresPhp('>= 8.5.0')]
public function testPipeOperator(): void
{
Expand Down
68 changes: 68 additions & 0 deletions tests/PHPStan/Rules/Functions/data/bug-11101-custom-pure.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Bug11101CustomPure;

/**
* @phpstan-pure
* @param array<int> $array
* @param callable(int): bool $callback
* @param-immediately-invoked-callable $callback
* @return array<int>
*/
function pureWithImmediateCallback(array $array, callable $callback): array
{
return array_filter($array, $callback);
}

/**
* @phpstan-pure
* @param array<int> $array
* @param callable(int): bool $callback
* @param-later-invoked-callable $callback
* @return array<int>
*/
function pureWithLaterCallback(array $array, callable $callback): array
{
return $array;
}

class Foo
{

/**
* @param array<int> $array
*/
public function testImmediateCallback(array $array, callable $callback): void
{
// Pure callback - should be reported
pureWithImmediateCallback($array, fn ($v) => $v > 5);

// Impure callback - should NOT be reported
pureWithImmediateCallback($array, function ($v) {
echo $v;
return $v > 0;
});

// Unknown callback - should NOT be reported
pureWithImmediateCallback($array, $callback);
}

/**
* @param array<int> $array
*/
public function testLaterCallback(array $array, callable $callback): void
{
// Pure callback - should be reported
pureWithLaterCallback($array, fn ($v) => $v > 5);

// Impure callback - should be reported (later-invoked, callback impurity doesn't matter at call site)
pureWithLaterCallback($array, function ($v) {
echo $v;
return $v > 0;
});

// Unknown callback - should be reported (later-invoked, callback impurity doesn't matter at call site)
pureWithLaterCallback($array, $callback);
}

}
19 changes: 19 additions & 0 deletions tests/PHPStan/Rules/Functions/data/bug-11101-php84.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php // lint >= 8.4

namespace Bug11101Php84;

class Foo
{

/**
* @param array<int> $array
*/
public function doFoo(array $array): void
{
array_find($array, fn ($v) => $v > 5);
array_find_key($array, fn ($v) => $v > 5);
array_any($array, fn ($v) => $v > 5);
array_all($array, fn ($v) => $v > 5);
}

}
37 changes: 37 additions & 0 deletions tests/PHPStan/Rules/Functions/data/bug-11101.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Bug11101;

class Foo
{

/**
* @param array<int> $array
*/
public function doFoo(array $array): void
{
array_filter($array, 'is_string');
array_map('is_string', $array);
array_reduce($array, function ($carry, $item) {
return $carry + $item;
}, 0);
}

/**
* @param array<int> $array
*/
public function doBar(array $array, callable $callback): void
{
// These should NOT be reported because the callback might be impure
array_filter($array, $callback);
array_map($callback, $array);
array_reduce($array, $callback, 0);

// Impure closure should not be reported
array_filter($array, function ($v) {
echo $v;
return $v > 0;
});
}

}
Loading