baraja-core/serializer

There is no license information available for the latest version (v1.0.1) of this package.

Simple serializer any PHP type or structure to simple scalar array.

Installs: 27 990

Dependents: 1

Suggesters: 0

Security: 0

Stars: 1

Watchers: 1

Forks: 1

Open Issues: 0

pkg:composer/baraja-core/serializer

v1.0.1 2022-09-28 21:18 UTC

This package is auto-updated.

Last update: 2026-01-04 10:29:43 UTC


README

A simple, secure, and flexible serializer that converts any PHP object or data structure into a simple scalar array, ready to be sent via REST API.

The serializer automatically handles backward and forward compatibility, security concerns, and supports a wide range of PHP types including DTOs, entities, enums, DateTime objects, and more.

πŸ’‘ Key Principles

  • Zero dependencies - Works standalone without any required external packages
  • Security-first - Automatically hides sensitive data (passwords, credit cards, PINs)
  • Type-safe - Full support for PHP 8.0+ typed properties and modern features
  • Recursion protection - Detects and prevents infinite loops in object graphs
  • Depth limiting - Protects against overly deep nested structures (max 32 levels)
  • Convention-based - Configurable behavior through the convention system
  • Bridge interfaces - Extensible architecture for custom type handling

πŸ—οΈ Architecture

The package follows a simple yet powerful architecture with clear separation of concerns:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Serializer                         β”‚
β”‚  (Main entry point - singleton or instance-based usage)   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                   SerializerConvention                    β”‚
β”‚  (Configuration: date format, null handling, hidden keys) β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                    Bridge Interfaces                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ ItemsListInterface  β”‚  β”‚  StatusCountInterface     β”‚  β”‚
β”‚  β”‚ (List collections)  β”‚  β”‚  (Status with count)      β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
                             β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     Supported Types                       β”‚
β”‚  - Scalars (string, int, float, bool, null)               β”‚
β”‚  - Arrays (indexed and associative)                       β”‚
β”‚  - Objects (DTOs, entities, stdClass)                     β”‚
β”‚  - DateTimeInterface                                      β”‚
β”‚  - UnitEnum (PHP 8.1+)                                    β”‚
β”‚  - Nette\Utils\Paginator                                  β”‚
β”‚  - Baraja\Localization\Translation                        β”‚
β”‚  - Baraja\EcommerceStandard\DTO\PriceInterface            β”‚
β”‚  - Objects with __toString() method                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

βš™οΈ Core Components

Serializer

The main class responsible for converting PHP data structures to arrays. It can be used as a singleton via Serializer::get() or instantiated with a custom convention.

Key features:

  • Processes objects using reflection to access all properties (including private)
  • Automatically skips internal properties (those starting with _)
  • Tracks object instances to detect circular references
  • Enforces depth limit to prevent stack overflow

SerializerConvention

Configuration class that controls serialization behavior:

Property Default Description
dateTimeFormat 'Y-m-d H:i:s' Format for DateTime serialization
rewriteTooStringMethod true Use __toString() for stringable objects
rewriteNullToUndefined false Remove null values from output
keysToHide ['password', 'passwd', ...] Keys containing sensitive data

Bridge Interfaces

ItemsListInterface - For objects representing a collection of items:

interface ItemsListInterface
{
    /** @return array<int, array<string, mixed>> */
    public function getData(): array;
}

StatusCountInterface - For status objects with a count (useful for filters, tabs):

interface StatusCountInterface
{
    public function getKey(): string;
    public function getLabel(): string;
    public function getCount(): int;
}

πŸ“¦ Installation

It's best to use Composer for installation, and you can also find the package on Packagist and GitHub.

To install, simply use the command:

$ composer require baraja-core/serializer

You can use the package manually by creating an instance of the internal classes.

Requirements

  • PHP 8.0 or higher
  • No required dependencies (optional integrations available)

πŸš€ Basic Usage

Simple DTO Serialization

use Baraja\Serializer\Serializer;

class UserDTO
{
    public function __construct(
        public string $name,
        public string $email,
        public int $age,
    ) {
    }
}

$serializer = Serializer::get();

$user = new UserDTO(
    name: 'Jan Barasek',
    email: 'jan@example.com',
    age: 30,
);

$result = $serializer->serialize($user);
// Result: ['name' => 'Jan Barasek', 'email' => 'jan@example.com', 'age' => 30]

Nested Objects

class AddressDTO
{
    public function __construct(
        public string $street,
        public string $city,
    ) {
    }
}

class PersonDTO
{
    public function __construct(
        public string $name,
        public AddressDTO $address,
    ) {
    }
}

$person = new PersonDTO(
    name: 'Jan',
    address: new AddressDTO(street: 'Main St', city: 'Prague'),
);

$result = $serializer->serialize($person);
// Result: [
//     'name' => 'Jan',
//     'address' => ['street' => 'Main St', 'city' => 'Prague']
// ]

Array Serialization

$data = [
    'users' => [
        new UserDTO('Alice', 'alice@example.com', 25),
        new UserDTO('Bob', 'bob@example.com', 30),
    ],
    'total' => 2,
];

$result = $serializer->serialize($data);

DateTime Handling

class EventDTO
{
    public function __construct(
        public string $title,
        public \DateTimeInterface $startDate,
    ) {
    }
}

$event = new EventDTO(
    title: 'Conference',
    startDate: new \DateTime('2024-06-15 10:00:00'),
);

$result = $serializer->serialize($event);
// Result: ['title' => 'Conference', 'startDate' => '2024-06-15 10:00:00']

Enum Serialization (PHP 8.1+)

enum Status: string
{
    case Active = 'active';
    case Inactive = 'inactive';
}

