Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure valid type is passed into insert() #314

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/coding-style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
name: Nette Code Checker
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: 8.0
Expand All @@ -21,7 +21,7 @@ jobs:
name: Nette Coding Standard
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: 8.0
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/static-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
name: PHPStan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: 8.0
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:

name: PHP ${{ matrix.php }} tests on ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
Expand All @@ -34,7 +34,7 @@ jobs:
name: Code Coverage
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: 8.0
Expand Down
51 changes: 25 additions & 26 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
Nette Utility Classes
=====================
[![Nette Utils](https://github.com/nette/utils/assets/194960/c33fdb74-0652-4cad-ac6e-c1ce0d29e32a)](https://doc.nette.org/en/utils)

[![Downloads this Month](https://img.shields.io/packagist/dm/nette/utils.svg)](https://packagist.org/packages/nette/utils)
[![Tests](https://github.com/nette/utils/workflows/Tests/badge.svg?branch=master)](https://github.com/nette/utils/actions)
Expand All @@ -11,25 +10,27 @@ Nette Utility Classes
Introduction
------------

In package nette/utils you will find a set of [useful classes](https://doc.nette.org/utils) for everyday use:

- [Arrays](https://doc.nette.org/utils/arrays) - manipulate arrays
- [Callback](https://doc.nette.org/utils/callback) - PHP callbacks
- [Date and Time](https://doc.nette.org/utils/datetime) - modify times and dates
- [Filesystem](https://doc.nette.org/utils/filesystem) - copying, renaming, …
- [Finder](https://doc.nette.org/utils/finder) - finds files and directories
- [Helper Functions](https://doc.nette.org/utils/helpers)
- [HTML elements](https://doc.nette.org/utils/html-elements) - generate HTML
- [Images](https://doc.nette.org/utils/images) - crop, resize, rotate images
- [JSON](https://doc.nette.org/utils/json) - encoding and decoding
- [Generating Random Strings](https://doc.nette.org/utils/random)
- [Paginator](https://doc.nette.org/utils/paginator) - pagination math
- [PHP Reflection](https://doc.nette.org/utils/reflection)
- [Strings](https://doc.nette.org/utils/strings) - useful text functions
- [SmartObject](https://doc.nette.org/utils/smartobject) - PHP object enhancements
- [Validation](https://doc.nette.org/utils/validators) - validate inputs
- [Type](https://doc.nette.org/utils/type) - PHP data type

In package nette/utils you will find a set of useful classes for everyday use:

✅ [Arrays](https://doc.nette.org/utils/arrays)<br>
✅ [Callback](https://doc.nette.org/utils/callback) - PHP callbacks<br>
✅ [Filesystem](https://doc.nette.org/utils/filesystem) - copying, renaming, …<br>
✅ [Finder](https://doc.nette.org/utils/finder) - finds files and directories<br>
✅ [Floats](https://doc.nette.org/utils/floats) - floating point numbers<br>
✅ [Helper Functions](https://doc.nette.org/utils/helpers)<br>
✅ [HTML elements](https://doc.nette.org/utils/html-elements) - generate HTML<br>
✅ [Images](https://doc.nette.org/utils/images) - crop, resize, rotate images<br>
✅ [Iterables](https://doc.nette.org/utils/iterables) <br>
✅ [JSON](https://doc.nette.org/utils/json) - encoding and decoding<br>
✅ [Generating Random Strings](https://doc.nette.org/utils/random)<br>
✅ [Paginator](https://doc.nette.org/utils/paginator) - pagination math<br>
✅ [PHP Reflection](https://doc.nette.org/utils/reflection)<br>
✅ [Strings](https://doc.nette.org/utils/strings) - useful text functions<br>
✅ [SmartObject](https://doc.nette.org/utils/smartobject) - PHP object enhancements<br>
✅ [Validation](https://doc.nette.org/utils/validators) - validate inputs<br>
✅ [Type](https://doc.nette.org/utils/type) - PHP data type

 <!---->

Installation
------------
Expand All @@ -40,11 +41,9 @@ The recommended way to install is via Composer:
composer require nette/utils
```

- Nette Utils 4.0 is compatible with PHP 8.0 to 8.3
- Nette Utils 3.2 is compatible with PHP 7.2 to 8.3
- Nette Utils 3.1 is compatible with PHP 7.1 to 8.0
- Nette Utils 3.0 is compatible with PHP 7.1 to 8.0
- Nette Utils 2.5 is compatible with PHP 5.6 to 8.0
Nette Utils 4.0 is compatible with PHP 8.0 to 8.3.

 <!---->

[Support Me](https://github.com/sponsors/dg)
--------------------------------------------
Expand Down
24 changes: 5 additions & 19 deletions src/Iterators/CachingIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,12 @@ class CachingIterator extends \CachingIterator implements \Countable
private int $counter = 0;


public function __construct($iterator)
public function __construct(iterable|\stdClass $iterable)
{
if (is_array($iterator) || $iterator instanceof \stdClass) {
$iterator = new \ArrayIterator($iterator);

} elseif ($iterator instanceof \IteratorAggregate) {
do {
$iterator = $iterator->getIterator();
} while ($iterator instanceof \IteratorAggregate);

assert($iterator instanceof \Iterator);

} elseif ($iterator instanceof \Iterator) {
} elseif ($iterator instanceof \Traversable) {
$iterator = new \IteratorIterator($iterator);
} else {
throw new Nette\InvalidArgumentException(sprintf('Invalid argument passed to %s; array or Traversable expected, %s given.', self::class, get_debug_type($iterator)));
}

parent::__construct($iterator, 0);
$iterable = $iterable instanceof \stdClass
? new \ArrayIterator($iterable)
: Nette\Utils\Iterables::toIterator($iterable);
parent::__construct($iterable, 0);
}


Expand Down
3 changes: 1 addition & 2 deletions src/Iterators/Mapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@
namespace Nette\Iterators;



/**
* Applies the callback to the elements of the inner iterator.
* @deprecated use Nette\Utils\Iterables::map()
*/
class Mapper extends \IteratorIterator
{
Expand Down
77 changes: 77 additions & 0 deletions src/Iterators/MemoizingIterator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/

declare(strict_types=1);

namespace Nette\Iterators;


/**
* MemoizingIterator wraps around another iterator and caches its keys and values during iteration.
* This allows the data to be re-iterated multiple times.
* @template K
* @template V
* @implements \Iterator<K, V>
*/
class MemoizingIterator implements \Iterator
{
private array $cache = [];
private int $index = 0;


/**
* @param \Iterator<K, V> $inner
*/
public function __construct(
private readonly \Iterator $inner,
) {
}


public function rewind(): void
{
if (!$this->cache) {
$this->inner->rewind();
}
$this->index = 0;
}


/**
* @return V
*/
public function current(): mixed
{
return $this->cache[$this->index][1] ?? null;
}


/**
* @return K
*/
public function key(): mixed
{
return $this->cache[$this->index][0] ?? null;
}


public function next(): void
{
if (!isset($this->cache[++$this->index])) {
$this->inner->next();
}
}


public function valid(): bool
{
if (!isset($this->cache[$this->index]) && $this->inner->valid()) {
$this->cache[$this->index] = [$this->inner->key(), $this->inner->current()];
}
return isset($this->cache[$this->index]);
}
}
22 changes: 11 additions & 11 deletions src/Utils/Arrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -369,11 +369,11 @@ public static function pick(array &$array, string|int $key, mixed $default = nul

/**
* Tests whether at least one element in the array passes the test implemented by the provided function,
* which has the signature `function ($value, $key, array $array): bool`.
* @template K
* which has the signature `function (mixed $value, int|string $key, array $array): bool`.
* @template K of array-key
* @template V
* @param iterable<K, V> $array
* @param callable(V, K, ($array is array ? array<K, V> : iterable<K, V>)): bool $predicate
* @param array<K, V> $array
* @param callable(V, K, array<K, V>): bool $predicate
*/
public static function some(iterable $array, callable $predicate): bool
{
Expand All @@ -389,11 +389,11 @@ public static function some(iterable $array, callable $predicate): bool

/**
* Tests whether all elements in the array pass the test implemented by the provided function,
* which has the signature `function ($value, $key, array $array): bool`.
* @template K
* which has the signature `function (mixed $value, int|string $key, array $array): bool`.
* @template K of array-key
* @template V
* @param iterable<K, V> $array
* @param callable(V, K, ($array is array ? array<K, V> : iterable<K, V>)): bool $predicate
* @param array<K, V> $array
* @param callable(V, K, array<K, V>): bool $predicate
*/
public static function every(iterable $array, callable $predicate): bool
{
Expand Down Expand Up @@ -430,12 +430,12 @@ public static function filter(array $array, callable $predicate): array

/**
* Returns an array containing the original keys and results of applying the given transform function to each element.
* The function has signature `function ($value, $key, array $array): mixed`.
* The function has signature `function (mixed $value, int|string $key, array $array): mixed`.
* @template K of array-key
* @template V
* @template R
* @param iterable<K, V> $array
* @param callable(V, K, ($array is array ? array<K, V> : iterable<K, V>)): R $transformer
* @param array<K, V> $array
* @param callable(V, K, array<K, V>): R $transformer
* @return array<K, R>
*/
public static function map(iterable $array, callable $transformer): array
Expand Down
99 changes: 99 additions & 0 deletions src/Utils/Cast.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/

declare(strict_types=1);

namespace Nette\Utils;

use Nette;
use TypeError;


/**
* Converts variables in a similar way to implicit casting in PHP in strict types mode.
*/
final class Cast
{
use Nette\StaticClass;

public static function bool(mixed $value): bool
{
return match (true) {
is_bool($value) => $value,
is_int($value) => $value !== 0,
is_float($value) => $value !== 0.0,
is_string($value) => $value !== '' && $value !== '0',
default => throw new TypeError('Cannot cast ' . get_debug_type($value) . ' to bool.'),
};
}


public static function int(mixed $value): int
{
return match (true) {
is_bool($value) => (int) $value,
is_int($value) => $value,
is_float($value) => $value === (float) ($tmp = (int) $value)
? $tmp
: throw new TypeError('Cannot cast ' . self::string($value) . ' to int.'),
is_string($value) => preg_match('~^-?\d+(\.0*)?$~D', $value)
? (int) $value
: throw new TypeError("Cannot cast '$value' to int."),
default => throw new TypeError('Cannot cast ' . get_debug_type($value) . ' to int.'),
};
}


public static function float(mixed $value): float
{
return match (true) {
is_bool($value) => $value ? 1.0 : 0.0,
is_int($value) => (float) $value,
is_float($value) => $value,
is_string($value) => preg_match('~^-?\d+(\.\d*)?$~D', $value)
? (float) $value
: throw new TypeError("Cannot cast '$value' to float."),
default => throw new TypeError('Cannot cast ' . get_debug_type($value) . ' to float.'),
};
}


public static function string(mixed $value): string
{
return match (true) {
is_bool($value) => $value ? '1' : '0',
is_int($value) => (string) $value,
is_float($value) => str_contains($tmp = (string) $value, '.') ? $tmp : $tmp . '.0',
is_string($value) => $value,
default => throw new TypeError('Cannot cast ' . get_debug_type($value) . ' to string.'),
};
}


public static function boolOrNull(mixed $value): ?bool
{
return $value === null ? null : self::bool($value);
}


public static function intOrNull(mixed $value): ?int
{
return $value === null ? null : self::int($value);
}


public static function floatOrNull(mixed $value): ?float
{
return $value === null ? null : self::float($value);
}


public static function stringOrNull(mixed $value): ?string
{
return $value === null ? null : self::string($value);
}
}
3 changes: 3 additions & 0 deletions src/Utils/Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,9 @@ final public function getText(): string
*/
final public function addHtml(mixed $child): static
{
if (!$child instanceof HtmlStringable) {
$child = (string) $child;
}
return $this->insert(null, $child);
}

Expand Down
Loading