Skip to content
Merged
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ src/

| Metric | Value |
|---|---|
| PHP source files | 19 |
| Source lines | ~720 |
| PHP source files | 20 |
| Source lines | ~810 |
| Test files | 13 |
| Test lines | ~700 |
| External runtime dependencies | 1 (kariricode/property-inspector) |
Expand Down
7 changes: 6 additions & 1 deletion src/Attribute/Serialize.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
/**
* Marks a property for serialization control.
*
* Parameters: name (wire-name override), groups (string[]), format-specific options.
* Parameters: name (wire-name override), groups (string[]), ignore (bool).
* Properties without this attribute are included automatically using their property name.
*
* @package KaririCode\Serializer\Attribute
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
#[\Attribute(\Attribute::TARGET_PROPERTY)]
final readonly class Serialize
Expand Down
7 changes: 7 additions & 0 deletions src/Configuration/SerializerConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

namespace KaririCode\Serializer\Configuration;

/**
* Immutable configuration value object for the serializer engine.
*
* @package KaririCode\Serializer\Configuration
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
final readonly class SerializerConfiguration
{
public function __construct(
Expand Down
7 changes: 7 additions & 0 deletions src/Contract/EncoderRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

namespace KaririCode\Serializer\Contract;

/**
* Contract for format-encoder registries.
*
* @package KaririCode\Serializer\Contract
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
interface EncoderRegistry
{
public function register(Encoder $encoder): void;
Expand Down
9 changes: 9 additions & 0 deletions src/Core/InMemoryEncoderRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@
use KaririCode\Serializer\Contract\EncoderRegistry;
use KaririCode\Serializer\Exception\SerializationException;

/**
* In-memory encoder registry backed by a plain PHP array.
*
* Throws SerializationException on duplicate registration or unknown format lookup.
*
* @package KaririCode\Serializer\Core
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
final class InMemoryEncoderRegistry implements EncoderRegistry
{
/** @var array<string, Encoder> */
Expand Down
10 changes: 10 additions & 0 deletions src/Core/SerializationContextImpl.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@

use KaririCode\Serializer\Contract\SerializationContext;

/**
* Immutable serialization context carrying the active format and parameters.
*
* Uses the named-constructor pattern: instantiate via `SerializationContextImpl::create()`.
* `withFormat()` and `withParameters()` return new instances (full immutability).
*
* @package KaririCode\Serializer\Core
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
final readonly class SerializationContextImpl implements SerializationContext
{
/**
Expand Down
26 changes: 14 additions & 12 deletions src/Core/SerializerEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* Central serialization orchestrator.
*
* Delegates to format-specific encoders via the registry.
* Resolves the active format from the request or falls back to the
* configured default, then delegates encode/decode to the matching encoder.
*
* @package KaririCode\Serializer\Core
* @author Walmir Silva <walmir.silva@kariricode.org>
Expand All @@ -34,20 +36,20 @@ public function __construct(
public function serialize(array $data, ?string $format = null, array $parameters = []): SerializationResult
{
$config = $this->configuration ?? new SerializerConfiguration();
$fmt = $format ?? $config->defaultFormat;
$encoder = $this->registry->resolve($fmt);
$resolvedFormat = $format ?? $config->defaultFormat;
$encoder = $this->registry->resolve($resolvedFormat);

$ctx = SerializationContextImpl::create($fmt);
$serializationContext = SerializationContextImpl::create($resolvedFormat);
if ($config->prettyPrint) {
$ctx = $ctx->withParameters(['pretty' => true]);
$serializationContext = $serializationContext->withParameters(['pretty' => true]);
}
if ($parameters !== []) {
$ctx = $ctx->withParameters($parameters);
$serializationContext = $serializationContext->withParameters($parameters);
}

$payload = $encoder->encode($data, $ctx);
$payload = $encoder->encode($data, $serializationContext);

return new SerializationResult($data, $payload, $fmt);
return new SerializationResult($data, $payload, $resolvedFormat);
}

/**
Expand All @@ -59,14 +61,14 @@ public function serialize(array $data, ?string $format = null, array $parameters
public function deserialize(string $payload, ?string $format = null, array $parameters = []): array
{
$config = $this->configuration ?? new SerializerConfiguration();
$fmt = $format ?? $config->defaultFormat;
$encoder = $this->registry->resolve($fmt);
$resolvedFormat = $format ?? $config->defaultFormat;
$encoder = $this->registry->resolve($resolvedFormat);

$ctx = SerializationContextImpl::create($fmt);
$serializationContext = SerializationContextImpl::create($resolvedFormat);
if ($parameters !== []) {
$ctx = $ctx->withParameters($parameters);
$serializationContext = $serializationContext->withParameters($parameters);
}

return $encoder->decode($payload, $ctx);
return $encoder->decode($payload, $serializationContext);
}
}
12 changes: 8 additions & 4 deletions src/Encoder/CsvEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
use KaririCode\Serializer\Exception\SerializationException;

/**
* Encodes/decodes flat arrays-of-arrays as CSV.
* Encodes/decodes flat arrays-of-arrays as RFC 4180-compliant CSV via php://temp.
*
* Parameters: separator (string, ','), enclosure (string, '"'), header (bool, true).
*
* @package KaririCode\Serializer\Encoder
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
final readonly class CsvEncoder implements Encoder
{
Expand All @@ -38,13 +42,13 @@ public function encode(array $data, SerializationContext $context): string
if ($hasHeader && \is_array($firstRow)) {
/** @var array<int|string, string> $keys */
$keys = array_keys($firstRow);
fputcsv($stream, $keys, $separator, $enclosure);
fputcsv($stream, $keys, $separator, $enclosure, escape: '\\');
}

