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
11 changes: 11 additions & 0 deletions src/Utils/Arrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ public static function contains(array $array, mixed $value): bool
* @param array<K, V> $array
* @param ?callable(V, K, array<K, V>): bool $predicate
* @param ?callable(): E $else
* @param-immediately-invoked-callable $predicate
* @param-immediately-invoked-callable $else
* @return ($else is null ? ?V : V|E)
*/
public static function first(array $array, ?callable $predicate = null, ?callable $else = null): mixed
Expand All @@ -149,6 +151,8 @@ public static function first(array $array, ?callable $predicate = null, ?callabl
* @param array<K, V> $array
* @param ?callable(V, K, array<K, V>): bool $predicate
* @param ?callable(): E $else
* @param-immediately-invoked-callable $predicate
* @param-immediately-invoked-callable $else
* @return ($else is null ? ?V : V|E)
*/
public static function last(array $array, ?callable $predicate = null, ?callable $else = null): mixed
Expand All @@ -166,6 +170,7 @@ public static function last(array $array, ?callable $predicate = null, ?callable
* @template V
* @param array<K, V> $array
* @param ?callable(V, K, array<K, V>): bool $predicate
* @param-immediately-invoked-callable $predicate
* @return ?K
*/
public static function firstKey(array $array, ?callable $predicate = null): int|string|null
Expand All @@ -188,6 +193,7 @@ public static function firstKey(array $array, ?callable $predicate = null): int|
* @template V
* @param array<K, V> $array
* @param ?callable(V, K, array<K, V>): bool $predicate
* @param-immediately-invoked-callable $predicate
* @return ?K
*/
public static function lastKey(array $array, ?callable $predicate = null): int|string|null
Expand Down Expand Up @@ -400,6 +406,7 @@ public static function pick(array &$array, string|int $key, mixed $default = nul
* @template V
* @param array<K, V> $array
* @param callable(V, K, array<K, V>): bool $predicate
* @param-immediately-invoked-callable $predicate
*/
public static function some(iterable $array, callable $predicate): bool
{
Expand All @@ -419,6 +426,7 @@ public static function some(iterable $array, callable $predicate): bool
* @template V
* @param array<K, V> $array
* @param callable(V, K, array<K, V>): bool $predicate
* @param-immediately-invoked-callable $predicate
*/
public static function every(iterable $array, callable $predicate): bool
{
Expand All @@ -438,6 +446,7 @@ public static function every(iterable $array, callable $predicate): bool
* @template V
* @param array<K, V> $array
* @param callable(V, K, array<K, V>): bool $predicate
* @param-immediately-invoked-callable $predicate
* @return array<K, V>
*/
public static function filter(array $array, callable $predicate): array
Expand All @@ -459,6 +468,7 @@ public static function filter(array $array, callable $predicate): array
* @template R
* @param array<K, V> $array
* @param callable(V, K, array<K, V>): R $transformer
* @param-immediately-invoked-callable $transformer
* @return array<K, R>
*/
public static function map(iterable $array, callable $transformer): array
Expand All @@ -481,6 +491,7 @@ public static function map(iterable $array, callable $transformer): array
* @template ResV
* @param array<K, V> $array
* @param callable(V, K, array<K, V>): ?array{ResK, ResV} $transformer
* @param-immediately-invoked-callable $transformer
* @return array<ResK, ResV>
*/
public static function mapWithKeys(array $array, callable $transformer): array
Expand Down
1 change: 1 addition & 0 deletions src/Utils/Callback.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ final class Callback
* @param callable-string $function
* @param list<mixed> $args
* @param callable(string, int): (bool|void|null) $onError
* @param-immediately-invoked-callable $onError
*/
public static function invokeSafe(string $function, array $args, callable $onError): mixed
{
Expand Down
1 change: 1 addition & 0 deletions src/Utils/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class Helpers
/**
* Executes a callback and returns the captured output as a string.
* @param callable(): void $func
* @param-immediately-invoked-callable $func
*/
public static function capture(callable $func): string
{
Expand Down
6 changes: 6 additions & 0 deletions src/Utils/Iterables.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public static function containsKey(iterable $iterable, mixed $key): bool
* @param iterable<K, V> $iterable
* @param ?callable(V, K, iterable<K, V>): bool $predicate
* @param ?callable(): E $else
* @param-immediately-invoked-callable $predicate
* @param-immediately-invoked-callable $else
* @return ($else is null ? ?V : V|E)
*/
public static function first(iterable $iterable, ?callable $predicate = null, ?callable $else = null): mixed
Expand All @@ -77,6 +79,8 @@ public static function first(iterable $iterable, ?callable $predicate = null, ?c
* @param iterable<K, V> $iterable
* @param ?callable(V, K, iterable<K, V>): bool $predicate
* @param ?callable(): E $else
* @param-immediately-invoked-callable $predicate
* @param-immediately-invoked-callable $else
* @return ($else is null ? ?K : K|E)
*/
public static function firstKey(iterable $iterable, ?callable $predicate = null, ?callable $else = null): mixed
Expand All @@ -96,6 +100,7 @@ public static function firstKey(iterable $iterable, ?callable $predicate = null,
* @template V
* @param iterable<K, V> $iterable
* @param callable(V, K, iterable<K, V>): bool $predicate
* @param-immediately-invoked-callable $predicate
*/
public static function some(iterable $iterable, callable $predicate): bool
{
Expand All @@ -114,6 +119,7 @@ public static function some(iterable $iterable, callable $predicate): bool
* @template V
* @param iterable<K, V> $iterable
* @param callable(V, K, iterable<K, V>): bool $predicate
* @param-immediately-invoked-callable $predicate
*/
public static function every(iterable $iterable, callable $predicate): bool
{
Expand Down
1 change: 1 addition & 0 deletions src/Utils/Process.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ public function isRunning(): bool
* continuously and kept in memory; an optional callback is invoked with each new output/error chunk.
*
* @param (\Closure(string, string): void)|null $callback
* @param-immediately-invoked-callable $callback
*/
public function wait(?\Closure $callback = null): void
{
Expand Down
1 change: 1 addition & 0 deletions src/Utils/Strings.php
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ public static function matchAll(
/**
* Replaces all matches of the regular expression $pattern, which can be a string or array in the form `pattern => replacement`.
* @param string|array<string, string> $pattern
* @param-immediately-invoked-callable $replacement
*/
public static function replace(
string $subject,
Expand Down
9 changes: 9 additions & 0 deletions tests/types/TypesTest.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,12 @@ require __DIR__ . '/../bootstrap.php';
use Nette\PHPStan\Tester\TypeAssert;

TypeAssert::assertTypes(__DIR__ . '/utils-types.php');

TypeAssert::assertErrors(
__DIR__ . '/immediately-invoked-callables.php',
array_map(
fn(int $line) => "missingType.checkedException on line $line",
[13, 14, 20, 21, 27, 33, 39, 45, 51, 57, 64, 75, 76, 82, 83, 89, 95, 101, 107, 113, 119],
),
[__DIR__ . '/immediately-invoked-callables.neon'],
);
4 changes: 4 additions & 0 deletions tests/types/immediately-invoked-callables.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
parameters:
exceptions:
check:
missingCheckedExceptionInThrows: true
133 changes: 133 additions & 0 deletions tests/types/immediately-invoked-callables.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php declare(strict_types=1);

/**
* PHPStan tests: exceptions thrown by callbacks that are invoked before the method returns
* are attributed to the caller. Run by TypesTest.phpt with immediately-invoked-callables.neon.
*/

use Nette\Utils\{Arrays, Callback, Finder, Helpers, Iterables, Process, Strings};


function testArraysFirst(): void
{
Arrays::first([1], fn() => throw new Exception);
Arrays::first([1], null, fn() => throw new Exception);
}


function testArraysLast(): void
{
Arrays::last([1], fn() => throw new Exception);
Arrays::last([1], null, fn() => throw new Exception);
}


function testArraysFirstKey(): void
{
Arrays::firstKey([1], fn() => throw new Exception);
}


function testArraysLastKey(): void
{
Arrays::lastKey([1], fn() => throw new Exception);
}


function testArraysSome(): void
{
Arrays::some([1], fn() => throw new Exception);
}


function testArraysEvery(): void
{
Arrays::every([1], fn() => throw new Exception);
}


function testArraysFilter(): void
{
Arrays::filter([1], fn() => throw new Exception);
}


function testArraysMap(): void
{
Arrays::map([1], fn() => throw new Exception);
}


/** @param array<int, int> $array */
function testArraysMapWithKeys(array $array): void
{
Arrays::mapWithKeys($array, function (int $v): array {
if ($v < 0) {
throw new Exception;
}
return [$v, $v];
});
}


function testIterablesFirst(): void
{
Iterables::first([1], fn() => throw new Exception);
Iterables::first([1], null, fn() => throw new Exception);
}


function testIterablesFirstKey(): void
{
Iterables::firstKey([1], fn() => throw new Exception);
Iterables::firstKey([1], null, fn() => throw new Exception);
}


function testIterablesSome(): void
{
Iterables::some([1], fn() => throw new Exception);
}


function testIterablesEvery(): void
{
Iterables::every([1], fn() => throw new Exception);
}


function testStringsReplace(): void
{
Strings::replace('a', '#a#', fn() => throw new Exception);
}


function testHelpersCapture(): void
{
Helpers::capture(fn() => throw new Exception);
}


function testCallbackInvokeSafe(): void
{
Callback::invokeSafe('strlen', ['a'], fn() => throw new Exception);
}


function testProcessWait(Process $process): void
{
$process->wait(fn() => throw new Exception);
}


// lazy methods store the callback and invoke it later, so the exception is not thrown by the call
function testIterablesMapIsLazy(): void
{
Iterables::map([1], fn() => throw new Exception);
}


function testFinderFilterIsLazy(): void
{
Finder::findFiles()->filter(fn() => throw new Exception);
}
Loading