class ItemDTO
{
    public function __construct(
        public string $name,
        public Status $status,
    ) {
    }
}

$item = new ItemDTO(name: 'Product', status: Status::Active);

$result = $serializer->serialize($item);
// Result: ['name' => 'Product', 'status' => 'active']

πŸ›‘οΈ Security Features

Automatic Password Protection

The serializer automatically detects and hides sensitive keys. When a sensitive key is found, its value is replaced with *****:

class LoginDTO
{
    public function __construct(
        public string $username,
        public string $password,
    ) {
    }
}

$login = new LoginDTO(username: 'admin', password: 'secret123');

$result = $serializer->serialize($login);
// Result: ['username' => 'admin', 'password' => '*****']

Default hidden keys: password, passwd, pass, pwd, creditcard, credit card, cc, pin

Exception: BCrypt hashes (format $2[ayb]$...) are allowed through, as they are already securely hashed.

Internal Property Protection

Properties starting with underscore (_) are automatically excluded from serialization:

class EntityDTO
{
    public function __construct(
        public string $name,
        public string $_internalId, // Will be excluded
    ) {
    }
}

Recursion Detection

The serializer tracks object instances and throws an exception if circular references are detected:

class Node
{
    public function __construct(
        public string $name,
        public ?Node $parent = null,
    ) {
    }
}

$node = new Node('A');
$node->parent = $node; // Circular reference

$serializer->serialize($node);
// Throws: InvalidArgumentException with recursion warning

πŸ”§ Advanced Usage

Custom Convention

Create a custom convention by extending SerializerConvention:

use Baraja\Serializer\Serializer;
use Baraja\Serializer\SerializerConvention;

class CustomConvention extends SerializerConvention
{
    private string $dateTimeFormat = 'c'; // ISO 8601
    private bool $rewriteNullToUndefined = true;
}

$convention = new CustomConvention();
$serializer = new Serializer($convention);

Using Bridge Interfaces

ItemsListInterface

Implement this interface for paginated or list results:

use Baraja\Serializer\Bridge\ItemsListInterface;

class UserList implements ItemsListInterface
{
    /** @param UserDTO[] $users */
    public function __construct(
        private array $users,
    ) {
    }

    public function getData(): array
    {
        return array_map(
            fn(UserDTO $user) => [
                'name' => $user->name,
                'email' => $user->email,
            ],
            $this->users,
        );
    }
}

Convention: ItemsListInterface objects must be placed in a key named items.

StatusCountInterface

Useful for filter tabs or status counts:

use Baraja\Serializer\Bridge\StatusCountInterface;

class OrderStatus implements StatusCountInterface
{
    public function __construct(
        private string $key,
        private string $label,
        private int $count,
    ) {
    }

    public function getKey(): string
    {
        return $this->key;
    }

    public function getLabel(): string
    {
        return $this->label;
    }

    public function getCount(): int
    {
        return $this->count;
    }
}

$statuses = [
    new OrderStatus('pending', 'Pending', 5),
    new OrderStatus('completed', 'Completed', 42),
];

$result = $serializer->serialize($statuses);
// Result: [
//     ['key' => 'pending', 'label' => 'Pending', 'count' => 5],
//     ['key' => 'completed', 'label' => 'Completed', 'count' => 42],
// ]

Nette Paginator Support

If nette/utils is installed, Paginator objects are automatically serialized:

use Nette\Utils\Paginator;

$paginator = new Paginator();
$paginator->setItemCount(100);
$paginator->setItemsPerPage(10);
$paginator->setPage(3);

$result = $serializer->serialize(['paginator' => $paginator]);
// Result: [
//     'paginator' => [
//         'page' => 3,
//         'pageCount' => 10,
//         'itemCount' => 100,
//         'itemsPerPage' => 10,
//         'firstPage' => 1,
//         'lastPage' => 10,
//         'isFirstPage' => false,
//         'isLastPage' => false,
//     ]
// ]

Convention: Paginator objects must be placed in a key named paginator.

Translation Support

If baraja-core/localization is installed, Translation objects are converted to strings:

use Baraja\Localization\Translation;

$translation = new Translation(['en' => 'Hello', 'cs' => 'Ahoj']);

$result = $serializer->serialize(['greeting' => $translation]);
// Result: ['greeting' => 'Hello'] (based on current locale)

Price Support

If baraja-core/ecommerce-standard is installed, PriceInterface objects are serialized:

// Assuming $price implements PriceInterface

$result = $serializer->serialize(['price' => $price]);
// Result: [
//     'price' => [
//         'value' => '199.00',
//         'currency' => 'USD',
//         'html' => '$199.00',
//         'isFree' => false,
//     ]
// ]

⚠️ Conventions and Constraints

Enforced Conventions

  1. ItemsListInterface must be in key items
  2. Paginator must be in key paginator
  3. Maximum depth is 32 levels
  4. Properties starting with _ are excluded

Error Handling

The serializer throws exceptions for:

  • LogicException: Structure depth exceeds 32 levels
  • InvalidArgumentException: Circular reference detected
  • InvalidArgumentException: ItemsListInterface not in items key
  • InvalidArgumentException: Paginator not in paginator key
  • InvalidArgumentException: Unsupported value type

⚠️ Security Logging

When a sensitive key with a non-BCrypt value is detected, the serializer logs a critical warning via Tracy Debugger (if available):

Security warning: User password may have been compromised!
The Baraja API prevented passwords being passed through the API in a readable form.

This helps identify potential security issues during development.

πŸ‘₯ Author

Jan Barasek - https://baraja.cz

πŸ“„ License

baraja-core/serializer is licensed under the MIT license. See the LICENSE file for more details.