Skip to content

Commit 51579dd

Browse files
committed
Se crea helper JsonSerializer
1 parent 349a4f0 commit 51579dd

1 file changed

Lines changed: 136 additions & 0 deletions

File tree

src/JsonSerializer.php

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* Derafu: Support - Essential PHP Utilities.
7+
*
8+
* Copyright (c) 2025 Esteban De La Fuente Rubio / Derafu <https://www.derafu.org>
9+
* Licensed under the MIT License.
10+
* See LICENSE file for more details.
11+
*/
12+
13+
namespace Derafu\Support;
14+
15+
use DateTime;
16+
use DateTimeInterface;
17+
use JsonException;
18+
use JsonSerializable;
19+
use Stringable;
20+
21+
/**
22+
* JSON serializer for converting PHP objects and arrays to JSON strings.
23+
*
24+
* This serializer handles the conversion of complex nested structures including
25+
* objects that implement JsonSerializable, have toArray() methods, or are
26+
* special types like DateTimeInterface and Stringable.
27+
*/
28+
final class JsonSerializer
29+
{
30+
/**
31+
* Default flags used for JSON encoding.
32+
*/
33+
private const DEFAULT_FLAGS = [
34+
JSON_PRETTY_PRINT, // Format JSON with whitespace for readability.
35+
JSON_INVALID_UTF8_SUBSTITUTE, // Replace invalid UTF-8 sequences with Unicode replacement character.
36+
JSON_UNESCAPED_LINE_TERMINATORS, // Don't escape line terminators.
37+
JSON_UNESCAPED_SLASHES, // Don't escape forward slashes.
38+
JSON_UNESCAPED_UNICODE, // Don't escape Unicode characters.
39+
JSON_THROW_ON_ERROR, // Throw exception on encoding errors.
40+
];
41+
42+
/**
43+
* Serializes a PHP value to a JSON string.
44+
*
45+
* This method handles complex nested structures by recursively transforming
46+
* objects into arrays or scalar values that can be encoded to JSON.
47+
*
48+
* @param mixed $value The value to serialize to JSON.
49+
* @param int|null $flags JSON encoding flags (uses DEFAULT_FLAGS if null).
50+
* @param int $depth Maximum recursion depth.
51+
* @return string JSON encoded string.
52+
* @throws JsonException When encoding fails.
53+
*/
54+
public static function serialize(
55+
mixed $value,
56+
?int $flags = null,
57+
int $depth = 512
58+
): string {
59+
// Use default flags if none provided.
60+
if ($flags === null) {
61+
$flags = array_reduce(
62+
self::DEFAULT_FLAGS,
63+
fn ($carry, $flag) => $carry | $flag,
64+
0
65+
);
66+
}
67+
68+
// Transform objects to arrays or scalar values.
69+
self::transformObjects($value);
70+
71+
// Encode to JSON.
72+
return json_encode($value, $flags, $depth);
73+
}
74+
75+
/**
76+
* Recursively transforms objects in a value to make them JSON serializable.
77+
*
78+
* This method handles:
79+
*
80+
* - Objects implementing JsonSerializable (calls jsonSerialize()).
81+
* - Objects with toArray() method (calls toArray()).
82+
* - DateTimeInterface objects (formats as ISO 8601 string).
83+
* - Stringable objects (converts to string).
84+
* - Arrays (processes each element recursively).
85+
*
86+
* @param mixed &$value The value to transform (passed by reference).
87+
* @return void
88+
*/
89+
private static function transformObjects(mixed &$value): void
90+
{
91+
// Handle arrays by processing each element recursively.
92+
if (is_array($value)) {
93+
foreach ($value as &$item) {
94+
self::transformObjects($item);
95+
}
96+
// Break the reference to prevent accidental modifications.
97+
unset($item);
98+
return;
99+
}
100+
101+
// If not an object, no transformation needed.
102+
if (!is_object($value)) {
103+
return;
104+
}
105+
106+
// Handle different types of objects.
107+
108+
// Get the serialized representation and transform it recursively.
109+
if ($value instanceof JsonSerializable) {
110+
111+
$serialized = $value->jsonSerialize();
112+
$value = $serialized;
113+
self::transformObjects($value);
114+
}
115+
116+
// Convert to array and transform recursively.
117+
elseif (method_exists($value, 'toArray')) {
118+
$array = $value->toArray();
119+
$value = $array;
120+
self::transformObjects($value);
121+
}
122+
123+
// Format DateTime objects as ISO 8601 strings.
124+
elseif ($value instanceof DateTimeInterface) {
125+
$value = $value->format(DateTime::ATOM);
126+
}
127+
128+
// Convert Stringable objects to strings.
129+
elseif ($value instanceof Stringable) {
130+
$value = $value->__toString();
131+
}
132+
133+
// Objects that don't match any of the above conditions remain as is,
134+
// which may result in only public properties being encoded.
135+
}
136+
}

0 commit comments

Comments
 (0)