foreach ($data as $row) {
if (\is_array($row)) {
/** @var array<int|string, bool|float|int|string|null> $row */
fputcsv($stream, $row, $separator, $enclosure);
fputcsv($stream, $row, $separator, $enclosure, escape: '\\');
}
}

Expand Down Expand Up @@ -75,7 +79,7 @@ public function decode(string $payload, SerializationContext $context): array
}

$rows = array_values(
array_map(static fn (string $line) => str_getcsv($line, $separator, $enclosure), $lines),
array_map(static fn (string $line) => str_getcsv($line, $separator, $enclosure, escape: '\\'), $lines),
);

if ($hasHeader && \count($rows) > 1) {
Expand Down
10 changes: 10 additions & 0 deletions src/Encoder/JsonEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@
use KaririCode\Serializer\Contract\SerializationContext;
use KaririCode\Serializer\Exception\SerializationException;

/**
* Encodes/decodes data as RFC 8259-compliant JSON.
*
* Uses JSON_THROW_ON_ERROR for deterministic error handling.
* Supports `pretty` context parameter for human-readable output.
*
* @package KaririCode\Serializer\Encoder
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
final readonly class JsonEncoder implements Encoder
{
/** @param array<mixed> $data */
Expand Down
8 changes: 7 additions & 1 deletion src/Encoder/QueryStringEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
use KaririCode\Serializer\Contract\Encoder;
use KaririCode\Serializer\Contract\SerializationContext;

/** Encodes/decodes URL query strings (application/x-www-form-urlencoded). */
/**
* Encodes/decodes data as RFC 3986-compliant URL query strings (application/x-www-form-urlencoded).
*
* @package KaririCode\Serializer\Encoder
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
final readonly class QueryStringEncoder implements Encoder
{
/** @param array<mixed> $data */
Expand Down
10 changes: 10 additions & 0 deletions src/Encoder/XmlEncoder.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@
use KaririCode\Serializer\Contract\SerializationContext;
use KaririCode\Serializer\Exception\SerializationException;

/**
* Encodes/decodes arrays as application/xml via SimpleXMLElement + DOMDocument.
*
* Uses `libxml_use_internal_errors` to suppress PHP notices on malformed XML decode.
* Supports `root` and `pretty` context parameters.
*
* @package KaririCode\Serializer\Encoder
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
final readonly class XmlEncoder implements Encoder
{
/** @param array<mixed> $data */
Expand Down
7 changes: 7 additions & 0 deletions src/Event/SerializationCompletedEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

use KaririCode\Serializer\Result\SerializationResult;

/**
* Event emitted when a serialization operation completes successfully.
*
* @package KaririCode\Serializer\Event
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
final readonly class SerializationCompletedEvent
{
public function __construct(public SerializationResult $result, public float $durationMs, public float $timestamp = 0)
Expand Down
7 changes: 7 additions & 0 deletions src/Event/SerializationStartedEvent.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

namespace KaririCode\Serializer\Event;

/**
* Event emitted when a serialization operation begins.
*
* @package KaririCode\Serializer\Event
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
final readonly class SerializationStartedEvent
{
public function __construct(public string $format, public float $timestamp = 0)
Expand Down
9 changes: 9 additions & 0 deletions src/Exception/SerializationException.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@

namespace KaririCode\Serializer\Exception;

/**
* Domain exception for all serialization and deserialization failures.
*
* Uses static factory methods for type-safe construction.
*
* @package KaririCode\Serializer\Exception
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
final class SerializationException extends \RuntimeException
{
public static function encodingFailed(string $format, string $reason): self
Expand Down
9 changes: 9 additions & 0 deletions src/Integration/ProcessorBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
use KaririCode\Serializer\Core\SerializerEngine;
use KaririCode\Serializer\Result\SerializationResult;

/**
* Bridges the processor-pipeline to the serializer engine.
*
* Adapts the generic Pipeline interface to format-specific serialization output.
*
* @package KaririCode\Serializer\Integration
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
final readonly class ProcessorBridge
{
public function __construct(
Expand Down
1 change: 1 addition & 0 deletions src/Result/SerializationResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/**
* Immutable result of a serialization pass.
*
* @package KaririCode\Serializer\Result
* @author Walmir Silva <walmir.silva@kariricode.org>
* @since 3.1.0 ARFA 1.3
*/
Expand Down
5 changes: 5 additions & 0 deletions tests/Conformance/ImmutableStateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
namespace KaririCode\Serializer\Tests\Conformance;

use KaririCode\Serializer\Core\SerializationContextImpl;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

#[CoversClass(SerializationContextImpl::class)]
final class ImmutableStateTest extends TestCase
{
#[Test]
public function testContextImmutability(): void
{
$ctx = SerializationContextImpl::create('json');
Expand All @@ -18,6 +22,7 @@ public function testContextImmutability(): void
$this->assertSame('xml', $ctx2->getFormat());
}

#[Test]
public function testParametersImmutability(): void
{
$ctx = SerializationContextImpl::create();
Expand Down