Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 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 Expand Up @@ -237,6 +244,8 @@
'output_reset_rewrite_vars' => ['hasSideEffects' => true],
'pclose' => ['hasSideEffects' => true],
'popen' => ['hasSideEffects' => true],
'preg_replace_callback' => ['hasSideEffects' => false],
'preg_replace_callback_array' => ['hasSideEffects' => false],
'readfile' => ['hasSideEffects' => true],
'rename' => ['hasSideEffects' => true],
'rewind' => ['hasSideEffects' => true],
Expand Down
9 changes: 9 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 Expand Up @@ -1616,6 +1623,8 @@
'preg_last_error' => ['hasSideEffects' => true],
'preg_last_error_msg' => ['hasSideEffects' => true],
'preg_quote' => ['hasSideEffects' => false],
'preg_replace_callback' => ['hasSideEffects' => false],
'preg_replace_callback_array' => ['hasSideEffects' => false],
'preg_split' => ['hasSideEffects' => false],
'property_exists' => ['hasSideEffects' => false],
'quoted_printable_decode' => ['hasSideEffects' => false],
Expand Down
16 changes: 8 additions & 8 deletions tests/PHPStan/Analyser/data/discussion-7124.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,30 +33,30 @@ function filter(array $array, ?callable $callback = null, int $mode = ARRAY_FILT

function () {
// This one does fail, as both the value + key is asked and the key + value is used
filter(
echo count(filter(
[false, true, false],
static fn (int $key, bool $value): bool => 0 === $key % 2 && $value,
mode: ARRAY_FILTER_USE_BOTH,
);
));

// This one should fail, as both the value + key is asked but only the key is used
filter(
echo count(filter(
[false, true, false],
static fn (int $key): bool => 0 === $key % 2,
mode: ARRAY_FILTER_USE_BOTH,
);
));

// This one should fail, as only the key is asked but the value is used
filter(
echo count(filter(
[false, true, false],
static fn (bool $value): bool => $value,
mode: ARRAY_FILTER_USE_KEY,
);
));

// This one should fail, as only the value is asked but the key is used
filter(
echo count(filter(
[false, true, false],
static fn (int $key): bool => 0 === $key % 2,
mode: 0,
);
));
};
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]);
echo count(array_filter([0, 1, 2]));
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,65 @@ public function testBug12224(): void
$this->analyse([__DIR__ . '/data/bug-12224.php'], []);
}

public function testBug11101(): void
{
$this->analyse([__DIR__ . '/data/bug-11101.php'], [
[
'Call to function array_filter() on a separate line has no effect.',
8,
],
[
'Call to function array_map() on a separate line has no effect.',
9,
],
[
'Call to function array_reduce() on a separate line has no effect.',
10,
],
[
'Call to function array_filter() on a separate line has no effect.',
13,
],
[
'Call to function preg_replace_callback() on a separate line has no effect.',
14,
],
[
'Call to function preg_replace_callback_array() 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_any() on a separate line has no effect.',
8,
],
[
'Call to function array_all() on a separate line has no effect.',
9,
],
[
'Call to function array_any() on a separate line has no effect.',
31,
],
]);
}

public function testBug11101PureClosure(): void
{
$this->analyse([__DIR__ . '/data/bug-11101-pure-closure.php'], [
[
'Call to function array_map() on a separate line has no effect.',
17,
],
]);
}

public function testBug4455(): void
{
require_once __DIR__ . '/data/bug-4455.php';
Expand Down
33 changes: 33 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,33 @@
<?php

namespace Bug11101Php84;

function doFoo(array $array): void
{
// pure callbacks - reported as no effect
array_any($array, 'is_string');
array_all($array, 'is_string');
}

function doBar(array $array): void
{
// impure callbacks - NOT reported
array_any($array, function ($v) {
echo $v;
return true;
});
array_all($array, function ($v) {
echo $v;
return true;
});
}

function maybeImpure(array $array): void
{
$cb = rand(0,1) ? 'is_string' : function ($v) {
echo $v;
return true;
};
array_any($array, $cb);

}
20 changes: 20 additions & 0 deletions tests/PHPStan/Rules/Functions/data/bug-11101-pure-closure.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php declare(strict_types = 1);

namespace Bug11101PureClosure;

class Foo
{

/** @param list<string> $args */
public function test(array $args): void
{
$pureFx = static function (string $v): void {};

assert(isset($args[0]));

$pureFx($args[0]);

array_map($pureFx, $args);
}

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

namespace Bug11101;

function doFoo(array $array): void
{
// pure callbacks - reported as no effect
array_filter($array, 'is_string');
array_map('is_string', $array);
array_reduce($array, function ($carry, $item) {
return $carry + $item;
}, 0);
array_filter($array);
preg_replace_callback('/\d/', 'strtoupper', 'abc');
preg_replace_callback_array(['/\d/' => 'strtoupper'], 'abc');
}

function doBar(array $array, callable $cb): void
{
// impure callbacks - NOT reported
array_filter($array, function ($v) {
echo $v;
return true;
});
array_map(function ($v) {
echo $v;
return $v;
}, $array);
array_reduce($array, function ($carry, $item) {
echo $item;
return $carry;
}, 0);
array_map('printf', $array);

// unknown callable purity - NOT reported
array_map($cb, $array);
array_filter($array, $cb);
}
6 changes: 6 additions & 0 deletions tests/PHPStan/Rules/Pure/PureMethodRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,12 @@ public function testBug12048(): void
$this->analyse([__DIR__ . '/data/bug-12048.php'], []);
}

public function testBug11100(): void
{
$this->treatPhpDocTypesAsCertain = true;
$this->analyse([__DIR__ . '/data/bug-11100.php'], []);
}

public function testBug12224(): void
{
$this->treatPhpDocTypesAsCertain = true;
Expand Down
30 changes: 30 additions & 0 deletions tests/PHPStan/Rules/Pure/data/bug-11100.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php // lint >= 8.0

namespace Bug11100;

final class FooClass
{
/**
* @param array<string, string|int> $getParams
*
* @phpstan-pure
*/
public function build(array $getParams, string $trailingSlash, bool $useSlashSeparator): string
{
if ($getParams === []) {
return '';
}

if ($useSlashSeparator) {
return '/' . implode('/', array_map(
static function (string $key, string|int $value): string {
return rawurlencode($key) . '/' . rawurlencode((string) $value);
},
array_keys($getParams),
$getParams
)) . $trailingSlash;
}

return $trailingSlash . '?' . http_build_query($getParams);
}
}
Loading