Skip to content

Commit 9226b78

Browse files
committed
Se agrega helper Caster.
1 parent 86b06d2 commit 9226b78

3 files changed

Lines changed: 424 additions & 0 deletions

File tree

src/Caster.php

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Derafu: Support - Essential PHP Utilities.
7+
*
8+
* Copyright (c) 2026 Esteban De La Fuente Rubio / Derafu <https://www.derafu.dev>
9+
* Licensed under the MIT License.
10+
* See LICENSE file for more details.
11+
*/
12+
13+
namespace Derafu\Support;
14+
15+
/**
16+
* Value casting utility class.
17+
*
18+
* Provides functionality to cast values to the most appropriate scalar type.
19+
*/
20+
final class Caster
21+
{
22+
/**
23+
* Casts a mixed value to the most appropriate scalar type.
24+
*
25+
* @param mixed $value The value to cast.
26+
* @return int|float|bool|string|null The casted value.
27+
*/
28+
public static function cast(mixed $value): int|float|bool|string|null
29+
{
30+
// Null is returned as is.
31+
if (is_null($value)) {
32+
return null;
33+
}
34+
35+
// Booleans are returned as is.
36+
if (is_bool($value)) {
37+
return $value;
38+
}
39+
40+
// Integers and floats are returned as is.
41+
if (is_int($value) || is_float($value)) {
42+
return $value;
43+
}
44+
45+
// Arrays, objects, resources, etc. → force to string.
46+
if (!is_string($value)) {
47+
return (string) $value;
48+
}
49+
50+
// Parse string to the most appropriate scalar type.
51+
return self::parseString($value);
52+
}
53+
54+
/**
55+
* Casts a value to the most appropriate scalar type and returns the type.
56+
*
57+
* @param mixed $value The value to cast.
58+
* @return array{value: int|float|bool|string|null, type: string} The casted
59+
* value and type.
60+
*/
61+
public static function castWithType(mixed $value): array
62+
{
63+
$casted = self::cast($value);
64+
65+
return [
66+
'value' => $casted,
67+
'type' => gettype($casted),
68+
];
69+
}
70+
71+
/**
72+
* Parses a string to the most appropriate scalar type.
73+
*
74+
* @param string $value The string to parse.
75+
* @return int|float|bool|string|null The parsed value.
76+
*/
77+
private static function parseString(string $value): int|float|bool|string|null
78+
{
79+
$trimmed = trim($value);
80+
81+
// Null literal.
82+
if (self::isNullLiteral($trimmed)) {
83+
return null;
84+
}
85+
86+
// Boolean literal.
87+
$bool = self::parseBool($trimmed);
88+
if ($bool !== null) {
89+
return $bool;
90+
}
91+
92+
// Integer (includes negative and underscore notation: 1_000).
93+
if (self::isInteger($trimmed)) {
94+
return (int) str_replace('_', '', $trimmed);
95+
}
96+
97+
// Float (includes scientific notation: 1.5e3).
98+
if (self::isFloat($trimmed)) {
99+
return (float) str_replace('_', '', $trimmed);
100+
}
101+
102+
return $value; // Remains as string.
103+
}
104+
105+
/**
106+
* Checks if a string is a null literal.
107+
*
108+
* @param string $v The string to check.
109+
* @return bool True if the string is a null literal.
110+
*/
111+
private static function isNullLiteral(string $v): bool
112+
{
113+
return in_array(strtolower($v), ['null', 'nil', ''], true);
114+
}
115+
116+
/**
117+
* Parses a string to a boolean.
118+
*
119+
* @param string $v The string to parse.
120+
* @return bool|null The parsed boolean or null if not a boolean literal.
121+
*/
122+
private static function parseBool(string $v): ?bool
123+
{
124+
return match (strtolower($v)) {
125+
'true', 'yes', 'on', '1' => true,
126+
'false', 'no', 'off', '0' => false,
127+
default => null,
128+
};
129+
}
130+
131+
/**
132+
* Checks if a string is an integer.
133+
*
134+
* @param string $v The string to check.
135+
* @return bool True if the string is an integer.
136+
*/
137+
private static function isInteger(string $v): bool
138+
{
139+
// Optional sign + digits (with _ as visual separator).
140+
return (bool) preg_match('/^[+-]?[0-9][0-9_]*$/', $v);
141+
}
142+
143+
/**
144+
* Checks if a string is a float.
145+
*
146+
* @param string $v The string to check.
147+
* @return bool True if the string is a float.
148+
*/
149+
private static function isFloat(string $v): bool
150+
{
151+
// Covers: 3.14 | .5 | 3. | 1_000.50 | 1.5e3 | -2.8E-10.
152+
return (bool) preg_match(
153+
'/^[+-]?(?:[0-9][0-9_]*\.?[0-9_]*|[0-9_]*\.[0-9][0-9_]*)(?:[eE][+-]?[0-9]+)?$/',
154+
$v
155+
);
156+
}
157+
}

tests/fixtures/.empty

Whitespace-only changes.

0 commit comments

Comments
 (0)