Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

Fixed the messy auto-generated serde logic in the library

Abierto
#21 0 comentarios 0 reacciones 1 asignado Ver en GitHub

@mridang ya está trabajando en esto.

Desde el 7/5/2025.

Evaluación

Este issue todavía no se ha evaluado.

Descripción

Title: Refactor PHP SDK Models with Symfony Serializer via ZitadelModel Base Class

Description:
This initiative will refactor the PHP SDK's model classes to leverage the Symfony Serializer component. The goal is to significantly reduce boilerplate, improve maintainability, and adopt a more standard approach to JSON serialization and deserialization by introducing a common ZitadelModel base class and updating model generation templates.

Problem:

The current PHP models, generated by OpenAPI Generator, include substantial duplicated code for serialization/deserialization (e.g., static metadata arrays like $openAPITypes, $attributeMap, and reliance on a custom global ObjectSerializer). This leads to:

  • Increased maintenance overhead for models.
  • Less idiomatic PHP and a steeper learning curve.
  • Potential inconsistencies compared to using a mature, standard serialization library.

Impact:

Refactoring will result in:

  • Slimmer, cleaner, and more maintainable model classes.
  • Easier adoption of modern PHP features within models.
  • Standardized and robust JSON handling powered by Symfony Serializer.
  • Reduced risk of bugs associated with custom serialization logic.
  • Improved developer experience when working with SDK models.

Solution / Tasks:

1. Implement ZitadelModel Base Class:
Create an abstract class ZitadelModel that encapsulates a pre-configured Symfony\Component\Serializer\SerializerInterface instance. This class will provide common JSON and array serialization/deserialization methods for all SDK models to inherit.

Example ZitadelModel.php (ensure all use statements are at the top of the file):

<?php
namespace Zitadel\Client\Model; // Or your chosen SDK namespace

use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Normalizer\UidNormalizer; // If using Symfony Uid components
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; // For context keys

abstract class ZitadelModel
{
    private static ?SerializerInterface $serializer = null;

    protected static function getSerializer(): SerializerInterface
    {
        if (self::$serializer === null) {
            $classMetadataFactory = new ClassMetadataFactory(new AttributeLoader());
            $nameConverter = new MetadataAwareNameConverter($classMetadataFactory);
            $reflectionExtractor = new ReflectionExtractor(); // Define once
            $phpDocExtractor = new PhpDocExtractor(); // Define once
            $propertyInfoExtractor = new PropertyInfoExtractor(
                [$reflectionExtractor], // list extractors
                [$phpDocExtractor, $reflectionExtractor], // type extractors
                [$phpDocExtractor], // description extractors
                [$reflectionExtractor], // access extractors
                [$reflectionExtractor]  // mutator extractors
            );
            $normalizers = [
                new DateTimeNormalizer(),
                new UidNormalizer(), // Optional, if using UIDs
                new ArrayDenormalizer(),
                new ObjectNormalizer(
                    $classMetadataFactory, $nameConverter, null, $propertyInfoExtractor
                )
            ];
            $encoders = [new JsonEncoder()];
            self::$serializer = new Serializer($normalizers, $encoders);
        }
        return self::$serializer;
    }

    public function toJson(array $context = []): string
    {
        // Example: $context[AbstractObjectNormalizer::SKIP_NULL_VALUES] = true;
        return static::getSerializer()->serialize($this, JsonEncoder::FORMAT, $context);
    }

    public static function fromJson(string $jsonString, array $context = []): static
    {
        return static::getSerializer()->deserialize($jsonString, static::class, JsonEncoder::FORMAT, $context);
    }

    public function toArray(array $context = []): array
    {
        $data = static::getSerializer()->normalize($this, null, $context);
        return is_array($data) ? $data : (array) $data; // Ensure array output
    }

    public static function fromArray(array $data, array $context = []): static
    {
        return static::getSerializer()->denormalize($data, static::class, null, $context);
    }
}

2. Update OpenAPI Generator Templates for PHP Models:
Modify the OpenAPI Generator templates for PHP models to:

  • Make generated models extend ZitadelModel.
  • Generate PHP typed properties (PHP 7.4+) for all model fields, ideally using constructor property promotion (PHP 8.0+).
  • Use #[Symfony\Component\Serializer\Attribute\SerializedName("jsonKey")] for mapping JSON keys to PHP property names where they differ.
  • Remove the old boilerplate code (static metadata arrays, $container, custom ser/des logic).
  • Ensure enums are generated as PHP 8.1+ backed enums if possible, or as simple string/int properties compatible with Symfony Serializer.

Target structure for a generated model (e.g., UserServiceUser.php):

<?php
namespace Zitadel\Client\Model; // Or your chosen SDK namespace

use Symfony\Component\Serializer\Attribute\SerializedName;
// Assuming other models (UserServiceDetails etc.) also extend ZitadelModel
// Assuming UserServiceUserState is an Enum or another ZitadelModel

class UserServiceUser extends ZitadelModel
{
    public function __construct(
        #[SerializedName("userId")]
        public ?string $userId = null,
        public ?UserServiceDetails $details = null,
        public ?UserServiceUserState $state = null, // Or your Enum type
        public ?string $username = null,
        #[SerializedName("loginNames")]
        /** @var string[]|null */ // PHPDoc for generics if needed
        public ?array $loginNames = null,
        #[SerializedName("preferredLoginName")]
        public ?string $preferredLoginName = null,
        public ?UserServiceHumanUser $human = null,
        public ?UserServiceMachineUser $machine = null
    ) {}
}

3. Update SDK Code to Use New Model Methods:

  • Search the SDK codebase for usages of the old global ObjectSerializer::sanitizeForSerialization() and ObjectSerializer::deserialize().
  • Replace these calls with the new methods from ZitadelModel:
    • ObjectSerializer::sanitizeForSerialization($instance) -> $instance->toArray() or $instance->toJson().
    • ObjectSerializer::deserialize($data, ModelClass::class) -> ModelClass::fromJson($jsonData) or ModelClass::fromArray($arrayData).
  • The aim is to eliminate the models' dependency on ObjectSerializer.

Expected Outcomes:

  • PHP SDK models are significantly leaner, primarily containing property definitions and inheriting ser/des logic.
  • JSON serialization and deserialization are handled robustly by the Symfony Serializer component, configured in ZitadelModel.
  • The custom global ObjectSerializer is no longer used for model transformations.
  • The SDK is easier to maintain and aligns better with modern PHP practices.
  • Functional equivalence for JSON-based API interactions is preserved.

Additional Notes:

  • Dependencies: Ensure composer.json includes symfony/serializer, symfony/property-access, symfony/property-info. Consider symfony/serializer-pack.
  • Null Handling: The default behavior of Symfony Serializer regarding nulls should be reviewed. Context options like AbstractObjectNormalizer::SKIP_NULL_VALUES can be used in toJson/toArray if specific null omission behavior is required.
  • Testing: Thorough testing of serialization/deserialization for various model types (including nested objects, arrays, and different data types) will be crucial.
  • Discriminators: Functionality related to discriminators is explicitly out of scope for this task and can be addressed separately if needed.
Lenguaje dominante
PHP
Estrellas
5
Forks
2
Merge medio
8 min
PR fusionados (30 d)
1

Preparar el entorno

Aún no hemos revisado los archivos de configuración de este proyecto. Empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de zitadel/client-php

Todos los issues de zitadel/client-php

Issues similares

Más issues de PHP

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.