-
-
Notifications
You must be signed in to change notification settings - Fork 149
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
381 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
<?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; | ||
|
||
|
||
/** | ||
* Provides safe, lossless type casting. Throws TypeError if conversion would result | ||
* in data loss. Supports bool, int, float, string, and array types. | ||
*/ | ||
final class Cast | ||
{ | ||
use Nette\StaticClass; | ||
|
||
/** | ||
* Safely converts a value to a specified type. Supported types: bool, int, float, string, array. | ||
* @throws TypeError if the value cannot be converted | ||
*/ | ||
public static function to(mixed $value, string $type): mixed | ||
{ | ||
return match ($type) { | ||
'bool' => self::toBool($value), | ||
'int' => self::toInt($value), | ||
'float' => self::toFloat($value), | ||
'string' => self::toString($value), | ||
'array' => self::toArray($value), | ||
default => throw new TypeError("Unsupported type '$type'."), | ||
}; | ||
} | ||
|
||
|
||
/** | ||
* Safely converts a value to a specified type or returns null if the value is null. | ||
* Supported types: bool, int, float, string, array. | ||
* @throws TypeError if the value cannot be converted | ||
*/ | ||
public static function toOrNull(mixed $value, string $type): mixed | ||
{ | ||
return $value === null ? null : self::to($value, $type); | ||
} | ||
|
||
|
||
/** | ||
* Safely converts a value to a boolean. | ||
* @throws TypeError if the value cannot be converted | ||
*/ | ||
public static function toBool(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', | ||
$value === null => false, | ||
default => throw new TypeError('Cannot cast ' . get_debug_type($value) . ' to bool.'), | ||
}; | ||
} | ||
|
||
|
||
/** | ||
* Safely converts a value to an integer. | ||
* @throws TypeError if the value cannot be converted | ||
*/ | ||
public static function toInt(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::toString($value) . ' to int.'), | ||
is_string($value) => preg_match('~^-?\d+(\.0*)?$~D', $value) | ||
? (int) $value | ||
: throw new TypeError("Cannot cast '$value' to int."), | ||
$value === null => 0, | ||
default => throw new TypeError('Cannot cast ' . get_debug_type($value) . ' to int.'), | ||
}; | ||
} | ||
|
||
|
||
/** | ||
* Safely converts a value to a float. | ||
* @throws TypeError if the value cannot be converted | ||
*/ | ||
public static function toFloat(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."), | ||
$value === null => 0.0, | ||
default => throw new TypeError('Cannot cast ' . get_debug_type($value) . ' to float.'), | ||
}; | ||
} | ||
|
||
|
||
/** | ||
* Safely converts a value to a string. | ||
* @throws TypeError if the value cannot be converted | ||
*/ | ||
public static function toString(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, | ||
$value === null => '', | ||
default => throw new TypeError('Cannot cast ' . get_debug_type($value) . ' to string.'), | ||
}; | ||
} | ||
|
||
|
||
/** | ||
* Wraps the value in an array if it is not already one or returns empty array if the value is null. | ||
*/ | ||
public static function toArray(mixed $value): array | ||
{ | ||
return match (true) { | ||
is_array($value) => $value, | ||
$value === null => [], | ||
default => [$value], | ||
}; | ||
} | ||
|
||
|
||
/** | ||
* Safely converts a value to a boolean or returns null if the value is null. | ||
* @throws TypeError if the value cannot be converted | ||
*/ | ||
public static function toBoolOrNull(mixed $value): ?bool | ||
{ | ||
return $value === null ? null : self::toBool($value); | ||
} | ||
|
||
|
||
/** | ||
* Safely converts a value to an integer or returns null if the value is null. | ||
* @throws TypeError if the value cannot be converted | ||
*/ | ||
public static function toIntOrNull(mixed $value): ?int | ||
{ | ||
return $value === null ? null : self::toInt($value); | ||
} | ||
|
||
|
||
/** | ||
* Safely converts a value to a float or returns null if the value is null. | ||
* @throws TypeError if the value cannot be converted | ||
*/ | ||
public static function toFloatOrNull(mixed $value): ?float | ||
{ | ||
return $value === null ? null : self::toFloat($value); | ||
} | ||
|
||
|
||
/** | ||
* Safely converts a value to a string or returns null if the value is null. | ||
* @throws TypeError if the value cannot be converted | ||
*/ | ||
public static function toStringOrNull(mixed $value): ?string | ||
{ | ||
return $value === null ? null : self::toString($value); | ||
} | ||
|
||
|
||
/** | ||
* Wraps the value in an array if it is not already one or returns null if the value is null. | ||
*/ | ||
public static function toArrayOrNull(mixed $value): ?array | ||
{ | ||
return $value === null ? null : self::toArray($value); | ||
} | ||
|
||
|
||
/** | ||
* Converts false to null, does not change other values. | ||
*/ | ||
public static function falseToNull(mixed $value): mixed | ||
{ | ||
return $value === false ? null : $value; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
use Nette\Utils\Cast; | ||
use Tester\Assert; | ||
|
||
|
||
require __DIR__ . '/../bootstrap.php'; | ||
|
||
|
||
Assert::same(1, Cast::falseToNull(1)); | ||
Assert::same(0, Cast::falseToNull(0)); | ||
Assert::same(null, Cast::falseToNull(null)); | ||
Assert::same(true, Cast::falseToNull(true)); | ||
Assert::same(null, Cast::falseToNull(false)); | ||
Assert::same([], Cast::falseToNull([])); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
use Nette\Utils\Cast; | ||
use Tester\Assert; | ||
|
||
require __DIR__ . '/../bootstrap.php'; | ||
|
||
|
||
// bool | ||
Assert::false(Cast::toBool(null)); | ||
Assert::true(Cast::toBool(true)); | ||
Assert::true(Cast::toBool(1)); | ||
Assert::true(Cast::toBool(2)); | ||
Assert::true(Cast::toBool(0.1)); | ||
Assert::true(Cast::toBool('1')); | ||
Assert::true(Cast::toBool('0.0')); | ||
Assert::false(Cast::toBool(false)); | ||
Assert::false(Cast::toBool(0)); | ||
Assert::false(Cast::toBool(0.0)); | ||
Assert::false(Cast::toBool('')); | ||
Assert::false(Cast::toBool('0')); | ||
Assert::exception( | ||
fn() => Cast::toBool([]), | ||
TypeError::class, | ||
'Cannot cast array to bool.', | ||
); | ||
|
||
|
||
// int | ||
Assert::same(0, Cast::toInt(null)); | ||
Assert::same(0, Cast::toInt(false)); | ||
Assert::same(1, Cast::toInt(true)); | ||
Assert::same(0, Cast::toInt(0)); | ||
Assert::same(1, Cast::toInt(1)); | ||
Assert::exception( | ||
fn() => Cast::toInt(PHP_INT_MAX + 1), | ||
TypeError::class, | ||
'Cannot cast 9.2233720368548E+18 to int.', | ||
); | ||
Assert::same(0, Cast::toInt(0.0)); | ||
Assert::same(1, Cast::toInt(1.0)); | ||
Assert::exception( | ||
fn() => Cast::toInt(0.1), | ||
TypeError::class, | ||
'Cannot cast 0.1 to int.', | ||
); | ||
Assert::exception( | ||
fn() => Cast::toInt(''), | ||
TypeError::class, | ||
"Cannot cast '' to int.", | ||
); | ||
Assert::same(0, Cast::toInt('0')); | ||
Assert::same(1, Cast::toInt('1')); | ||
Assert::same(-1, Cast::toInt('-1.')); | ||
Assert::same(1, Cast::toInt('1.0000')); | ||
Assert::exception( | ||
fn() => Cast::toInt('0.1'), | ||
TypeError::class, | ||
"Cannot cast '0.1' to int.", | ||
); | ||
Assert::exception( | ||
fn() => Cast::toInt([]), | ||
TypeError::class, | ||
'Cannot cast array to int.', | ||
); | ||
|
||
|
||
// float | ||
Assert::same(0.0, Cast::toFloat(null)); | ||
Assert::same(0.0, Cast::toFloat(false)); | ||
Assert::same(1.0, Cast::toFloat(true)); | ||
Assert::same(0.0, Cast::toFloat(0)); | ||
Assert::same(1.0, Cast::toFloat(1)); | ||
Assert::same(0.0, Cast::toFloat(0.0)); | ||
Assert::same(1.0, Cast::toFloat(1.0)); | ||
Assert::same(0.1, Cast::toFloat(0.1)); | ||
Assert::exception( | ||
fn() => Cast::toFloat(''), | ||
TypeError::class, | ||
"Cannot cast '' to float.", | ||
); | ||
Assert::same(0.0, Cast::toFloat('0')); | ||
Assert::same(1.0, Cast::toFloat('1')); | ||
Assert::same(-1.0, Cast::toFloat('-1.')); | ||
Assert::same(1.0, Cast::toFloat('1.0')); | ||
Assert::same(0.1, Cast::toFloat('0.1')); | ||
Assert::exception( | ||
fn() => Cast::toFloat([]), | ||
TypeError::class, | ||
'Cannot cast array to float.', | ||
); | ||
|
||
|
||
// string | ||
Assert::same('', Cast::toString(null)); | ||
Assert::same('0', Cast::toString(false)); // differs from PHP strict casting | ||
Assert::same('1', Cast::toString(true)); | ||
Assert::same('0', Cast::toString(0)); | ||
Assert::same('1', Cast::toString(1)); | ||
Assert::same('0.0', Cast::toString(0.0)); // differs from PHP strict casting | ||
Assert::same('1.0', Cast::toString(1.0)); // differs from PHP strict casting | ||
Assert::same('-0.1', Cast::toString(-0.1)); | ||
Assert::same('9.2233720368548E+18', Cast::toString(PHP_INT_MAX + 1)); | ||
Assert::same('', Cast::toString('')); | ||
Assert::same('x', Cast::toString('x')); | ||
Assert::exception( | ||
fn() => Cast::toString([]), | ||
TypeError::class, | ||
'Cannot cast array to string.', | ||
); | ||
|
||
|
||
// array | ||
Assert::same([], Cast::toArray(null)); | ||
Assert::same([false], Cast::toArray(false)); | ||
Assert::same([true], Cast::toArray(true)); | ||
Assert::same([0], Cast::toArray(0)); | ||
Assert::same([0.0], Cast::toArray(0.0)); | ||
Assert::same([1], Cast::toArray([1])); | ||
Assert::equal([new stdClass], Cast::toArray(new stdClass)); // differs from PHP strict casting | ||
|
||
|
||
// OrNull | ||
Assert::true(Cast::toBoolOrNull(true)); | ||
Assert::null(Cast::toBoolOrNull(null)); | ||
Assert::same(0, Cast::toIntOrNull(0)); | ||
Assert::null(Cast::toIntOrNull(null)); | ||
Assert::same(0.0, Cast::toFloatOrNull(0)); | ||
Assert::null(Cast::toFloatOrNull(null)); | ||
Assert::same('0', Cast::toStringOrNull(0)); | ||
Assert::null(Cast::toStringOrNull(null)); | ||
Assert::same([], Cast::toArrayOrNull([])); | ||
Assert::null(Cast::toArrayOrNull(null)); |
Oops, something went wrong.