andreypostal/json-handler-php

View on GitHub
src/JsonSerializerTrait.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
namespace Andrey\JsonHandler;

use Andrey\JsonHandler\Attributes\JsonItemAttribute;
use Andrey\JsonHandler\Attributes\JsonObjectAttribute;
use JsonException;
use ReflectionClass;
use ReflectionException;

trait JsonSerializerTrait
{
    /**
     * @throws ReflectionException
     * @throws JsonException
     */
    public function serialize(object $obj): array
    {
        $class = new ReflectionClass($obj);

        $skipAttributeCheck = ($class->getAttributes(JsonObjectAttribute::class)[0] ?? null) !== null;
        $output = [];
        $properties = $class->getProperties();
        foreach ($properties as $property) {
            $attributes = $property->getAttributes(JsonItemAttribute::class);
            $attr = $attributes[0] ?? null;
            if ($attr === null && !$skipAttributeCheck) {
                continue;
            }
            /** @var JsonItemAttribute $item */
            $item = $attr?->newInstance() ?? new JsonItemAttribute();
            $key = $item->key ?? $property->name;

            if ($property->getType()?->isBuiltin()) {
                $output[$key] = $this->handleArray($item, $property->getValue($obj));
                continue;
            }

            $class = new ReflectionClass($property->getValue($obj));
            if ($class->isEnum()) {
                $output[$key] = $property->getValue($obj)->value;
                continue;
            }
            $output[$key] = $this->serialize($property->getValue($obj));
        }
        return $output;
    }

    /**
     * @param JsonItemAttribute $item
     * @param mixed $value
     * @return mixed
     * @throws ReflectionException
     * @throws JsonException
     *
     * @noinspection GetTypeMissUseInspection
     */
    private function handleArray(JsonItemAttribute $item, mixed $value): mixed
    {
        if (gettype($value) !== 'array') {
            return $value;
        }

        if (!class_exists($item->type)) {
            return $value;
        }
        $class = new ReflectionClass($item->type);
        $isEnum = $class->isEnum();

        return array_reduce(
            array: $value,
            callback: function(array $l, mixed $c) use ($isEnum): array {
                if ($isEnum) {
                    $v = $c->value;
                } else {
                    $v = $this->serialize($c);
                }
                $l[] = $v;
                return $l;
            },
            initial: [],
        );
    }
}