Fixed the messy auto-generated serde logic in the library
@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()andObjectSerializer::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)orModelClass::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
ObjectSerializeris 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.jsonincludessymfony/serializer,symfony/property-access,symfony/property-info. Considersymfony/serializer-pack. - Null Handling: The default behavior of Symfony Serializer regarding nulls should be reviewed. Context options like
AbstractObjectNormalizer::SKIP_NULL_VALUEScan be used intoJson/toArrayif 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
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de zitadel/client-php
-
Release SDK for V5Quizá libre de nuevo @mridang la tomó hace 299 días y no hay ningún pull request abierto. Abierto
zitadel/client-php#58 · 1 asignado ·
Todos los issues de zitadel/client-php
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Automattic/static-site-importer#1879 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
521xueweihan/HelloGitHub#3790 ·
-
[Bug] Feed date, title and author too long to fit inside article box on smaller screens, mobileAbiertoBug (unconfirmed) Good first issue 1️⃣ help wanted UI :art:
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
FreshRSS/FreshRSS#9360 · 2 comentarios ·
Los mantenedores suelen responder en 1 día