diff --git a/.gitignore b/.gitignore
index 650c8a0b..92ba894c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ vendor/
composer.lock
phpcs.xml
phpunit.xml
+/lsp/
diff --git a/.travis.yml b/.travis.yml
index 4d44dfd4..c62bf6e2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,3 +18,4 @@ install:
script:
- composer tests
- composer coding-style
+ - composer types
diff --git a/composer.json b/composer.json
index fd0706b7..c70fa9af 100644
--- a/composer.json
+++ b/composer.json
@@ -15,7 +15,8 @@
}
],
"require": {
- "php": "~7"
+ "php": "~7",
+ "vimeo/psalm": "dev-master"
},
"require-dev": {
"phpunit/phpunit": "~7",
@@ -124,6 +125,7 @@
},
"scripts": {
"tests": "vendor/bin/phpunit",
+ "types": "vendor/bin/psalm",
"coding-style": "vendor/bin/phpcs && vendor/bin/php-cs-fixer fix --dry-run --diff --config=.php_cs.dist",
"clear": "rm -rf vendor/"
}
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 19bea939..d9c67cab 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -29,11 +29,6 @@
beStrictAboutOutputDuringTests="true"
verbose="true"
colors="true">
-
-
- src/Functional
-
-
@@ -42,8 +37,7 @@
-
+
@@ -53,5 +47,4 @@
-
diff --git a/psalm.xml b/psalm.xml
new file mode 100644
index 00000000..a21bb25d
--- /dev/null
+++ b/psalm.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Functional/Average.php b/src/Functional/Average.php
index b3029f34..e0569a6c 100644
--- a/src/Functional/Average.php
+++ b/src/Functional/Average.php
@@ -11,13 +11,13 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns the average of all numeric values in the array or null if no numeric value was found
*
- * @param Traversable|array $collection
- * @return null|float|int
+ * @param iterable $collection
+ * @return numeric|null
+ * @psalm-pure
*/
function average($collection)
{
@@ -28,7 +28,7 @@ function average($collection)
foreach ($collection as $element) {
if (\is_numeric($element)) {
- $sum += $element;
+ $sum = ($sum === null) ? $element : $sum + $element;
++$divisor;
}
}
diff --git a/src/Functional/ButLast.php b/src/Functional/ButLast.php
index 1899f604..23381767 100644
--- a/src/Functional/ButLast.php
+++ b/src/Functional/ButLast.php
@@ -11,13 +11,15 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns an array containing the elements of the list without its last element.
*
- * @param Traversable|array $collection
- * @return array
+ * @template TKey of array-key
+ * @template TValue of mixed
+ * @param iterable $collection
+ * @return array
+ * @psalm-pure
*/
function but_last($collection)
{
diff --git a/src/Functional/Capture.php b/src/Functional/Capture.php
index 341ca970..a324e736 100644
--- a/src/Functional/Capture.php
+++ b/src/Functional/Capture.php
@@ -13,13 +13,22 @@
/**
* Return a new function that captures the return value of $callback in $result and returns the callbacks return value
*
- * @param callable $callback
- * @param mixed $result
- * @return callable
+ * @template TArg of mixed
+ * @template TResult of mixed
+ * @param callable(...TArg): TResult $callback
+ * @param-out TResult $result
+ * @param TResult $result
+ * @return callable(...TArg): TResult
+ * @psalm-pure
*/
function capture(callable $callback, &$result)
{
- return function (...$args) use ($callback, &$result) {
+ return
+ /**
+ * @param TArg $args
+ * @return TResult
+ */
+ static function (...$args) use ($callback, &$result) {
$result = $callback(...$args);
return $result;
diff --git a/src/Functional/CompareObjectHashOn.php b/src/Functional/CompareObjectHashOn.php
index 6f59b13f..a48cc660 100644
--- a/src/Functional/CompareObjectHashOn.php
+++ b/src/Functional/CompareObjectHashOn.php
@@ -13,13 +13,23 @@
/**
* Returns a comparison function that can be used with e.g. `usort()`
*
- * @param callable $comparison A function that compares the two values. Pick e.g. strcmp() or strnatcasecmp()
- * @param callable $keyFunction A function that takes an argument and returns the value that should be compared
- * @return callable
+ * @template TCompVal of scalar
+ * @template TObjIn extends object
+ * @template TObjOut extends object
+ * @param callable(TCompVal, TCompVal): int $comparison A function that compares the two values. Pick e.g. `strcmp()` or `strnatcasecmp()`
+ * @param callable(TObjIn): TObjOut $keyFunction A function that takes an argument and returns the value that should be compared
+ * @return callable(TObjIn, TObjIn): int
+ * @psalm-pure
*/
function compare_object_hash_on(callable $comparison, callable $keyFunction = null)
{
+ /** @var callable(TObjIn): scalar $keyFunction */
$keyFunction = $keyFunction ? compose($keyFunction, 'spl_object_hash') : 'spl_object_hash';
+ /**
+ * @fixme report issue with psalm
+ * @var callable(scalar, scalar): int $comparison
+ */
+
return compare_on($comparison, $keyFunction);
}
diff --git a/src/Functional/CompareOn.php b/src/Functional/CompareOn.php
index 8f506709..e93817c1 100644
--- a/src/Functional/CompareOn.php
+++ b/src/Functional/CompareOn.php
@@ -13,19 +13,37 @@
/**
* Returns a comparison function that can be used with e.g. `usort()`
*
- * @param callable $comparison A function that compares the two values. Pick e.g. strcmp() or strnatcasecmp()
- * @param callable $reducer A function that takes an argument and returns the value that should be compared
- * @return callable
+ * @template TCompVal of scalar
+ * @template TVal
+ * @param callable(TCompVal, TCompVal): int $comparison A function that compares the two values. Pick e.g. `strcmp()` or `strnatcasecmp()`
+ * @param callable(TVal): TCompVal $reducer A function that takes an argument and returns the value that should be compared
+ * @return callable(TVal, TVal): int
+ * @fixme file psalm issue for "Argument 1 expects empty, … provided" (when not passing a reducer)
+ * @psalm-pure
*/
-function compare_on(callable $comparison, callable $reducer = null)
+function compare_on(callable $comparison, callable $reducer = null): callable
{
if ($reducer === null) {
- return static function ($left, $right) use ($comparison) {
+ return
+ /**
+ * @param TVal $left
+ * @param TVal $right
+ */
+ static function ($left, $right) use ($comparison): int {
+ /**
+ * @var TCompVal $left
+ * @var TCompVal $right
+ */
return $comparison($left, $right);
};
}
- return static function ($left, $right) use ($reducer, $comparison) {
+ return
+ /**
+ * @param TVal $left
+ * @param TVal $right
+ */
+ static function ($left, $right) use ($reducer, $comparison): int {
return $comparison($reducer($left), $reducer($right));
};
}
diff --git a/src/Functional/Compose.php b/src/Functional/Compose.php
index 99ba32db..4b6edd24 100644
--- a/src/Functional/Compose.php
+++ b/src/Functional/Compose.php
@@ -15,15 +15,23 @@
*
* @param callable ...$functions
* @return callable
- * @todo Add callable typehint when HHVM supports use of typehints with variadic arguments
- * @see https://github.com/facebook/hhvm/issues/6954
+ * @psalm-pure
*/
-function compose(...$functions)
+function compose(callable ...$functions): callable
{
return \array_reduce(
$functions,
- function ($carry, $item) {
- return function ($x) use ($carry, $item) {
+ /**
+ * @param mixed $carry
+ * @param mixed $item
+ */
+ static function ($carry, $item) {
+ return
+ /**
+ * @param mixed $x
+ * @return mixed
+ */
+ static function ($x) use ($carry, $item) {
return $item($carry($x));
};
},
diff --git a/src/Functional/Concat.php b/src/Functional/Concat.php
index 64fe8d57..0a19842c 100644
--- a/src/Functional/Concat.php
+++ b/src/Functional/Concat.php
@@ -13,8 +13,9 @@
/**
* Concatenates zero or more strings
*
- * @param string[] ...$strings
+ * @param string $strings
* @return string
+ * @psalm-pure
*/
function concat(string ...$strings)
{
diff --git a/src/Functional/ConstFunction.php b/src/Functional/ConstFunction.php
index ae32d56a..93a349c5 100644
--- a/src/Functional/ConstFunction.php
+++ b/src/Functional/ConstFunction.php
@@ -13,12 +13,14 @@
/**
* Wrap value within a function, which will return it, without any modifications.
*
- * @param mixed $value
- * @return callable
+ * @template T
+ * @param T $value
+ * @return callable(): T
+ * @psalm-pure
*/
function const_function($value)
{
- return function () use ($value) {
+ return static function () use ($value) {
return $value;
};
}
diff --git a/src/Functional/Contains.php b/src/Functional/Contains.php
index f813f504..c41f1f74 100644
--- a/src/Functional/Contains.php
+++ b/src/Functional/Contains.php
@@ -11,16 +11,17 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns true if the collection contains the given value. If the third parameter is
* true values will be compared in strict mode
*
- * @param Traversable|array $collection
- * @param mixed $value
+ * @template TLookup
+ * @param iterable $collection
+ * @param TLookup $value
* @param bool $strict
* @return bool
+ * @psalm-pure
*/
function contains($collection, $value, $strict = true)
{
diff --git a/src/Functional/Converge.php b/src/Functional/Converge.php
index 764526c7..af4f389f 100644
--- a/src/Functional/Converge.php
+++ b/src/Functional/Converge.php
@@ -16,13 +16,23 @@
* The results of each branching function are passed as arguments
* to the converging function to produce the return value.
*
- * @param callable $convergingFunction Will be invoked with the return values of all branching functions as its arguments
- * @param callable[] $branchingFunctions A list of functions
- * @return callable A flipped version of the given function
+ * @template TArg
+ * @template TBranchReturn
+ * @template TConvergeReturn
+ * @template TConvergeFn of callable(...TArg): TBranchReturn
+ * @param callable(...TBranchReturn): TConvergeReturn $convergingFunction Will be invoked with the return values of all branching functions as its arguments
+ * @param list $branchingFunctions A list of functions
+ * @return callable(...TArg): TConvergeReturn
+ * @psalm-pure
*/
-function converge($convergingFunction, array $branchingFunctions)
+function converge($convergingFunction, array $branchingFunctions): callable
{
- return function (...$values) use ($convergingFunction, $branchingFunctions) {
+ return
+ /**
+ * @param TArg $values
+ * @return TConvergeReturn
+ */
+ static function (...$values) use ($convergingFunction, $branchingFunctions) {
$result = [];
foreach ($branchingFunctions as $branchingFunction) {
diff --git a/src/Functional/Curry.php b/src/Functional/Curry.php
index 97bb835e..5717c669 100644
--- a/src/Functional/Curry.php
+++ b/src/Functional/Curry.php
@@ -18,14 +18,20 @@
* Return a curryied version of the given function. You can decide if you also
* want to curry optional parameters or not.
*
- * @param callable $function the function to curry
+ * @template TArg
+ * @template TReturn
+ * @param callable(...TArg): TReturn $function the function you want to curryg
* @param bool $required curry optional parameters ?
- * @return callable a curryied version of the given function
+ * @return callable(...TArg): callable a curryied version of the given function
+ * @return callable
+ * @psalm-pure
*/
-function curry(callable $function, $required = true)
+function curry(callable $function, bool $required = true): callable
{
+ /** @psalm-suppress ArgumentTypeCoercion */
if (\method_exists('Closure', 'fromCallable')) {
// Closure::fromCallable was introduced in PHP 7.1
+ /** @psalm-suppress InvalidArgument */
$reflection = new ReflectionFunction(Closure::fromCallable($function));
} else {
if (\is_string($function) && \strpos($function, '::', 1) !== false) {
@@ -35,6 +41,7 @@ function curry(callable $function, $required = true)
} elseif (\is_object($function) && \method_exists($function, '__invoke')) {
$reflection = new ReflectionMethod($function, '__invoke');
} else {
+ /** @psalm-suppress InvalidArgument */
$reflection = new ReflectionFunction($function);
}
}
diff --git a/src/Functional/CurryN.php b/src/Functional/CurryN.php
index f018ceca..0f9a2532 100644
--- a/src/Functional/CurryN.php
+++ b/src/Functional/CurryN.php
@@ -17,18 +17,31 @@
* If you give a smaller number you will have an error when calling the given function. If
* you give a higher number, arguments will simply be ignored.
*
+ * @template TArg
+ * @template TReturn
* @param int $count number of arguments you want to curry
- * @param callable $function the function you want to curry
- * @return callable a curryied version of the given function
+ * @param callable(...TArg): TReturn $function the function you want to curry
+ * @return callable(...TArg): callable a curryied version of the given function
+ * @return callable
+ * @psalm-pure
*/
-function curry_n($count, callable $function)
+function curry_n($count, callable $function): callable
{
- $accumulator = function (array $arguments) use ($count, $function, &$accumulator) {
- return function (...$newArguments) use ($count, $function, $arguments, $accumulator) {
+ /**
+ * @param list $arguments
+ * @return callable(...TArg): callable
+ */
+ $accumulator = static function (array $arguments) use ($count, $function, &$accumulator): callable {
+ return
+ /**
+ * @psalm-param TArg $newArguments
+ * @psalm-return callable(...TArg): callable
+ */
+ static function (...$newArguments) use ($count, $function, $arguments, $accumulator) {
$arguments = \array_merge($arguments, $newArguments);
if ($count <= \count($arguments)) {
- return \call_user_func_array($function, $arguments);
+ return $function(...$arguments);
}
return $accumulator($arguments);
diff --git a/src/Functional/Difference.php b/src/Functional/Difference.php
index 0c906f17..76e2b585 100644
--- a/src/Functional/Difference.php
+++ b/src/Functional/Difference.php
@@ -11,14 +11,14 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Takes a collection and returns the difference of all elements
*
- * @param Traversable|array $collection
- * @param integer|float $initial
- * @return integer|float
+ * @param iterable $collection
+ * @param numeric $initial
+ * @return numeric
+ * @psalm-pure
*/
function difference($collection, $initial = 0)
{
diff --git a/src/Functional/DropFirst.php b/src/Functional/DropFirst.php
index 833ac6ea..b4da4f38 100644
--- a/src/Functional/DropFirst.php
+++ b/src/Functional/DropFirst.php
@@ -11,16 +11,18 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Drop all elements from a collection until callback returns false
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return array
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
+ * @return array
+ * @psalm-pure
*/
-function drop_first($collection, callable $callback)
+function drop_first($collection, callable $callback): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/DropLast.php b/src/Functional/DropLast.php
index e6e7e6a9..fe1d8087 100644
--- a/src/Functional/DropLast.php
+++ b/src/Functional/DropLast.php
@@ -11,19 +11,20 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Drop all elements from a collection after callback returns true
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return array
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
+ * @return array
+ * @psalm-pure
*/
-function drop_last($collection, callable $callback)
+function drop_last($collection, callable $callback): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
- InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2);
$result = [];
diff --git a/src/Functional/Each.php b/src/Functional/Each.php
index 9065e44f..311dd1b0 100644
--- a/src/Functional/Each.php
+++ b/src/Functional/Each.php
@@ -11,15 +11,17 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Iterates over a collection of elements, yielding each in turn to a callback function. Each invocation of $callback
* is called with three arguments: (element, index, collection)
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return null
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): void $callback
+ * @return void
+ * @psalm-pure
*/
function each($collection, callable $callback)
{
diff --git a/src/Functional/Equal.php b/src/Functional/Equal.php
index e95d3e93..4862e097 100644
--- a/src/Functional/Equal.php
+++ b/src/Functional/Equal.php
@@ -14,12 +14,14 @@
* Performs an equal comparison
*
* @param mixed $b the value to compare to
- *
- * @return callable the function to perform the comparison
+ * @return callable(mixed): bool the function to perform the comparison
+ * @psalm-pure
*/
-function equal($b)
+function equal($b): callable
{
- return function ($a) use ($b) {
+ return
+ /** @param mixed $a */
+ static function ($a) use ($b): bool {
return $a == $b;
};
}
diff --git a/src/Functional/ErrorToException.php b/src/Functional/ErrorToException.php
index fb9693a5..c9c5c9af 100644
--- a/src/Functional/ErrorToException.php
+++ b/src/Functional/ErrorToException.php
@@ -15,16 +15,23 @@
/**
* Takes a function and returns a new function that wraps the callback and rethrows PHP errors as exception
*
- * @param callable $callback
- * @throws ErrorException Throws exception if PHP error happened
- * @return mixed
+ * @template TArg
+ * @template TReturn
+ * @param callable(...TArg): TReturn $callback
+ * @return callable(...TArg): TReturn
*/
-function error_to_exception(callable $callback)
+function error_to_exception(callable $callback): callable
{
- return function (...$arguments) use ($callback) {
+ return
+ /**
+ * @param TArg $arguments
+ * @throws ErrorException Throws exception if PHP error happened
+ * @return TReturn
+ */
+ static function (...$arguments) use ($callback) {
try {
\set_error_handler(
- static function ($level, $message, $file, $line) {
+ static function (int $level, string $message, string $file = '', int $line = 0): bool {
throw new ErrorException($message, 0, $level, $file, $line);
}
);
diff --git a/src/Functional/Every.php b/src/Functional/Every.php
index d2fcd845..a46efe6c 100644
--- a/src/Functional/Every.php
+++ b/src/Functional/Every.php
@@ -11,17 +11,19 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns true if every value in the collection passes the callback truthy test. Opposite of Functional\none().
* Callback arguments will be element, index, collection
*
- * @param Traversable|array $collection
- * @param callable|null $callback
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
* @return bool
+ * @psalm-pure
*/
-function every($collection, callable $callback = null)
+function every($collection, callable $callback = null): bool
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Exceptions/InvalidArgumentException.php b/src/Functional/Exceptions/InvalidArgumentException.php
index 5a64a3ac..6dcf6881 100644
--- a/src/Functional/Exceptions/InvalidArgumentException.php
+++ b/src/Functional/Exceptions/InvalidArgumentException.php
@@ -10,78 +10,55 @@
namespace Functional\Exceptions;
+use ArrayAccess;
+use Traversable;
+
class InvalidArgumentException extends \InvalidArgumentException
{
/**
- * @param mixed $callback
- * @param string $callee
- * @param integer $parameterPosition
+ * @param mixed $collection
+ * @param callable-string $callee
+ * @param int $parameterPosition
+ * @psalm-assert iterable $collection
* @throws InvalidArgumentException
+ * @return void
+ * @psalm-pure
*/
- public static function assertCallback($callback, $callee, $parameterPosition)
- {
- if (!\is_callable($callback)) {
- if (!\is_array($callback) && !\is_string($callback)) {
- throw new static(
- \sprintf(
- '%s() expected parameter %d to be a valid callback, no array, string, closure or functor given',
- $callee,
- $parameterPosition
- )
- );
- }
-
- $type = \gettype($callback);
- switch ($type) {
- case 'array':
- $type = 'method';
- $callback = \array_values($callback);
-
- $sep = '::';
- if (\is_object($callback[0])) {
- $callback[0] = \get_class($callback[0]);
- $sep = '->';
- }
-
- $callback = \implode($sep, $callback);
- break;
-
- default:
- $type = 'function';
- break;
- }
-
- throw new static(
- \sprintf(
- "%s() expects parameter %d to be a valid callback, %s '%s' not found or invalid %s name",
- $callee,
- $parameterPosition,
- $type,
- $callback,
- $type
- )
- );
- }
- }
-
- public static function assertCollection($collection, $callee, $parameterPosition)
+ public static function assertCollection($collection, $callee, int $parameterPosition = 0)
{
- self::assertCollectionAlike($collection, 'Traversable', $callee, $parameterPosition);
+ self::assertCollectionAlike($collection, Traversable::class, $callee, $parameterPosition);
}
- public static function assertArrayAccess($collection, $callee, $parameterPosition)
+ /**
+ * @param mixed $collection
+ * @param callable-string $callee
+ * @param int $parameterPosition
+ * @psalm-assert array|ArrayAccess $collection
+ * @throws InvalidArgumentException
+ * @return void
+ * @psalm-pure
+ */
+ public static function assertArrayAccess($collection, string $callee, int $parameterPosition = 0)
{
- self::assertCollectionAlike($collection, 'ArrayAccess', $callee, $parameterPosition);
+ self::assertCollectionAlike($collection, ArrayAccess::class, $callee, $parameterPosition);
}
- public static function assertMethodName($methodName, $callee, $parameterPosition)
+ /**
+ * @param mixed $methodName
+ * @param callable-string $callee
+ * @param int $parameterPosition
+ * @psalm-assert string $methodName
+ * @throws InvalidArgumentException
+ * @return void
+ * @psalm-pure
+ */
+ public static function assertMethodName($methodName, string $callee, int $parameterPosition = 0)
{
if (!\is_string($methodName)) {
- throw new static(
+ throw new self(
\sprintf(
- '%s() expects parameter %d to be string, %s given',
- $callee,
- $parameterPosition,
+ '%s to be string, %s given',
+ self::getErrorMessage($callee, $parameterPosition),
self::getType($methodName)
)
);
@@ -89,41 +66,24 @@ public static function assertMethodName($methodName, $callee, $parameterPosition
}
/**
- * @param mixed $propertyName
- * @param string $callee
- * @param integer $parameterPosition
+ * @param mixed $value
+ * @param callable-string $callee
+ * @param int $parameterPosition
+ * @psalm-assert int $value
* @throws InvalidArgumentException
+ * @return void
+ * @psalm-pure
*/
- public static function assertPropertyName($propertyName, $callee, $parameterPosition)
- {
- if (
- !\is_string($propertyName) &&
- !\is_int($propertyName) &&
- !\is_float($propertyName) &&
- !\is_null($propertyName)
- ) {
- throw new static(
- \sprintf(
- '%s() expects parameter %d to be a valid property name or array index, %s given',
- $callee,
- $parameterPosition,
- self::getType($propertyName)
- )
- );
- }
- }
-
- public static function assertPositiveInteger($value, $callee, $parameterPosition)
+ public static function assertPositiveInteger($value, string $callee, int $parameterPosition = 0)
{
if ((string)(int)$value !== (string)$value || $value < 0) {
$type = self::getType($value);
$type = $type === 'integer' ? 'negative integer' : $type;
- throw new static(
+ throw new self(
\sprintf(
- '%s() expects parameter %d to be positive integer, %s given',
- $callee,
- $parameterPosition,
+ '%s to be positive integer, %s given',
+ self::getErrorMessage($callee, $parameterPosition),
$type
)
);
@@ -132,56 +92,27 @@ public static function assertPositiveInteger($value, $callee, $parameterPosition
/**
* @param mixed $key
- * @param string $callee
- * @throws static
+ * @param callable-string $callee
+ * @param int $parameterPosition
+ * @psalm-assert array-key $methodName
+ * @throws InvalidArgumentException
+ * @return void
+ * @psalm-pure
*/
- public static function assertValidArrayKey($key, $callee)
+ public static function assertValidArrayKey($key, $callee, int $parameterPosition = 0)
{
$keyTypes = ['NULL', 'string', 'integer', 'double', 'boolean'];
$keyType = \gettype($key);
if (!\in_array($keyType, $keyTypes, true)) {
- throw new static(
+ throw new self(
\sprintf(
- '%s(): callback returned invalid array key of type "%s". Expected %4$s or %3$s',
- $callee,
- $keyType,
+ '%s to be a valid array key, "%s" given. Expected "%s" or "%s"',
+ self::getErrorMessage($callee, $parameterPosition),
+ self::getType($key),
\array_pop($keyTypes),
- \implode(', ', $keyTypes)
- )
- );
- }
- }
-
- public static function assertArrayKeyExists($collection, $key, $callee)
- {
- if (!isset($collection[$key])) {
- throw new static(
- \sprintf(
- '%s(): unknown key "%s"',
- $callee,
- $key
- )
- );
- }
- }
-
- /**
- * @param boolean $value
- * @param string $callee
- * @param integer $parameterPosition
- * @throws InvalidArgumentException
- */
- public static function assertBoolean($value, $callee, $parameterPosition)
- {
- if (!\is_bool($value)) {
- throw new static(
- \sprintf(
- '%s() expects parameter %d to be boolean, %s given',
- $callee,
- $parameterPosition,
- self::getType($value)
+ \implode('", "', $keyTypes)
)
);
}
@@ -189,39 +120,21 @@ public static function assertBoolean($value, $callee, $parameterPosition)
/**
* @param mixed $value
- * @param string $callee
- * @param integer $parameterPosition
- * @throws InvalidArgumentException
- */
- public static function assertInteger($value, $callee, $parameterPosition)
- {
- if (!\is_int($value)) {
- throw new static(
- \sprintf(
- '%s() expects parameter %d to be integer, %s given',
- $callee,
- $parameterPosition,
- self::getType($value)
- )
- );
- }
- }
-
- /**
- * @param integer $value
* @param integer $limit
- * @param string $callee
- * @param integer $parameterPosition
+ * @param callable-string $callee
+ * @param int $parameterPosition
+ * @psalm-assert int $value
* @throws InvalidArgumentException
+ * @return void
+ * @psalm-pure
*/
- public static function assertIntegerGreaterThanOrEqual($value, $limit, $callee, $parameterPosition)
+ public static function assertIntegerGreaterThanOrEqual($value, $limit, string $callee, int $parameterPosition = 0)
{
if (!\is_int($value) || $value < $limit) {
- throw new static(
+ throw new self(
\sprintf(
- '%s() expects parameter %d to be an integer greater than or equal to %d',
- $callee,
- $parameterPosition,
+ '%s to be an integer greater than or equal to %d',
+ self::getErrorMessage($callee, $parameterPosition),
$limit
)
);
@@ -229,30 +142,39 @@ public static function assertIntegerGreaterThanOrEqual($value, $limit, $callee,
}
/**
- * @param integer $value
+ * @param mixed $value
* @param integer $limit
- * @param string $callee
+ * @param callable-string $callee
* @param integer $parameterPosition
+ * @psalm-assert int $value
* @throws InvalidArgumentException
+ * @return void
+ * @psalm-pure
*/
- public static function assertIntegerLessThanOrEqual($value, $limit, $callee, $parameterPosition)
+ public static function assertIntegerLessThanOrEqual($value, int $limit, string $callee, int $parameterPosition = 0)
{
if (!\is_int($value) || $value > $limit) {
- throw new static(
+ throw new self(
\sprintf(
- '%s() expects parameter %d to be an integer less than or equal to %d',
- $callee,
- $parameterPosition,
+ '%s to be an integer less than or equal to %d',
+ self::getErrorMessage($callee, $parameterPosition),
$limit
)
);
}
}
- public static function assertResolvablePlaceholder(array $args, $position)
+ /**
+ * @param array $args
+ * @param int $position
+ * @throws InvalidArgumentException
+ * @return void
+ * @psalm-pure
+ */
+ public static function assertResolvablePlaceholder(array $args, int $position)
{
if (\count($args) === 0) {
- throw new static(
+ throw new self(
\sprintf('Cannot resolve parameter placeholder at position %d. Parameter stack is empty.', $position)
);
}
@@ -260,19 +182,20 @@ public static function assertResolvablePlaceholder(array $args, $position)
/**
* @param mixed $collection
- * @param string $className
- * @param string $callee
- * @param integer $parameterPosition
+ * @param class-string $className
+ * @param callable-string $callee
+ * @param int $parameterPosition
* @throws InvalidArgumentException
+ * @return void
+ * @psalm-pure
*/
- private static function assertCollectionAlike($collection, $className, $callee, $parameterPosition)
+ private static function assertCollectionAlike($collection, $className, string $callee, int $parameterPosition = 0)
{
if (!\is_array($collection) && !$collection instanceof $className) {
- throw new static(
+ throw new self(
\sprintf(
- '%s() expects parameter %d to be array or instance of %s, %s given',
- $callee,
- $parameterPosition,
+ '%s to be array or instance of %s, %s given',
+ self::getErrorMessage($callee, $parameterPosition),
$className,
self::getType($collection)
)
@@ -280,15 +203,39 @@ private static function assertCollectionAlike($collection, $className, $callee,
}
}
+ /**
+ * @param mixed $value
+ * @return class-string|string
+ * @psalm-pure
+ */
+ private static function getType($value): string
+ {
+ return \is_object($value) ? \get_class($value) : \gettype($value);
+ }
+
+ /**
+ * @param callable-string $callee
+ * @param int $parameterPosition
+ * @return string
+ * @psalm-pure
+ */
+ private static function getErrorMessage(string $callee, int $parameterPosition): string
+ {
+ $message = $parameterPosition === 0
+ ? '%1$s() expects return value'
+ : '%1$s() expects parameter %2$d';
+
+ return \sprintf($message, $callee, $parameterPosition);
+ }
+
+ /**
+ * @param mixed $value
+ * @param callable-string $callee
+ */
public static function assertNonZeroInteger($value, $callee)
{
if (!\is_int($value) || $value == 0) {
throw new static(\sprintf('%s expected parameter %d to be non-zero', $callee, $value));
}
}
-
- private static function getType($value)
- {
- return \is_object($value) ? \get_class($value) : \gettype($value);
- }
}
diff --git a/src/Functional/Exceptions/MatchException.php b/src/Functional/Exceptions/MatchException.php
index d111ff90..7f55bb10 100644
--- a/src/Functional/Exceptions/MatchException.php
+++ b/src/Functional/Exceptions/MatchException.php
@@ -12,6 +12,13 @@
class MatchException extends InvalidArgumentException
{
+ /**
+ * @param array $conditions
+ * @param callable-string $callee
+ * @throw MatchException
+ * @return void
+ * @psalm-pure
+ */
public static function assert(array $conditions, $callee)
{
foreach ($conditions as $key => $condition) {
@@ -21,10 +28,19 @@ public static function assert(array $conditions, $callee)
}
}
+ /**
+ * @param array-key $key
+ * @param mixed $condition
+ * @param callable-string $callee
+ * @psalm-assert array $condition
+ * @throw MatchException
+ * @return void
+ * @psalm-pure
+ */
private static function assertArray($key, $condition, $callee)
{
if (!\is_array($condition)) {
- throw new static(
+ throw new self(
\sprintf(
'%s() expects condition at key %d to be array, %s given',
$callee,
@@ -35,10 +51,18 @@ private static function assertArray($key, $condition, $callee)
}
}
+ /**
+ * @param array-key $key
+ * @param array $condition
+ * @param callable-string $callee
+ * @throw MatchException
+ * @return void
+ * @psalm-pure
+ */
private static function assertLength($key, $condition, $callee)
{
if (\count($condition) < 2) {
- throw new static(
+ throw new self(
\sprintf(
'%s() expects size of condition at key %d to be greater than or equals to 2, %d given',
$callee,
@@ -49,10 +73,18 @@ private static function assertLength($key, $condition, $callee)
}
}
+ /**
+ * @param array-key $key
+ * @param array $condition
+ * @param callable-string $callee
+ * @throw MatchException
+ * @return void
+ * @psalm-pure
+ */
private static function assertCallables($key, $condition, $callee)
{
if (!\is_callable($condition[0]) || !\is_callable($condition[1])) {
- throw new static(
+ throw new self(
\sprintf(
'%s() expects first two items of condition at key %d to be callables',
$callee,
diff --git a/src/Functional/False.php b/src/Functional/False.php
index cc0986b3..74928b20 100644
--- a/src/Functional/False.php
+++ b/src/Functional/False.php
@@ -11,15 +11,16 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns true if all elements of the collection are strictly false
*
- * @param Traversable|array $collection
+ * @template V of bool
+ * @param iterable $collection
* @return bool
+ * @psalm-pure
*/
-function false($collection)
+function false($collection): bool
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Falsy.php b/src/Functional/Falsy.php
index 48558e48..42fe48c5 100644
--- a/src/Functional/Falsy.php
+++ b/src/Functional/Falsy.php
@@ -16,10 +16,11 @@
/**
* Returns true if all elements of the collection evaluate to false
*
- * @param Traversable|array $collection
+ * @template V of mixed
+ * @param iterable $collection
* @return bool
*/
-function falsy($collection)
+function falsy($collection): bool
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Filter.php b/src/Functional/Filter.php
index ec207d7e..908abbb7 100644
--- a/src/Functional/Filter.php
+++ b/src/Functional/Filter.php
@@ -11,18 +11,24 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Alias of Functional\select()
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return array
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
+ * @return array
+ * @psalm-pure
*/
-function filter($collection, callable $callback)
+function filter($collection, callable $callback): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
+ /**
+ * @psalm-suppress InvalidScalarArgument
+ * @fixme report bug
+ */
return select($collection, $callback);
}
diff --git a/src/Functional/First.php b/src/Functional/First.php
index 7bb0d8e2..a10b90ea 100644
--- a/src/Functional/First.php
+++ b/src/Functional/First.php
@@ -11,16 +11,18 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Looks through each element in the collection, returning the first one that passes a truthy test (callback). The
* function returns as soon as it finds an acceptable element, and doesn't traverse the entire collection. Callback
* arguments will be element, index, collection
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return mixed
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
+ * @return V|null
+ * @psalm-pure
*/
function first($collection, callable $callback = null)
{
diff --git a/src/Functional/FirstIndexOf.php b/src/Functional/FirstIndexOf.php
index be812d5b..e07dffa6 100644
--- a/src/Functional/FirstIndexOf.php
+++ b/src/Functional/FirstIndexOf.php
@@ -11,14 +11,16 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns the first index holding specified value in the collection. Returns false if value was not found
*
- * @param Traversable|array $collection
- * @param mixed $value
- * @return mixed
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param V|callable(V, K, iterable): bool $value
+ * @return K|false
+ * @psalm-pure
*/
function first_index_of($collection, $value)
{
diff --git a/src/Functional/FlatMap.php b/src/Functional/FlatMap.php
index 8e52085a..302b5017 100644
--- a/src/Functional/FlatMap.php
+++ b/src/Functional/FlatMap.php
@@ -24,11 +24,16 @@
* then flat_map(collection, callback) will return [1,2,3,[4]]
* while flatten(map(collection, callback)) will return [1,2,3,4]
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return array
+ * @template K of array-key
+ * @template V
+ * @template TMapped
+ * @template TMappedOrIterable of TMapped|iterable
+ * @param iterable $collection
+ * @param callable(V, K, iterable): TMappedOrIterable $callback
+ * @return list
+ * @psalm-pure
*/
-function flat_map($collection, callable $callback)
+function flat_map($collection, callable $callback): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Flatten.php b/src/Functional/Flatten.php
index a8568f03..0eca2cec 100644
--- a/src/Functional/Flatten.php
+++ b/src/Functional/Flatten.php
@@ -17,8 +17,13 @@
* Takes a nested combination of collections and returns their contents as a single, flat array.
* Does not preserve indexes.
*
- * @param Traversable|array $collection
- * @return array
+ * @template K of array-key
+ * @template V
+ * @template VFlat of scalar|object
+ * @fixme express "object but not traversable"
+ * @param iterable $collection
+ * @return list
+ * @psalm-pure
*/
function flatten($collection)
{
diff --git a/src/Functional/Flip.php b/src/Functional/Flip.php
index ff59399d..8d4dca7b 100644
--- a/src/Functional/Flip.php
+++ b/src/Functional/Flip.php
@@ -15,12 +15,20 @@
*
* If one argument is provided, it is passed to the function without change.
*
- * @param callable $callback the function you want to flip
- * @return callable a flipped version of the given function
+ * @template TArg
+ * @template TReturn
+ * @param callable(...TArg): TReturn $callback the function you want to flip
+ * @return callable(...TArg): TReturn a flipped version of the given function
+ * @psalm-pure
*/
-function flip(callable $callback)
+function flip(callable $callback): callable
{
- return function () use ($callback) {
- return $callback(...\array_reverse(\func_get_args()));
+ return
+ /**
+ * @param TArg $args
+ * @return TReturn
+ */
+ static function (...$args) use ($callback) {
+ return $callback(...\array_reverse($args));
};
}
diff --git a/src/Functional/Functional.php b/src/Functional/Functional.php
index 9795044c..9e0114c5 100644
--- a/src/Functional/Functional.php
+++ b/src/Functional/Functional.php
@@ -10,6 +10,11 @@
namespace Functional;
+/**
+ * @psalm-mutation-free
+ * @psalm-external-mutation-free
+ * @psalm-immutable
+ */
final class Functional
{
diff --git a/src/Functional/GreaterThan.php b/src/Functional/GreaterThan.php
index cd6cc377..ab83d224 100644
--- a/src/Functional/GreaterThan.php
+++ b/src/Functional/GreaterThan.php
@@ -13,12 +13,18 @@
/**
* Returns true if $a is strictly greater than $b.
*
- * @param mixed $b
- * @return \Closure(mixed)
+ * @template V of numeric
+ * @param V $b
+ * @return callable(V): bool
+ * @psalm-pure
*/
-function greater_than($b)
+function greater_than($b): callable
{
- return function ($a) use ($b) {
+ return
+ /**
+ * @param V $a
+ */
+ static function ($a) use ($b): bool {
return $a > $b;
};
}
diff --git a/src/Functional/GreaterThanOrEqual.php b/src/Functional/GreaterThanOrEqual.php
index baa3da39..d6cd700f 100644
--- a/src/Functional/GreaterThanOrEqual.php
+++ b/src/Functional/GreaterThanOrEqual.php
@@ -13,12 +13,18 @@
/**
* Returns true if $a is greater than or equal to $b.
*
- * @param mixed $b
- * @return \Closure(mixed)
+ * @template V of numeric
+ * @param V $b
+ * @return callable(V): bool
+ * @psalm-pure
*/
-function greater_than_or_equal($b)
+function greater_than_or_equal($b): callable
{
- return function ($a) use ($b) {
+ return
+ /**
+ * @param V $a
+ */
+ static function ($a) use ($b): bool {
return $a >= $b;
};
}
diff --git a/src/Functional/Group.php b/src/Functional/Group.php
index 88f53011..94b953fe 100644
--- a/src/Functional/Group.php
+++ b/src/Functional/Group.php
@@ -16,11 +16,15 @@
/**
* Groups a collection by index returned by callback.
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return array
+ * @template K of array-key
+ * @template TGroup of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): TGroup $callback
+ * @return array>
+ * @psalm-pure
*/
-function group($collection, callable $callback)
+function group($collection, callable $callback): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Head.php b/src/Functional/Head.php
index c7dd66a2..60e5c8a0 100644
--- a/src/Functional/Head.php
+++ b/src/Functional/Head.php
@@ -11,18 +11,24 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Alias for Functional\first
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return mixed
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
+ * @return V|null
+ * @psalm-pure
*/
function head($collection, callable $callback = null)
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
+ /**
+ * @psalm-suppress InvalidScalarArgument
+ * @fixme report bug
+ */
return first($collection, $callback);
}
diff --git a/src/Functional/Id.php b/src/Functional/Id.php
index b4fef17d..ad5c4539 100644
--- a/src/Functional/Id.php
+++ b/src/Functional/Id.php
@@ -13,8 +13,10 @@
/**
* Return value itself, without any modifications.
*
- * @param mixed $value
- * @return mixed
+ * @template T
+ * @param T $value
+ * @return T
+ * @psalm-pure
*/
function id($value)
{
diff --git a/src/Functional/Identical.php b/src/Functional/Identical.php
index 80b1c878..a5a63e23 100644
--- a/src/Functional/Identical.php
+++ b/src/Functional/Identical.php
@@ -13,12 +13,18 @@
/**
* Returns true if $a is equal to $b, and they are of the same type.
*
- * @param mixed $b
- * @return callable
+ * @template V
+ * @param V $b
+ * @return callable(V): bool
+ * @psalm-pure
*/
-function identical($b)
+function identical($b): callable
{
- return function ($a) use ($b) {
+ return
+ /**
+ * @param V $a
+ */
+ static function ($a) use ($b): bool {
return $a === $b;
};
}
diff --git a/src/Functional/IfElse.php b/src/Functional/IfElse.php
index c099adef..92b9d64c 100644
--- a/src/Functional/IfElse.php
+++ b/src/Functional/IfElse.php
@@ -13,15 +13,21 @@
/**
* Performs an if/else condition over a value using functions as statements
*
- * @param callable $if the condition function
- * @param callable $then function to call if condition is true
- * @param callable $else function to call if condition is false
- *
- * @return mixed the return value of the given $then or $else functions
+ * @template V
+ * @template TReturn
+ * @param callable(V): bool $if the condition function
+ * @param callable(V): TReturn $then function to call if condition is true
+ * @param callable(V): TReturn $else function to call if condition is false
+ * @return callable(V): TReturn the return value of the given $then or $else functions
+ * @psalm-pure
*/
-function if_else(callable $if, callable $then, callable $else)
+function if_else(callable $if, callable $then, callable $else): callable
{
- return function ($value) use ($if, $then, $else) {
+ return
+ /**
+ * @param V $value
+ */
+ static function ($value) use ($if, $then, $else) {
return $if($value) ? $then($value) : $else($value);
};
}
diff --git a/src/Functional/IndexesOf.php b/src/Functional/IndexesOf.php
index 79135b2d..3fa56937 100644
--- a/src/Functional/IndexesOf.php
+++ b/src/Functional/IndexesOf.php
@@ -17,11 +17,14 @@
* Returns a list of array indexes, either matching the predicate or strictly equal to the the passed value. Returns an
* empty array if no values were found.
*
- * @param Traversable|array $collection
- * @param mixed|callable $value
- * @return array
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param V|callable(V, K, iterable): bool $value
+ * @return list
+ * @psalm-pure
*/
-function indexes_of($collection, $value)
+function indexes_of($collection, $value): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Intersperse.php b/src/Functional/Intersperse.php
index e5fe7ee7..bd3e1cd7 100644
--- a/src/Functional/Intersperse.php
+++ b/src/Functional/Intersperse.php
@@ -11,15 +11,16 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Insert a given value between each element of a collection.
* Indices are not preserved.
*
- * @param Traversable|array $collection
- * @param mixed $glue
- * @return array
+ * @template V
+ * @param iterable $collection
+ * @param V $glue
+ * @return list
+ * @psalm-pure
*/
function intersperse($collection, $glue)
{
diff --git a/src/Functional/Invoke.php b/src/Functional/Invoke.php
index 506fce92..7bd8bf02 100644
--- a/src/Functional/Invoke.php
+++ b/src/Functional/Invoke.php
@@ -11,16 +11,18 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Calls the method named by $methodName on each value in the collection. Any extra arguments passed to invoke will be
* forwarded on to the method invocation.
*
- * @param Traversable|array $collection
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
* @param string $methodName
* @param array $arguments
- * @return array
+ * @return array
+ * @psalm-pure
*/
function invoke($collection, $methodName, array $arguments = [])
{
diff --git a/src/Functional/InvokeFirst.php b/src/Functional/InvokeFirst.php
index a8f9183f..d6eef0d6 100644
--- a/src/Functional/InvokeFirst.php
+++ b/src/Functional/InvokeFirst.php
@@ -11,17 +11,18 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Calls the method named by $methodName on first object in the collection containing a callable method named
* $methodName. Any extra arguments passed to invoke will be forwarded on to the method invocation.
*
- * @param Traversable|array $collection
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
* @param string $methodName
* @param array $arguments
- *
* @return mixed
+ * @psalm-pure
*/
function invoke_first($collection, $methodName, array $arguments = [])
{
diff --git a/src/Functional/InvokeIf.php b/src/Functional/InvokeIf.php
index 85e3aa81..2f3e2570 100644
--- a/src/Functional/InvokeIf.php
+++ b/src/Functional/InvokeIf.php
@@ -14,11 +14,12 @@
* Calls the method named by $methodName on $object. Any extra arguments passed to invoke_if will be
* forwarded on to the method invocation. If $method is not callable on $object, $defaultValue is returned.
*
- * @param mixed $object
+ * @param object $object
* @param string $methodName
* @param array $methodArguments
* @param mixed $defaultValue
* @return mixed
+ * @psalm-pure
*/
function invoke_if($object, $methodName, array $methodArguments = [], $defaultValue = null)
{
diff --git a/src/Functional/InvokeLast.php b/src/Functional/InvokeLast.php
index 88a06c06..ce16db9a 100644
--- a/src/Functional/InvokeLast.php
+++ b/src/Functional/InvokeLast.php
@@ -10,17 +10,19 @@
namespace Functional;
-use Traversable;
use Functional\Exceptions\InvalidArgumentException;
/**
* Calls the method named by $methodName on last object in the collection containing a callable method named
* $methodName. Any extra arguments passed to invoke will be forwarded on to the method invocation.
*
- * @param Traversable|array $collection
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
* @param string $methodName
* @param array $arguments
* @return mixed
+ * @psalm-pure
*/
function invoke_last($collection, $methodName, array $arguments = [])
{
diff --git a/src/Functional/Invoker.php b/src/Functional/Invoker.php
index d7d80f4d..badb6857 100644
--- a/src/Functional/Invoker.php
+++ b/src/Functional/Invoker.php
@@ -10,7 +10,6 @@
namespace Functional;
-use Traversable;
use Functional\Exceptions\InvalidArgumentException;
/**
@@ -18,13 +17,19 @@
*
* @param string $methodName
* @param array $arguments
- * @return callable
+ * @return callable(object): mixed
+ * @psalm-pure
*/
function invoker($methodName, array $arguments = [])
{
InvalidArgumentException::assertMethodName($methodName, __FUNCTION__, 1);
- return static function ($object) use ($methodName, $arguments) {
+ return
+ /**
+ * @param object $object
+ * @return mixed
+ */
+ static function ($object) use ($methodName, $arguments) {
return $object->{$methodName}(...$arguments);
};
}
diff --git a/src/Functional/Last.php b/src/Functional/Last.php
index c49d660a..416f336f 100644
--- a/src/Functional/Last.php
+++ b/src/Functional/Last.php
@@ -17,9 +17,12 @@
* Looks through each element in the collection, returning the last one that passes a truthy test (callback).
* Callback arguments will be element, index, collection
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return mixed
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
+ * @return V|null
+ * @psalm-pure
*/
function last($collection, callable $callback = null)
{
diff --git a/src/Functional/LastIndexOf.php b/src/Functional/LastIndexOf.php
index cfb4c7e5..3febd090 100644
--- a/src/Functional/LastIndexOf.php
+++ b/src/Functional/LastIndexOf.php
@@ -11,14 +11,16 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns the last index holding specified value in the collection. Returns false if value was not found
*
- * @param Traversable|array $collection
- * @param mixed $value
- * @return mixed
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param V|callable(V, K, iterable): bool $value
+ * @return K|false
+ * @psalm-pure
*/
function last_index_of($collection, $value)
{
diff --git a/src/Functional/LessThan.php b/src/Functional/LessThan.php
index c5ff4af3..4a4d22b8 100644
--- a/src/Functional/LessThan.php
+++ b/src/Functional/LessThan.php
@@ -13,12 +13,18 @@
/**
* Returns true if $a is strictly less than $b.
*
- * @param mixed $b
- * @return \Closure(mixed)
+ * @template V of numeric
+ * @param V $b
+ * @return callable(V): bool
+ * @psalm-pure
*/
-function less_than($b)
+function less_than($b): callable
{
- return function ($a) use ($b) {
+ return
+ /**
+ * @param V $a
+ */
+ static function ($a) use ($b): bool {
return $a < $b;
};
}
diff --git a/src/Functional/LessThanOrEqual.php b/src/Functional/LessThanOrEqual.php
index dd2e9fe9..9951d579 100644
--- a/src/Functional/LessThanOrEqual.php
+++ b/src/Functional/LessThanOrEqual.php
@@ -13,12 +13,18 @@
/**
* Returns true if $a is less than or equal to $b.
*
- * @param mixed $b
- * @return \Closure(mixed)
+ * @template V of numeric
+ * @param V $b
+ * @return callable(V): bool
+ * @psalm-pure
*/
-function less_than_or_equal($b)
+function less_than_or_equal($b): callable
{
- return function ($a) use ($b) {
+ return
+ /**
+ * @param V $a
+ */
+ static function ($a) use ($b): bool {
return $a <= $b;
};
}
diff --git a/src/Functional/LexicographicCompare.php b/src/Functional/LexicographicCompare.php
index 9b056710..2c2bfbd8 100644
--- a/src/Functional/LexicographicCompare.php
+++ b/src/Functional/LexicographicCompare.php
@@ -14,12 +14,18 @@
* Returns an integer less than, equal to, or greater than zero when
* $a is respectively less than, equal to, or greater than $b.
*
- * @param mixed $b
- * @return \Closure(mixed)
+ * @template V
+ * @param V $b
+ * @return callable(V): int
+ * @psalm-pure
*/
-function lexicographic_compare($b)
+function lexicographic_compare($b): callable
{
- return function ($a) use ($b) {
+ return
+ /**
+ * @param V $a
+ */
+ static function ($a) use ($b): int {
return $a <=> $b;
};
}
diff --git a/src/Functional/Map.php b/src/Functional/Map.php
index a4b1fa8f..15d12d55 100644
--- a/src/Functional/Map.php
+++ b/src/Functional/Map.php
@@ -11,17 +11,20 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Produces a new array of elements by mapping each element in collection through a transformation function (callback).
* Callback arguments will be element, index, collection
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return array
+ * @template K of array-key
+ * @template V
+ * @template TMapped
+ * @param iterable $collection
+ * @param callable(V, K, iterable): TMapped $callback
+ * @return array
+ * @psalm-pure
*/
-function map($collection, callable $callback)
+function map($collection, callable $callback): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Match.php b/src/Functional/Match.php
index 83d3b2a3..ce299d0b 100644
--- a/src/Functional/Match.php
+++ b/src/Functional/Match.php
@@ -19,15 +19,21 @@
/**
* Performs an operation checking for the given conditions
*
- * @param array $conditions the conditions to check against
- *
- * @return callable|null the function that calls the callable of the first truthy condition
+ * @template V
+ * @template TReturn
+ * @param array> $conditions the conditions to check against
+ * @return callable(V): TReturn the function that calls the callable of the first truthy condition
+ * @psalm-pure
*/
-function match(array $conditions)
+function match(array $conditions): callable
{
MatchException::assert($conditions, __FUNCTION__);
- return function ($value) use ($conditions) {
+ return
+ /**
+ * @param V $value
+ */
+ static function ($value) use ($conditions) {
if (empty($conditions)) {
return null;
}
diff --git a/src/Functional/Maximum.php b/src/Functional/Maximum.php
index f2c4596f..44dbb2cb 100644
--- a/src/Functional/Maximum.php
+++ b/src/Functional/Maximum.php
@@ -11,13 +11,13 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns the maximum value of a collection
*
- * @param Traversable|array $collection
- * @return integer|float
+ * @param iterable $collection
+ * @return numeric|null
+ * @psalm-pure
*/
function maximum($collection)
{
diff --git a/src/Functional/Memoize.php b/src/Functional/Memoize.php
index 4a65bf50..b974b85b 100644
--- a/src/Functional/Memoize.php
+++ b/src/Functional/Memoize.php
@@ -15,10 +15,12 @@
/**
* Memoizes callbacks and returns their value instead of calling them
*
- * @param callable|null $callback Callable closure or function. Pass null to reset memory
- * @param array $arguments Arguments
+ * @template TArgs
+ * @template TReturn
+ * @param callable(...TArgs): TReturn|null $callback Callable closure or function. Pass null to reset memory
+ * @param TArgs|callable(): string $arguments Arguments
* @param array|string $key Optional memoize key to override the auto calculated hash
- * @return mixed
+ * @return TReturn
*/
function memoize(callable $callback = null, $arguments = [], $key = null)
{
@@ -39,7 +41,9 @@ function memoize(callable $callback = null, $arguments = [], $key = null)
static $keyGenerator = null;
if (!$keyGenerator) {
- $keyGenerator = function ($value) use (&$keyGenerator) {
+
+ /** @psalm-suppress MissingClosureParamType */
+ $keyGenerator = static function ($value) use (&$keyGenerator): string {
$type = \gettype($value);
if ($type === 'array') {
$key = \join(':', map($value, $keyGenerator));
@@ -54,6 +58,7 @@ function memoize(callable $callback = null, $arguments = [], $key = null)
}
if ($key === null) {
+ /** @var list $arguments */
$key = $keyGenerator(\array_merge([$callback], $arguments));
} elseif (\is_callable($key)) {
$key = $keyGenerator($key());
@@ -62,6 +67,7 @@ function memoize(callable $callback = null, $arguments = [], $key = null)
}
if (!isset($storage[$key]) && !\array_key_exists($key, $storage)) {
+ /** @var list $arguments */
$storage[$key] = $callback(...$arguments);
}
diff --git a/src/Functional/Minimum.php b/src/Functional/Minimum.php
index d0d67fb8..78cbda73 100644
--- a/src/Functional/Minimum.php
+++ b/src/Functional/Minimum.php
@@ -11,13 +11,13 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns the minimum value of a collection
*
- * @param Traversable|array $collection
- * @return integer|float
+ * @param iterable $collection
+ * @return numeric|null
+ * @psalm-pure
*/
function minimum($collection)
{
diff --git a/src/Functional/None.php b/src/Functional/None.php
index 34704737..4f842b14 100644
--- a/src/Functional/None.php
+++ b/src/Functional/None.php
@@ -11,17 +11,19 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns true if all of the elements in the collection pass the callback falsy test. Opposite of Functional\all().
* Callback arguments will be element, index, collection.
*
- * @param Traversable|array $collection
- * @param callable|null $callback
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
* @return bool
+ * @psalm-pure
*/
-function none($collection, callable $callback = null)
+function none($collection, callable $callback = null): bool
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Noop.php b/src/Functional/Noop.php
index ece00c2a..45cfe682 100644
--- a/src/Functional/Noop.php
+++ b/src/Functional/Noop.php
@@ -26,6 +26,9 @@
/**
* A no-operation function.
+ *
+ * @return void|null|mixed
+ * @psalm-pure
*/
function noop()
{
diff --git a/src/Functional/Not.php b/src/Functional/Not.php
index 97a5abce..c8e44d10 100644
--- a/src/Functional/Not.php
+++ b/src/Functional/Not.php
@@ -13,12 +13,18 @@
/**
* Logical negation of the given $function
*
- * @param callable $function The function to run value against
- * @return callable A negation version on the given $function
+ * @template V
+ * @param callable(V): bool $function The function to run value against
+ * @return callable(V): bool A negation version on the given $function
+ * @psalm-pure
*/
-function not(callable $function)
+function not(callable $function): callable
{
- return function ($value) use ($function) {
+ return
+ /**
+ * @param V $value
+ */
+ static function ($value) use ($function): bool {
return !$function($value);
};
}
diff --git a/src/Functional/OmitKeys.php b/src/Functional/OmitKeys.php
index 506cbebe..af3b7e91 100644
--- a/src/Functional/OmitKeys.php
+++ b/src/Functional/OmitKeys.php
@@ -16,9 +16,12 @@
/**
* Returns an array with the specified keys omitted from the array
*
- * @param Traversable|array $collection
- * @param array $keys
- * @return array
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param list $keys
+ * @return array
+ * @psalm-pure
*/
function omit_keys($collection, array $keys)
{
@@ -30,5 +33,8 @@ function omit_keys($collection, array $keys)
$array = $collection;
}
- return \array_diff_key($array, \array_flip($keys));
+ /** @var array $list */
+ $list = \array_diff_key($array, \array_flip($keys));
+
+ return $list;
}
diff --git a/src/Functional/PartialAny.php b/src/Functional/PartialAny.php
index 172adefb..eec9d581 100644
--- a/src/Functional/PartialAny.php
+++ b/src/Functional/PartialAny.php
@@ -17,13 +17,21 @@
*
* Use Functional\…, Functional\…() or Functional\placeholder() as a placeholder
*
- * @param callable $callback
- * @param mixed ...$arguments
- * @return callable
+ * @template TArgs
+ * @template TArgsOrPlaceholder
+ * @template TReturn
+ * @param callable(...TArgsOrPlaceholder, ...TArgs): TReturn $callback
+ * @param TArgsOrPlaceholder $arguments
+ * @return callable(...TArgs): TReturn
+ * @psalm-pure
*/
-function partial_any(callable $callback, ...$arguments)
+function partial_any(callable $callback, ...$arguments): callable
{
- return function (...$innerArguments) use ($callback, $arguments) {
+ return
+ /**
+ * @param TArgs $innerArguments
+ */
+ static function (...$innerArguments) use ($callback, $arguments) {
$placeholder = …();
foreach ($arguments as $position => &$argument) {
@@ -37,9 +45,10 @@ function partial_any(callable $callback, ...$arguments)
};
}
-/** @return resource */
-function …()
+/** @psalm-pure */
+function …(): string
{
+ /** @psalm-suppress ImpureStaticVariable */
static $placeholder;
if (!$placeholder) {
@@ -49,9 +58,8 @@ function …()
return $placeholder;
}
-
-/** @return resource */
-function placeholder()
+/** @psalm-pure */
+function placeholder(): string
{
return …();
}
diff --git a/src/Functional/PartialLeft.php b/src/Functional/PartialLeft.php
index 6ab523c2..771dcaab 100644
--- a/src/Functional/PartialLeft.php
+++ b/src/Functional/PartialLeft.php
@@ -13,15 +13,22 @@
/**
* Return a new function with the arguments partially applied starting from the left side
*
- * Use Functional\…, Functional\…() or Functional\placeholder() as a placeholder
- *
- * @param callable $callback
- * @param array ...$arguments
- * @return callable
+ * @template TArg1
+ * @template TArg2
+ * @template TReturn
+ * @param callable(...TArg1, ...TArg2): TReturn $callback
+ * @param TArg1 $arguments
+ * @return callable(...TArg2): TReturn
+ * @psalm-pure
*/
-function partial_left(callable $callback, ...$arguments)
+function partial_left(callable $callback, ...$arguments): callable
{
- return function (...$innerArguments) use ($callback, $arguments) {
+ return
+ /**
+ * @param TArg2 $innerArguments
+ */
+ static function (...$innerArguments) use ($callback, $arguments) {
+ /** @var TArg1 $arguments */
return $callback(...\array_merge($arguments, $innerArguments));
};
}
diff --git a/src/Functional/PartialMethod.php b/src/Functional/PartialMethod.php
index 9415a59f..5531e635 100644
--- a/src/Functional/PartialMethod.php
+++ b/src/Functional/PartialMethod.php
@@ -18,13 +18,18 @@
* @param string $methodName
* @param array $arguments
* @param mixed $defaultValue
- * @return callable
+ * @return callable(object): mixed
+ * @psalm-pure
*/
-function partial_method($methodName, array $arguments = [], $defaultValue = null)
+function partial_method(string $methodName, array $arguments = [], $defaultValue = null): callable
{
InvalidArgumentException::assertMethodName($methodName, __FUNCTION__, 1);
- return function ($object) use ($methodName, $arguments, $defaultValue) {
+ return
+ /**
+ * @param object $object
+ */
+ static function ($object) use ($methodName, $arguments, $defaultValue) {
if (!\is_callable([$object, $methodName])) {
return $defaultValue;
}
diff --git a/src/Functional/PartialRight.php b/src/Functional/PartialRight.php
index 6c0ac3fb..c3d181d5 100644
--- a/src/Functional/PartialRight.php
+++ b/src/Functional/PartialRight.php
@@ -13,13 +13,22 @@
/**
* Return a new function with the arguments partially applied starting from the right
*
- * @param callable $callback
- * @param array ...$arguments
- * @return callable
+ * @template TArg1
+ * @template TArg2
+ * @template TReturn
+ * @param callable(...TArg1, ...TArg2): TReturn $callback
+ * @param TArg2 $arguments
+ * @return callable(...TArg1): TReturn
+ * @psalm-pure
*/
-function partial_right(callable $callback, ...$arguments)
+function partial_right(callable $callback, ...$arguments): callable
{
- return function (...$innerArguments) use ($callback, $arguments) {
+ return
+ /**
+ * @param TArg1 $innerArguments
+ */
+ static function (...$innerArguments) use ($callback, $arguments) {
+ /** @var TArg2 $arguments */
return $callback(...\array_merge($innerArguments, $arguments));
};
}
diff --git a/src/Functional/Partition.php b/src/Functional/Partition.php
index 83340754..281c241d 100644
--- a/src/Functional/Partition.php
+++ b/src/Functional/Partition.php
@@ -22,11 +22,14 @@
* Elements are not re-ordered and have the same index they had in the
* original array.
*
- * @param Traversable|array $collection
- * @param callable ...$callbacks
- * @return array
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callbacks
+ * @return array>
+ * @psalm-pure
*/
-function partition($collection, callable ...$callbacks)
+function partition($collection, callable ...$callbacks): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Pick.php b/src/Functional/Pick.php
index 8c840aa0..4d0dd497 100644
--- a/src/Functional/Pick.php
+++ b/src/Functional/Pick.php
@@ -17,11 +17,15 @@
* Pick a single element from a collection of objects or arrays by index.
* If no such index exists, return the default value.
*
- * @param ArrayAccess|array $collection
- * @param mixed $index
- * @param mixed $default
- * @param callable $callback Custom function to check if index exists, default function is "isset"
- * @return mixed
+ * @template TCollection of ArrayAccess|array
+ * @template TIndex of array-key
+ * @template TDefault
+ * @param TCollection $collection
+ * @param TIndex $index
+ * @param TDefault $default
+ * @param callable(TCollection, TIndex): bool $callback Custom function to check if index exists, default function is "isset"
+ * @return TCollection[TIndex]|TDefault
+ * @psalm-pure
*/
function pick($collection, $index, $default = null, callable $callback = null)
{
diff --git a/src/Functional/Pluck.php b/src/Functional/Pluck.php
index b687f01c..ebf750b1 100644
--- a/src/Functional/Pluck.php
+++ b/src/Functional/Pluck.php
@@ -12,19 +12,19 @@
use ArrayAccess;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Extract a property from a collection of objects.
*
- * @param Traversable|array $collection
- * @param string $propertyName
- * @return array
+ * @param iterable $collection
+ * @param array-key $propertyName
+ * @return array
+ * @psalm-pure
*/
function pluck($collection, $propertyName)
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
- InvalidArgumentException::assertPropertyName($propertyName, __FUNCTION__, 2);
+ InvalidArgumentException::assertValidArrayKey($propertyName, __FUNCTION__, 2);
$aggregation = [];
diff --git a/src/Functional/Poll.php b/src/Functional/Poll.php
index ca48fcb3..9b4f51e6 100644
--- a/src/Functional/Poll.php
+++ b/src/Functional/Poll.php
@@ -19,13 +19,13 @@
/**
* Retry a callback until it returns a truthy value or the timeout (in microseconds) is reached
*
- * @param callable $callback
+ * @param callable(int, int): mixed $callback Callback receives retry count and delay and returns truthy value on success
* @param integer $timeout Timeout in microseconds
- * @param Traversable|null $delaySequence Default: no delay between calls
+ * @param iterable $delaySequence Default: no delay between calls
* @throws InvalidArgumentException
- * @return boolean
+ * @return mixed|false Truthy value on success, false on timeout
*/
-function poll(callable $callback, $timeout, Traversable $delaySequence = null)
+function poll(callable $callback, int $timeout, iterable $delaySequence = null)
{
InvalidArgumentException::assertIntegerGreaterThanOrEqual($timeout, 0, __FUNCTION__, 2);
@@ -33,7 +33,12 @@ function poll(callable $callback, $timeout, Traversable $delaySequence = null)
$delays = new AppendIterator();
if ($delaySequence) {
- $delays->append(new InfiniteIterator($delaySequence));
+ /** @psalm-suppress ArgumentTypeCoercion */
+ $delays->append(
+ new InfiniteIterator(
+ $delaySequence instanceof Traversable ? $delaySequence : new ArrayIterator($delaySequence)
+ )
+ );
}
$delays->append(new InfiniteIterator(new ArrayIterator([0])));
@@ -56,4 +61,7 @@ function poll(callable $callback, $timeout, Traversable $delaySequence = null)
++$retry;
}
+
+ // Never reached because of InfiniteIterator
+ return false;
}
diff --git a/src/Functional/Product.php b/src/Functional/Product.php
index ccd3a87e..f9ee5830 100644
--- a/src/Functional/Product.php
+++ b/src/Functional/Product.php
@@ -11,14 +11,14 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Takes a collection and returns the product of all elements
*
- * @param Traversable|array $collection
- * @param integer|float $initial
- * @return integer|float
+ * @param iterable $collection
+ * @param numeric $initial
+ * @return numeric
+ * @psalm-pure
*/
function product($collection, $initial = 1)
{
diff --git a/src/Functional/Ratio.php b/src/Functional/Ratio.php
index 85b690c9..3b8e20d4 100644
--- a/src/Functional/Ratio.php
+++ b/src/Functional/Ratio.php
@@ -11,14 +11,14 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Takes a collection and returns the quotient of all elements
*
- * @param Traversable|array $collection
- * @param integer|float $initial
- * @return integer|float
+ * @param iterable $collection
+ * @param numeric $initial
+ * @return numeric
+ * @psalm-pure
*/
function ratio($collection, $initial = 1)
{
diff --git a/src/Functional/ReduceLeft.php b/src/Functional/ReduceLeft.php
index e86d9b6d..25f81c6d 100644
--- a/src/Functional/ReduceLeft.php
+++ b/src/Functional/ReduceLeft.php
@@ -14,10 +14,14 @@
use Traversable;
/**
- * @param Traversable|array $collection
- * @param callable $callback
- * @param mixed $initial
- * @return mixed
+ * @template K of array-key
+ * @template V
+ * @template R
+ * @param iterable $collection
+ * @param callable(V, K, iterable, R): R $callback
+ * @param R $initial
+ * @return R
+ * @psalm-pure
*/
function reduce_left($collection, callable $callback, $initial = null)
{
diff --git a/src/Functional/ReduceRight.php b/src/Functional/ReduceRight.php
index 05148322..dac08119 100644
--- a/src/Functional/ReduceRight.php
+++ b/src/Functional/ReduceRight.php
@@ -11,13 +11,16 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
- * @param Traversable|array $collection
- * @param callable $callback
- * @param mixed $initial
- * @return mixed
+ * @template K of array-key
+ * @template V
+ * @template R
+ * @param iterable $collection
+ * @param callable(V, K, iterable, R): R $callback
+ * @param R $initial
+ * @return R
+ * @psalm-pure
*/
function reduce_right($collection, callable $callback, $initial = null)
{
diff --git a/src/Functional/Reindex.php b/src/Functional/Reindex.php
index 7c5dbcc1..31a2789e 100644
--- a/src/Functional/Reindex.php
+++ b/src/Functional/Reindex.php
@@ -17,11 +17,15 @@
* Produces a new array of elements by assigning the values to keys generated by a transformation function (callback).
* Callback arguments will be element, index, collection
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return array
+ * @template K of array-key
+ * @template K2 of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): K2 $callback
+ * @return array
+ * @psalm-pure
*/
-function reindex($collection, callable $callback)
+function reindex($collection, callable $callback): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Reject.php b/src/Functional/Reject.php
index 1a3bbe40..c7eae58d 100644
--- a/src/Functional/Reject.php
+++ b/src/Functional/Reject.php
@@ -17,11 +17,14 @@
* Returns the elements in list without the elements that the truthy test (callback) passes. The opposite of
* Functional\select(). Callback arguments will be element, index, collection
*
- * @param Traversable|array $collection
- * @param callable|null $callback
- * @return array
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
+ * @return array
+ * @psalm-pure
*/
-function reject($collection, callable $callback = null)
+function reject($collection, callable $callback = null): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Repeat.php b/src/Functional/Repeat.php
index 4d810271..a47436fe 100644
--- a/src/Functional/Repeat.php
+++ b/src/Functional/Repeat.php
@@ -16,13 +16,18 @@
/**
* Creates a function that can be used to repeat the execution of $callback.
*
- * @param callable $callback
- *
- * @return Closure
+ * @param callable(): void $callback
+ * @return callable(int): void
+ * @psalm-pure
*/
-function repeat(callable $callback)
+function repeat(callable $callback): callable
{
- return function ($times) use ($callback) {
+ return
+ /**
+ * @param int $times
+ * @return void
+ */
+ static function (int $times) use ($callback) {
InvalidArgumentException::assertPositiveInteger($times, __FUNCTION__, 1);
for ($i = 0; $i < $times; $i++) {
diff --git a/src/Functional/Retry.php b/src/Functional/Retry.php
index 02a74bf6..6382ee4c 100644
--- a/src/Functional/Retry.php
+++ b/src/Functional/Retry.php
@@ -34,6 +34,7 @@ function retry(callable $callback, $retries, Traversable $delaySequence = null)
if ($delaySequence) {
$delays = new AppendIterator();
+ /** @psalm-suppress ArgumentTypeCoercion */
$delays->append(new InfiniteIterator($delaySequence));
$delays->append(new InfiniteIterator(new ArrayIterator([0])));
$delays = new LimitIterator($delays, $retries);
diff --git a/src/Functional/Select.php b/src/Functional/Select.php
index 1e6b59ec..66c4901e 100644
--- a/src/Functional/Select.php
+++ b/src/Functional/Select.php
@@ -11,17 +11,19 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Looks through each element in the list, returning an array of all the elements that pass a truthy test (callback).
* Opposite is Functional\reject(). Callback arguments will be element, index, collection
*
- * @param Traversable|array $collection
- * @param callable|null $callback
- * @return array
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
+ * @return array
+ * @psalm-pure
*/
-function select($collection, callable $callback = null)
+function select($collection, callable $callback = null): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/SelectKeys.php b/src/Functional/SelectKeys.php
index e7eec114..7a84fc88 100644
--- a/src/Functional/SelectKeys.php
+++ b/src/Functional/SelectKeys.php
@@ -16,11 +16,14 @@
/**
* Select the specified keys from the array
*
- * @param Traversable|array $collection
- * @param array $keys
- * @return array
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param list $keys
+ * @return array
+ * @psalm-pure
*/
-function select_keys($collection, array $keys)
+function select_keys($collection, array $keys): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
@@ -30,5 +33,8 @@ function select_keys($collection, array $keys)
$array = $collection;
}
- return \array_intersect_key($array, \array_flip($keys));
+ /** @var array $list */
+ $list = \array_intersect_key($array, \array_flip($keys));
+
+ return $list;
}
diff --git a/src/Functional/SequenceConstant.php b/src/Functional/SequenceConstant.php
index f21dc70a..18bdb766 100644
--- a/src/Functional/SequenceConstant.php
+++ b/src/Functional/SequenceConstant.php
@@ -13,15 +13,15 @@
use ArrayIterator;
use Functional\Exceptions\InvalidArgumentException;
use InfiniteIterator;
-use Traversable;
/**
* Returns an infinite, traversable sequence of constant values
*
* @param integer $value
- * @return Traversable
+ * @return iterable
+ * @psalm-pure
*/
-function sequence_constant($value)
+function sequence_constant(int $value): iterable
{
InvalidArgumentException::assertIntegerGreaterThanOrEqual($value, 0, __FUNCTION__, 1);
diff --git a/src/Functional/SequenceExponential.php b/src/Functional/SequenceExponential.php
index 899d7c1b..08137329 100644
--- a/src/Functional/SequenceExponential.php
+++ b/src/Functional/SequenceExponential.php
@@ -16,12 +16,13 @@
/**
* Returns an infinite, traversable sequence that exponentially grows by given percentage
*
- * @param integer $start
- * @param integer $percentage Integer between 1 and 100
- * @return ExponentialSequence
+ * @param int $start
+ * @param int $percentage Integer between 1 and 100
+ * @return iterable
* @throws InvalidArgumentException
+ * @psalm-pure
*/
-function sequence_exponential($start, $percentage)
+function sequence_exponential(int $start, int $percentage): iterable
{
InvalidArgumentException::assertIntegerGreaterThanOrEqual($start, 1, __METHOD__, 1);
InvalidArgumentException::assertIntegerGreaterThanOrEqual($percentage, 1, __METHOD__, 2);
diff --git a/src/Functional/SequenceLinear.php b/src/Functional/SequenceLinear.php
index 052ce51b..d66dbc00 100644
--- a/src/Functional/SequenceLinear.php
+++ b/src/Functional/SequenceLinear.php
@@ -12,19 +12,18 @@
use Functional\Exceptions\InvalidArgumentException;
use Functional\Sequences\LinearSequence;
-use Traversable;
/**
* Returns an infinite, traversable sequence that linearly grows by given amount
*
- * @param integer $start
- * @param integer $amount
- * @return Traversable
+ * @param int $start
+ * @param int $amount
+ * @return iterable
+ * @psalm-pure
*/
-function sequence_linear($start, $amount)
+function sequence_linear(int $start, int $amount): iterable
{
InvalidArgumentException::assertIntegerGreaterThanOrEqual($start, 0, __FUNCTION__, 1);
- InvalidArgumentException::assertInteger($amount, __FUNCTION__, 2);
return new LinearSequence($start, $amount);
}
diff --git a/src/Functional/Sequences/ExponentialSequence.php b/src/Functional/Sequences/ExponentialSequence.php
index d70b9340..3b53c1c5 100644
--- a/src/Functional/Sequences/ExponentialSequence.php
+++ b/src/Functional/Sequences/ExponentialSequence.php
@@ -13,21 +13,34 @@
use Functional\Exceptions\InvalidArgumentException;
use Iterator;
+/**
+ * @psalm-external-mutation-free
+ */
class ExponentialSequence implements Iterator
{
- /** @var integer */
+ /**
+ * @psalm-readonly
+ * @var integer
+ */
private $start;
- /** @var integer */
+ /**
+ * @var integer
+ * @psalm-readonly
+ */
private $percentage;
- /** @var integer */
- private $value;
+ /**
+ * @var integer
+ */
+ private $value = 0;
- /** @var integer */
- private $times;
+ /**
+ * @var integer
+ */
+ private $times = 0;
- public function __construct($start, $percentage)
+ public function __construct(int $start, int $percentage)
{
InvalidArgumentException::assertIntegerGreaterThanOrEqual($start, 1, __METHOD__, 1);
InvalidArgumentException::assertIntegerGreaterThanOrEqual($percentage, 1, __METHOD__, 2);
@@ -37,23 +50,34 @@ public function __construct($start, $percentage)
$this->percentage = $percentage;
}
- public function current()
+ /**
+ * @psalm-mutation-free
+ */
+ public function current(): int
{
return $this->value;
}
public function next()
{
- $this->value = (int) \round(\pow($this->start * (1 + $this->percentage / 100), $this->times));
+ $this->value = (int) \round(($this->start * (1 + $this->percentage / 100)) ** $this->times);
$this->times++;
}
+ /**
+ * @psalm-pure
+ * @psalm-mutation-free
+ */
public function key()
{
return null;
}
- public function valid()
+ /**
+ * @psalm-pure
+ * @psalm-mutation-free
+ */
+ public function valid(): bool
{
return true;
}
diff --git a/src/Functional/Sequences/LinearSequence.php b/src/Functional/Sequences/LinearSequence.php
index 7ba59d99..53ddc677 100644
--- a/src/Functional/Sequences/LinearSequence.php
+++ b/src/Functional/Sequences/LinearSequence.php
@@ -13,27 +13,38 @@
use Functional\Exceptions\InvalidArgumentException;
use Iterator;
+/**
+ * @psalm-external-mutation-free
+ */
class LinearSequence implements Iterator
{
- /** @var integer */
+ /**
+ * @psalm-readonly
+ * @var integer
+ */
private $start;
- /** @var integer */
+ /**
+ * @psalm-readonly
+ * @var integer
+ */
private $amount;
/** @var integer */
- private $value;
+ private $value = 0;
- public function __construct($start, $amount)
+ public function __construct(int $start, int $amount)
{
InvalidArgumentException::assertIntegerGreaterThanOrEqual($start, 0, __METHOD__, 1);
- InvalidArgumentException::assertInteger($amount, __METHOD__, 2);
$this->start = $start;
$this->amount = $amount;
}
- public function current()
+ /**
+ * @psalm-mutation-free
+ */
+ public function current(): int
{
return $this->value;
}
@@ -43,12 +54,20 @@ public function next()
$this->value += $this->amount;
}
+ /**
+ * @psalm-pure
+ * @psalm-mutation-free
+ */
public function key()
{
- return 0;
+ return null;
}
- public function valid()
+ /**
+ * @psalm-pure
+ * @psalm-mutation-free
+ */
+ public function valid(): bool
{
return true;
}
diff --git a/src/Functional/Some.php b/src/Functional/Some.php
index 5583dac4..ad6a2052 100644
--- a/src/Functional/Some.php
+++ b/src/Functional/Some.php
@@ -17,11 +17,14 @@
* Returns true if some of the elements in the collection pass the callback truthy test. Short-circuits and stops
* traversing the collection if a truthy element is found. Callback arguments will be value, index, collection
*
- * @param Traversable|array $collection
- * @param callable|null $callback
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
* @return bool
+ * @psalm-pure
*/
-function some($collection, callable $callback = null)
+function some($collection, callable $callback = null): bool
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Sort.php b/src/Functional/Sort.php
index 1706b921..91e64ab5 100644
--- a/src/Functional/Sort.php
+++ b/src/Functional/Sort.php
@@ -16,15 +16,17 @@
/**
* Sorts a collection with a user-defined function, optionally preserving array keys
*
- * @param Traversable|array $collection
- * @param callable $callback
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, V, iterable): int $callback
* @param bool $preserveKeys
- * @return array
+ * @return list|array
+ * @psalm-pure
*/
-function sort($collection, callable $callback, $preserveKeys = false)
+function sort($collection, callable $callback, bool $preserveKeys = false): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
- InvalidArgumentException::assertBoolean($preserveKeys, __FUNCTION__, 3);
if ($collection instanceof Traversable) {
$array = \iterator_to_array($collection);
@@ -34,9 +36,17 @@ function sort($collection, callable $callback, $preserveKeys = false)
$fn = $preserveKeys ? 'uasort' : 'usort';
- $fn($array, function ($left, $right) use ($callback, $collection) {
- return $callback($left, $right, $collection);
- });
+ $fn(
+ $array,
+ /**
+ * @param V $left
+ * @param V $right
+ */
+ static function ($left, $right) use ($callback, $collection) {
+ /** @var iterable $collection */
+ return $callback($left, $right, $collection);
+ }
+ );
return $array;
}
diff --git a/src/Functional/Sum.php b/src/Functional/Sum.php
index f79f5de5..6797ebdb 100644
--- a/src/Functional/Sum.php
+++ b/src/Functional/Sum.php
@@ -11,14 +11,14 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Takes a collection and returns the sum of the elements
*
- * @param Traversable|array $collection
- * @param integer|float $initial
- * @return integer|float
+ * @param iterable $collection
+ * @param numeric $initial
+ * @return numeric
+ * @psalm-pure
*/
function sum($collection, $initial = 0)
{
diff --git a/src/Functional/SuppressError.php b/src/Functional/SuppressError.php
index 06a18ca8..213524f2 100644
--- a/src/Functional/SuppressError.php
+++ b/src/Functional/SuppressError.php
@@ -12,20 +12,25 @@
use ErrorException;
-use function Functional\const_function;
-
/**
* Takes a function and returns a new function that wraps the callback and suppresses the PHP error
*
- * @param callable $callback
- * @throws ErrorException Throws exception if PHP error happened
- * @return mixed
+ * @template TArg
+ * @template TReturn
+ * @param callable(...TArg): TReturn $callback
+ * @return callable(...TArg): TReturn
*/
-function suppress_error(callable $callback)
+function suppress_error(callable $callback): callable
{
- return function (...$arguments) use ($callback) {
+ return
+ /**
+ * @param TArg $arguments
+ * @return TReturn
+ */
+ static function (...$arguments) use ($callback) {
try {
- \set_error_handler(const_function(null));
+ \set_error_handler(static function () {
+ });
return $callback(...$arguments);
} finally {
diff --git a/src/Functional/Tail.php b/src/Functional/Tail.php
index e390d8ea..b3ddc5ee 100644
--- a/src/Functional/Tail.php
+++ b/src/Functional/Tail.php
@@ -11,17 +11,19 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns all items from $collection except first element (head). Preserves $collection keys.
* Takes an optional callback for filtering the collection.
*
- * @param Traversable|array $collection
- * @param callable $callback
- * @return array
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
+ * @param callable(V, K, iterable): bool $callback
+ * @return array
+ * @psalm-pure
*/
-function tail($collection, callable $callback = null)
+function tail($collection, callable $callback = null): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/TailRecursion.php b/src/Functional/TailRecursion.php
index 0ce4f43a..aabac087 100644
--- a/src/Functional/TailRecursion.php
+++ b/src/Functional/TailRecursion.php
@@ -16,16 +16,24 @@
* I took the solution from here https://gist.github.com/beberlei/4145442
* but reworked it and made without classes.
*
- * @param callable $fn
- * @return callable
+ * @template TArg
+ * @template TReturn
+ * @param callable(...TArg): TReturn $fn
+ * @return callable(...TArg): TReturn
+ * @psalm-pure
*/
function tail_recursion(callable $fn): callable
{
$underCall = false;
$queue = [];
- return function (...$args) use (&$fn, &$underCall, &$queue) {
+ return
+ /**
+ * @param TArg $args
+ */
+ static function (...$args) use (&$fn, &$underCall, &$queue) {
$result = null;
$queue[] = $args;
+
if (!$underCall) {
$underCall = true;
while ($head = \array_shift($queue)) {
@@ -33,6 +41,7 @@ function tail_recursion(callable $fn): callable
}
$underCall = false;
}
+
return $result;
};
}
diff --git a/src/Functional/TakeLeft.php b/src/Functional/TakeLeft.php
index 961009f3..f5e3d148 100644
--- a/src/Functional/TakeLeft.php
+++ b/src/Functional/TakeLeft.php
@@ -17,12 +17,14 @@
* Creates a slice of $collection with $count elements taken from the beginning. If the collection has less than $count
* elements, the whole collection will be returned as an array.
*
- * @param Traversable|array $collection
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
* @param int $count
- *
- * @return array
+ * @return array
+ * @psalm-pure
*/
-function take_left($collection, $count)
+function take_left($collection, int $count): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
InvalidArgumentException::assertPositiveInteger($count, __FUNCTION__, 2);
diff --git a/src/Functional/TakeRight.php b/src/Functional/TakeRight.php
index 94a8118a..d34392d4 100644
--- a/src/Functional/TakeRight.php
+++ b/src/Functional/TakeRight.php
@@ -19,13 +19,14 @@
* This function will reorder and reset the integer array indices by default. This behaviour can be changed by setting
* preserveKeys to TRUE. String keys are always preserved, regardless of this parameter.
*
- * @param Traversable|array $collection
+ * @template K of array-key
+ * @template V
+ * @param iterable $collection
* @param int $count
- * @param bool $preserveKeys
- *
- * @return array
+ * @return array
+ * @psalm-pure
*/
-function take_right($collection, $count, $preserveKeys = false)
+function take_right($collection, $count, bool $preserveKeys = false)
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
InvalidArgumentException::assertPositiveInteger($count, __FUNCTION__, 2);
diff --git a/src/Functional/Tap.php b/src/Functional/Tap.php
index f0c5d3a2..ac8063d3 100644
--- a/src/Functional/Tap.php
+++ b/src/Functional/Tap.php
@@ -13,9 +13,11 @@
/**
* Call the given Closure with the given value, then return the value.
*
- * @param mixed $value
- * @param callable $callback
- * @return mixed
+ * @template V
+ * @param V $value
+ * @param callable(V): void $callback
+ * @return V
+ * @psalm-pure
*/
function tap($value, callable $callback)
{
diff --git a/src/Functional/True.php b/src/Functional/True.php
index 6a7f0d8f..534fb73d 100644
--- a/src/Functional/True.php
+++ b/src/Functional/True.php
@@ -11,15 +11,15 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns true if all elements of the collection are strictly true
*
- * @param Traversable|array $collection
+ * @param iterable $collection
* @return bool
+ * @psalm-pure
*/
-function true($collection)
+function true($collection): bool
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Truthy.php b/src/Functional/Truthy.php
index 93c0877a..56256bba 100644
--- a/src/Functional/Truthy.php
+++ b/src/Functional/Truthy.php
@@ -16,10 +16,12 @@
/**
* Returns true if all elements of the collection evaluate to true
*
- * @param Traversable|array $collection
+ * @template V of mixed
+ * @param iterable $collection
* @return bool
+ * @psalm-pure
*/
-function truthy($collection)
+function truthy($collection): bool
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/Unique.php b/src/Functional/Unique.php
index 238d8264..0df78ecf 100644
--- a/src/Functional/Unique.php
+++ b/src/Functional/Unique.php
@@ -11,17 +11,20 @@
namespace Functional;
use Functional\Exceptions\InvalidArgumentException;
-use Traversable;
/**
* Returns an array of unique elements
*
- * @param Traversable|array $collection
- * @param callable $callback
+ * @template K of array-key
+ * @template V
+ * @template I
+ * @param iterable $collection
+ * @param callable(V, K, iterable): I $callback
* @param bool $strict
- * @return array
+ * @return array
+ * @psalm-pure
*/
-function unique($collection, callable $callback = null, $strict = true)
+function unique($collection, callable $callback = null, bool $strict = true): array
{
InvalidArgumentException::assertCollection($collection, __FUNCTION__, 1);
diff --git a/src/Functional/With.php b/src/Functional/With.php
index ec858829..bf8bf40d 100644
--- a/src/Functional/With.php
+++ b/src/Functional/With.php
@@ -23,8 +23,6 @@
*/
function with($value, callable $callback, $invokeValue = true, $default = null)
{
- InvalidArgumentException::assertCallback($callback, __FUNCTION__, 2);
-
if ($value === null) {
return $default;
}
diff --git a/src/Functional/ZipAll.php b/src/Functional/ZipAll.php
index 2c7658b8..80002e0f 100644
--- a/src/Functional/ZipAll.php
+++ b/src/Functional/ZipAll.php
@@ -27,6 +27,7 @@ function zip_all(...$args)
/** @var callable|null $callback */
$callback = null;
if (\is_callable(\end($args))) {
+ /** @var callable $callback */
$callback = \array_pop($args);
}
diff --git a/tests/Functional/Exceptions/InvalidArgumentExceptionTest.php b/tests/Functional/Exceptions/InvalidArgumentExceptionTest.php
index 2de9a7bc..a9d6d190 100644
--- a/tests/Functional/Exceptions/InvalidArgumentExceptionTest.php
+++ b/tests/Functional/Exceptions/InvalidArgumentExceptionTest.php
@@ -14,46 +14,6 @@
class InvalidArgumentExceptionTest extends TestCase
{
- public function testCallbackExceptionWithUndefinedStaticMethod()
- {
- $this->expectException('Functional\Exceptions\InvalidArgumentException');
- $this->expectExceptionMessage("func() expects parameter 1 to be a valid callback, method 'stdClass::method' not found or invalid method name");
-
- InvalidArgumentException::assertCallback(['stdClass', 'method'], 'func', 1);
- }
-
- public function testCallbackExceptionWithUndefinedFunction()
- {
- $this->expectException('Functional\Exceptions\InvalidArgumentException');
- $this->expectExceptionMessage("func() expects parameter 1 to be a valid callback, function 'undefinedFunction' not found or invalid function name");
-
- InvalidArgumentException::assertCallback('undefinedFunction', 'func', 1);
- }
-
- public function testCallbackExceptionWithUndefinedMethod()
- {
- $this->expectException('Functional\Exceptions\InvalidArgumentException');
- $this->expectExceptionMessage("func() expects parameter 2 to be a valid callback, method 'stdClass->method' not found or invalid method name");
-
- InvalidArgumentException::assertCallback([new \stdClass(), 'method'], 'func', 2);
- }
-
- public function testCallbackExceptionWithIncorrectArrayIndex()
- {
- $this->expectException('Functional\Exceptions\InvalidArgumentException');
- $this->expectExceptionMessage("func() expects parameter 1 to be a valid callback, method 'stdClass->method' not found or invalid method name");
-
- InvalidArgumentException::assertCallback([1 => new \stdClass(), 2 => 'method'], 'func', 1);
- }
-
- public function testCallbackExceptionWithObject()
- {
- $this->expectException('Functional\Exceptions\InvalidArgumentException');
- $this->expectExceptionMessage('func() expected parameter 1 to be a valid callback, no array, string, closure or functor given');
-
- InvalidArgumentException::assertCallback(new \stdClass(), 'func', 1);
- }
-
public function testExceptionIfStringIsPassedAsList()
{
$this->expectException('Functional\Exceptions\InvalidArgumentException');
@@ -99,14 +59,14 @@ public function testExceptionIfInvalidMethodName()
InvalidArgumentException::assertMethodName(new \stdClass(), "foo", 2);
}
- public function testExceptionIfInvalidPropertyName()
+ public function testExceptionIfInvalidArrayKey()
{
- InvalidArgumentException::assertPropertyName('property', 'func', 2);
- InvalidArgumentException::assertPropertyName(0, 'func', 2);
- InvalidArgumentException::assertPropertyName(0.2, 'func', 2);
+ InvalidArgumentException::assertValidArrayKey('property', 'func', 2);
+ InvalidArgumentException::assertValidArrayKey(0, 'func', 2);
+ InvalidArgumentException::assertValidArrayKey(0.2, 'func', 2);
$this->expectException('Functional\Exceptions\InvalidArgumentException');
- $this->expectExceptionMessage('func() expects parameter 2 to be a valid property name or array index, stdClass given');
- InvalidArgumentException::assertPropertyName(new \stdClass(), "func", 2);
+ $this->expectExceptionMessage('func() expects parameter 2 to be a valid array key, "stdClass" given. Expected "boolean" or "NULL", "string", "integer", "double"');
+ InvalidArgumentException::assertValidArrayKey(new \stdClass(), "func", 2);
}
public function testNoExceptionThrownWithPositiveInteger()
@@ -128,32 +88,4 @@ public function testExceptionIfStringInsteadOfPositiveInteger()
$this->expectExceptionMessage('func() expects parameter 2 to be positive integer, string given');
InvalidArgumentException::assertPositiveInteger('str', 'func', 2);
}
-
- public function testAssertIntegerAccessWithString()
- {
- $this->expectException('Functional\Exceptions\InvalidArgumentException');
- $this->expectExceptionMessage('func() expects parameter 4 to be integer, string given');
- InvalidArgumentException::assertInteger('string', "func", 4);
- }
-
- public function testAssertIntegerAccessWithObject()
- {
- $this->expectException('Functional\Exceptions\InvalidArgumentException');
- $this->expectExceptionMessage('func() expects parameter 4 to be integer, stdClass given');
- InvalidArgumentException::assertInteger(new \stdClass(), "func", 4);
- }
-
- public function testAssertBooleanAccessWithString()
- {
- $this->expectException('Functional\Exceptions\InvalidArgumentException');
- $this->expectExceptionMessage('func() expects parameter 4 to be boolean, string given');
- InvalidArgumentException::assertBoolean('string', "func", 4);
- }
-
- public function testAssertBooleanAccessWithObject()
- {
- $this->expectException('Functional\Exceptions\InvalidArgumentException');
- $this->expectExceptionMessage('func() expects parameter 4 to be boolean, stdClass given');
- InvalidArgumentException::assertBoolean(new \stdClass(), "func", 4);
- }
}
diff --git a/tests/Functional/GroupTest.php b/tests/Functional/GroupTest.php
index 41a0198f..acc3921b 100644
--- a/tests/Functional/GroupTest.php
+++ b/tests/Functional/GroupTest.php
@@ -57,9 +57,9 @@ public function testExceptionIsThrownWhenCallbacksReturnsInvalidKey()
$invalidTypes = [
- 'resource' => stream_context_create(),
- 'object' => new \stdClass(),
- 'array' => []
+ 'resource' => stream_context_create(),
+ 'stdClass' => new \stdClass(),
+ 'array' => [],
];
foreach ($invalidTypes as $type => $value) {
@@ -70,7 +70,7 @@ public function testExceptionIsThrownWhenCallbacksReturnsInvalidKey()
} catch (\Exception $e) {
$this->assertSame(
sprintf(
- 'Functional\group(): callback returned invalid array key of type "%s". Expected NULL, string, integer, double or boolean',
+ 'Functional\group() expects return value to be a valid array key, "%s" given. Expected "boolean" or "NULL", "string", "integer", "double"',
$type
),
$e->getMessage()
diff --git a/tests/Functional/MatchTest.php b/tests/Functional/MatchTest.php
index 323fc1bb..e52fb187 100644
--- a/tests/Functional/MatchTest.php
+++ b/tests/Functional/MatchTest.php
@@ -18,14 +18,19 @@ class MatchTest extends AbstractTestCase
{
public function testMatch()
{
- $test = match([
- [equal('foo'), const_function('is foo')],
- [equal('bar'), const_function('is bar')],
- [equal('baz'), const_function('is baz')],
- [const_function(true), function ($x) {
- return 'default is ' . $x;
- }],
- ]);
+ $test = match(
+ [
+ [equal('foo'), const_function('is foo')],
+ [equal('bar'), const_function('is bar')],
+ [equal('baz'), const_function('is baz')],
+ [
+ const_function(true),
+ function ($x) {
+ return 'default is ' . $x;
+ },
+ ],
+ ]
+ );
$this->assertEquals('is foo', $test('foo'));
$this->assertEquals('is bar', $test('bar'));
@@ -35,10 +40,12 @@ public function testMatch()
public function testNothingMatch()
{
- $test = match([
- [equal('foo'), const_function('is foo')],
- [equal('bar'), const_function('is bar')],
- ]);
+ $test = match(
+ [
+ [equal('foo'), const_function('is foo')],
+ [equal('bar'), const_function('is bar')],
+ ]
+ );
$this->assertNull($test('baz'));
}
@@ -50,36 +57,46 @@ public function testMatchConditionIsArray()
$callable = function () {
};
- $test = match([
- [$callable, $callable],
- '',
- ]);
+ $test = match(
+ [
+ [$callable, $callable],
+ '',
+ ]
+ );
}
public function testMatchConditionLength()
{
- $this->expectArgumentError('Functional\match() expects size of condition at key 1 to be greater than or equals to 2, 1 given');
+ $this->expectArgumentError(
+ 'Functional\match() expects size of condition at key 1 to be greater than or equals to 2, 1 given'
+ );
$callable = function () {
};
- $test = match([
- [$callable, $callable],
- [''],
- ]);
+ $test = match(
+ [
+ [$callable, $callable],
+ [''],
+ ]
+ );
}
public function testMatchConditionCallables()
{
$this->expectException(\Functional\Exceptions\InvalidArgumentException::class);
- $this->expectExceptionMessage('Functional\match() expects first two items of condition at key 1 to be callables');
+ $this->expectExceptionMessage(
+ 'Functional\match() expects first two items of condition at key 1 to be callables'
+ );
$callable = function () {
};
- $test = match([
- [$callable, $callable],
- [$callable, ''],
- ]);
+ $test = match(
+ [
+ [$callable, $callable],
+ [$callable, ''],
+ ]
+ );
}
}
diff --git a/tests/Functional/PartialMethodTest.php b/tests/Functional/PartialMethodTest.php
index d5cff3c1..e5fdbcc0 100644
--- a/tests/Functional/PartialMethodTest.php
+++ b/tests/Functional/PartialMethodTest.php
@@ -44,12 +44,6 @@ public function testWithNonObjectAndDefaultValue()
$this->assertSame('defaultValue', $method('non-object'));
}
- public function testWithInvalidMethodName()
- {
- $this->expectArgumentError('Functional\partial_method() expects parameter 1 to be string, integer given');
- partial_method(1);
- }
-
public function execute($arg1 = null, $arg2 = null)
{
return $arg1 ? $arg1 . ' ' . $arg2 : 'default';
diff --git a/tests/Functional/PluckTest.php b/tests/Functional/PluckTest.php
index a36e1f6f..f204a377 100644
--- a/tests/Functional/PluckTest.php
+++ b/tests/Functional/PluckTest.php
@@ -212,7 +212,7 @@ public function testPassNoCollection()
public function testPassNoPropertyName()
{
- $this->expectArgumentError('Functional\pluck() expects parameter 2 to be a valid property name or array index, stdClass given');
+ $this->expectArgumentError('Functional\pluck() expects parameter 2 to be a valid array key, "stdClass" given. Expected "boolean" or "NULL", "string", "integer", "double"');
pluck($this->propertyExistsSomewhere, new \stdClass());
}
diff --git a/tests/Functional/PollTest.php b/tests/Functional/PollTest.php
index 1e0dec93..1f0c54d6 100644
--- a/tests/Functional/PollTest.php
+++ b/tests/Functional/PollTest.php
@@ -11,7 +11,7 @@
namespace Functional\Tests;
use ArrayIterator;
-use PHPUnit_Framework_MockObject_MockObject as MockObject;
+use PHPUnit\Framework\MockObject\MockObject;
use function Functional\poll;
diff --git a/tests/Functional/SequenceLinearTest.php b/tests/Functional/SequenceLinearTest.php
index a3478f65..b71fe011 100644
--- a/tests/Functional/SequenceLinearTest.php
+++ b/tests/Functional/SequenceLinearTest.php
@@ -39,12 +39,4 @@ public function testArgumentMustBePositiveInteger()
);
sequence_linear(-1, 1);
}
-
- public function testAmountArgumentMustBeInteger()
- {
- $this->expectArgumentError(
- 'Functional\sequence_linear() expects parameter 2 to be integer'
- );
- sequence_linear(0, 1.1);
- }
}
diff --git a/tests/Functional/TailRecursionTest.php b/tests/Functional/TailRecursionTest.php
index ec8b20c7..86daafd6 100644
--- a/tests/Functional/TailRecursionTest.php
+++ b/tests/Functional/TailRecursionTest.php
@@ -19,12 +19,12 @@ class TailRecursionTest extends TestCase
public function testTailRecursion1()
{
$fact = tail_recursion(function ($n, $acc = 1) use (&$fact) {
- if ($n == 0) {
+ if ($n === 0) {
return $acc;
}
return $fact($n - 1, $acc * $n);
});
- $this->assertEquals(3628800, $fact(10));
+ $this->assertEquals(INF, $fact(100000));
}
public function testTailRecursion2()