Skip to content

Commit

Permalink
added Cast [WIP]
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Nov 30, 2023
1 parent 561b94f commit cd19046
Show file tree
Hide file tree
Showing 2 changed files with 238 additions and 0 deletions.
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);
}
}
139 changes: 139 additions & 0 deletions tests/Utils/Cast.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php

declare(strict_types=1);

use Nette\Utils\Cast;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';


// bool
Assert::true(Cast::bool(true));
Assert::true(Cast::bool(1));
Assert::true(Cast::bool(2));
Assert::true(Cast::bool(0.1));
Assert::true(Cast::bool('1'));
Assert::true(Cast::bool('0.0'));
Assert::false(Cast::bool(false));
Assert::false(Cast::bool(0));
Assert::false(Cast::bool(0.0));
Assert::false(Cast::bool(''));
Assert::false(Cast::bool('0'));
Assert::exception(
fn() => Cast::bool([]),
TypeError::class,
'Cannot cast array to bool.',
);
Assert::exception(
fn() => Cast::bool(null),
TypeError::class,
'Cannot cast null to bool.',
);


// int
Assert::same(0, Cast::int(false));
Assert::same(1, Cast::int(true));
Assert::same(0, Cast::int(0));
Assert::same(1, Cast::int(1));
Assert::exception(
fn() => Cast::int(PHP_INT_MAX + 1),
TypeError::class,
'Cannot cast 9.2233720368548E+18 to int.',
);
Assert::same(0, Cast::int(0.0));
Assert::same(1, Cast::int(1.0));
Assert::exception(
fn() => Cast::int(0.1),
TypeError::class,
'Cannot cast 0.1 to int.',
);
Assert::exception(
fn() => Cast::int(''),
TypeError::class,
"Cannot cast '' to int.",
);
Assert::same(0, Cast::int('0'));
Assert::same(1, Cast::int('1'));
Assert::same(-1, Cast::int('-1.'));
Assert::same(1, Cast::int('1.0000'));
Assert::exception(
fn() => Cast::int('0.1'),
TypeError::class,
"Cannot cast '0.1' to int.",
);
Assert::exception(
fn() => Cast::int([]),
TypeError::class,
'Cannot cast array to int.',
);
Assert::exception(
fn() => Cast::int(null),
TypeError::class,
'Cannot cast null to int.',
);


// float
Assert::same(0.0, Cast::float(false));
Assert::same(1.0, Cast::float(true));
Assert::same(0.0, Cast::float(0));
Assert::same(1.0, Cast::float(1));
Assert::same(0.0, Cast::float(0.0));
Assert::same(1.0, Cast::float(1.0));
Assert::same(0.1, Cast::float(0.1));
Assert::exception(
fn() => Cast::float(''),
TypeError::class,
"Cannot cast '' to float.",
);
Assert::same(0.0, Cast::float('0'));
Assert::same(1.0, Cast::float('1'));
Assert::same(-1.0, Cast::float('-1.'));
Assert::same(1.0, Cast::float('1.0'));
Assert::same(0.1, Cast::float('0.1'));
Assert::exception(
fn() => Cast::float([]),
TypeError::class,
'Cannot cast array to float.',
);
Assert::exception(
fn() => Cast::float(null),
TypeError::class,
'Cannot cast null to float.',
);


// string
Assert::same('0', Cast::string(false)); // differs from PHP strict casting
Assert::same('1', Cast::string(true));
Assert::same('0', Cast::string(0));
Assert::same('1', Cast::string(1));
Assert::same('0.0', Cast::string(0.0)); // differs from PHP strict casting
Assert::same('1.0', Cast::string(1.0)); // differs from PHP strict casting
Assert::same('-0.1', Cast::string(-0.1));
Assert::same('9.2233720368548E+18', Cast::string(PHP_INT_MAX + 1));
Assert::same('', Cast::string(''));
Assert::same('x', Cast::string('x'));
Assert::exception(
fn() => Cast::string([]),
TypeError::class,
'Cannot cast array to string.',
);
Assert::exception(
fn() => Cast::string(null),
TypeError::class,
'Cannot cast null to string.',
);


// OrNull
Assert::true(Cast::boolOrNull(true));
Assert::null(Cast::boolOrNull(null));
Assert::same(0, Cast::intOrNull(0));
Assert::null(Cast::intOrNull(null));
Assert::same(0.0, Cast::floatOrNull(0));
Assert::null(Cast::floatOrNull(null));
Assert::same('0', Cast::stringOrNull(0));
Assert::null(Cast::stringOrNull(null));

0 comments on commit cd19046

Please sign in to comment.