diff --git a/src/Iterators/CachingIterator.php b/src/Iterators/CachingIterator.php index 4c2830cb..30fc54fa 100644 --- a/src/Iterators/CachingIterator.php +++ b/src/Iterators/CachingIterator.php @@ -21,7 +21,7 @@ * @property-read bool $empty * @property-read bool $odd * @property-read bool $even - * @property-read int $counter + * @property-read int<0,max> $counter * @property-read TKey $nextKey * @property-read TValue $nextValue */ @@ -29,6 +29,7 @@ class CachingIterator extends \CachingIterator implements \Countable { use Nette\SmartObject; + /** @var int<0, max> */ private int $counter = 0; @@ -78,6 +79,7 @@ public function isEven(): bool } + /** @return int<0, max> */ public function getCounter(): int { return $this->counter; diff --git a/src/Utils/ArrayHash.php b/src/Utils/ArrayHash.php index cb19b363..8b19a1c3 100644 --- a/src/Utils/ArrayHash.php +++ b/src/Utils/ArrayHash.php @@ -47,6 +47,7 @@ public function &getIterator(): \Iterator } + /** @return int<0, max> */ public function count(): int { return count((array) $this); diff --git a/src/Utils/ArrayList.php b/src/Utils/ArrayList.php index 7852ce81..bbd4129e 100644 --- a/src/Utils/ArrayList.php +++ b/src/Utils/ArrayList.php @@ -50,6 +50,7 @@ public function &getIterator(): \Iterator } + /** @return int<0, max> */ public function count(): int { return count($this->list); diff --git a/src/Utils/Arrays.php b/src/Utils/Arrays.php index 51916a60..7ebbf9e5 100644 --- a/src/Utils/Arrays.php +++ b/src/Utils/Arrays.php @@ -95,6 +95,7 @@ public static function mergeTree(array $array1, array $array2): array /** * Returns zero-indexed position of given array key. Returns null if key is not found. * @param array $array + * @return ?int<0, max> */ public static function getKeyOffset(array $array, string|int $key): ?int { @@ -104,6 +105,7 @@ public static function getKeyOffset(array $array, string|int $key): ?int /** * @param array $array + * @return ?int<0, max> * @deprecated use getKeyOffset() */ public static function searchKey(array $array, string|int $key): ?int @@ -253,8 +255,9 @@ public static function renameKey(array &$array, string|int $oldKey, string|int $ /** * Returns only those array items, which matches a regular expression $pattern. - * @param string[] $array - * @return string[] + * @template K of array-key + * @param array $array + * @return array */ public static function grep( array $array, @@ -271,7 +274,7 @@ public static function grep( /** * Transforms multidimensional array to flat array. * @param array $array - * @return array + * @return ($preserveKeys is true ? array : list) */ public static function flatten(array $array, bool $preserveKeys = false): array { @@ -557,8 +560,9 @@ public static function toKey(mixed $value): int|string /** * Returns a copy of $array where every item is cast to string and wrapped with $prefix and $suffix. - * @param string[] $array - * @return string[] + * @template K of array-key + * @param array $array + * @return array */ public static function wrap(array $array, string $prefix = '', string $suffix = ''): array { diff --git a/src/Utils/Random.php b/src/Utils/Random.php index f3ec874c..62f0c127 100644 --- a/src/Utils/Random.php +++ b/src/Utils/Random.php @@ -23,6 +23,7 @@ final class Random /** * Generates a random string of given length from characters specified in second argument. * Supports intervals, such as `0-9` or `A-Z`. + * @return non-empty-string */ public static function generate(int $length = 10, string $charlist = '0-9a-z'): string { diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index d12d042e..b352f9a1 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -292,6 +292,7 @@ public static function indent(string $s, int $level = 1, string $chars = "\t"): /** * Converts all characters of UTF-8 string to lower case. + * @return ($s is non-empty-string ? non-empty-string : string) */ public static function lower(string $s): string { @@ -301,6 +302,7 @@ public static function lower(string $s): string /** * Converts the first character of a UTF-8 string to lower case and leaves the other characters unchanged. + * @return ($s is non-empty-string ? non-empty-string : string) */ public static function firstLower(string $s): string { @@ -310,6 +312,7 @@ public static function firstLower(string $s): string /** * Converts all characters of a UTF-8 string to upper case. + * @return ($s is non-empty-string ? non-empty-string : string) */ public static function upper(string $s): string { @@ -319,6 +322,7 @@ public static function upper(string $s): string /** * Converts the first character of a UTF-8 string to upper case and leaves the other characters unchanged. + * @return ($s is non-empty-string ? non-empty-string : string) */ public static function firstUpper(string $s): string { @@ -328,6 +332,7 @@ public static function firstUpper(string $s): string /** * Converts the first character of every word of a UTF-8 string to upper case and the others to lower case. + * @return ($s is non-empty-string ? non-empty-string : string) */ public static function capitalize(string $s): string { @@ -389,6 +394,7 @@ public static function findPrefix(array $strings): string /** * Returns number of characters (not bytes) in UTF-8 string. * That is the number of Unicode code points which may differ from the number of graphemes. + * @return ($s is non-empty-string ? int<1, max> : 0) */ public static function length(string $s): int { @@ -436,6 +442,7 @@ public static function padRight(string $s, int $length, string $pad = ' '): stri /** * Reverses UTF-8 string. + * @return ($s is non-empty-string ? non-empty-string : string) */ public static function reverse(string $s): string { @@ -479,6 +486,7 @@ public static function after(string $haystack, string $needle, int $nth = 1): ?s /** * Returns position in characters of $nth occurence of $needle in $haystack or null if the $needle was not found. * Negative value of `$nth` means searching from the end. + * @return ?int<0, max> */ public static function indexOf(string $haystack, string $needle, int $nth = 1): ?int { diff --git a/src/Utils/Validators.php b/src/Utils/Validators.php index 2c583e75..18025f39 100644 --- a/src/Utils/Validators.php +++ b/src/Utils/Validators.php @@ -252,6 +252,7 @@ public static function isCallable(mixed $value): bool /** * Checks if the value is a valid UTF-8 string. + * @phpstan-assert-if-true string $value */ public static function isUnicode(mixed $value): bool { diff --git a/tests/Iterators/CachingIterator.basic.phpt b/tests/Iterators/CachingIterator.basic.phpt index 9bdf0847..a50b3e66 100644 --- a/tests/Iterators/CachingIterator.basic.phpt +++ b/tests/Iterators/CachingIterator.basic.phpt @@ -69,3 +69,17 @@ test('', function () { Assert::same(0, $iterator->getCounter()); Assert::true($iterator->isEmpty()); }); + + +test('magic properties', function () { + $iterator = new Iterators\CachingIterator(['a' => 'Nette', 'b' => 'Framework']); + $iterator->rewind(); + Assert::true($iterator->first); + Assert::false($iterator->last); + Assert::false($iterator->empty); + Assert::true($iterator->odd); + Assert::false($iterator->even); + Assert::same(1, $iterator->counter); + Assert::same('b', $iterator->nextKey); + Assert::same('Framework', $iterator->nextValue); +}); diff --git a/tests/types/utils-types.php b/tests/types/utils-types.php index 50a81ddf..ce6aecd5 100644 --- a/tests/types/utils-types.php +++ b/tests/types/utils-types.php @@ -6,7 +6,7 @@ */ use Nette\Iterators\CachingIterator; -use Nette\Utils\{ArrayHash, Arrays, Helpers, Html, Iterables, Type, Validators}; +use Nette\Utils\{ArrayHash, ArrayList, Arrays, Helpers, Html, Iterables, Random, Strings, Type, Validators}; use function PHPStan\Testing\assertType; @@ -252,3 +252,87 @@ function testIterablesFirstKeyWithElse(iterable $iterable): void { assertType('string', Iterables::firstKey($iterable, else: fn() => 'default')); } + + +/** @param non-empty-string $nonEmpty */ +function testStringsLength(string $string, string $nonEmpty): void +{ + assertType('int<1, max>', Strings::length('abc')); + assertType('int<1, max>', Strings::length($nonEmpty)); + assertType('0', Strings::length('')); + assertType('int<0, max>', Strings::length($string)); +} + + +/** @param non-empty-string $nonEmpty */ +function testStringsCaseConversion(string $string, string $nonEmpty): void +{ + assertType('non-empty-string', Strings::lower($nonEmpty)); + assertType('non-empty-string', Strings::firstLower($nonEmpty)); + assertType('non-empty-string', Strings::upper('abc')); + assertType('non-empty-string', Strings::firstUpper($nonEmpty)); + assertType('non-empty-string', Strings::capitalize($nonEmpty)); + assertType('non-empty-string', Strings::reverse($nonEmpty)); + assertType('string', Strings::lower($string)); + assertType('string', Strings::reverse($string)); +} + + +function testStringsIndexOf(string $string): void +{ + assertType('int<0, max>|null', Strings::indexOf($string, 'x')); +} + + +function testRandomGenerate(int $length): void +{ + assertType('non-empty-string', Random::generate()); + assertType('non-empty-string', Random::generate($length, 'a-z')); +} + + +/** @param array $array */ +function testArraysGetKeyOffset(array $array): void +{ + assertType('int<0, max>|null', Arrays::getKeyOffset($array, 'key')); +} + + +/** @param array $array */ +function testArraysGrepAndWrap(array $array): void +{ + assertType('array', Arrays::grep($array, '#x#')); + assertType('array', Arrays::wrap($array, '<', '>')); + assertType('array<3|5, string>', Arrays::wrap([3 => 'a', 5 => 'b'])); +} + + +/** @param array $array */ +function testArraysFlatten(array $array, bool $preserveKeys): void +{ + assertType('list', Arrays::flatten($array)); + assertType('array', Arrays::flatten($array, preserveKeys: true)); + assertType('array', Arrays::flatten($array, $preserveKeys)); +} + + +/** + * @param ArrayHash $hash + * @param ArrayList $list + * @param CachingIterator $iterator + */ +function testCounts(ArrayHash $hash, ArrayList $list, CachingIterator $iterator): void +{ + assertType('int<0, max>', $hash->count()); + assertType('int<0, max>', $list->count()); + assertType('int<0, max>', $iterator->getCounter()); + assertType('int<0, max>', $iterator->counter); +} + + +function testValidatorsIsUnicode(mixed $value): void +{ + if (Validators::isUnicode($value)) { + assertType('string', $value); + } +}