Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(php): types + enums #4645

Merged
merged 24 commits into from
Sep 18, 2024
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 0 additions & 1 deletion generators/csharp/model/src/object/ObjectGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
import { ModelCustomConfigSchema } from "../ModelCustomConfig";
import { ModelGeneratorContext } from "../ModelGeneratorContext";
import { ExampleGenerator } from "../snippets/ExampleGenerator";
import { getUndiscriminatedUnionSerializerAnnotation } from "../undiscriminated-union/getUndiscriminatedUnionSerializerAnnotation";

export class ObjectGenerator extends FileGenerator<CSharpFile, ModelCustomConfigSchema, ModelGeneratorContext> {
private readonly typeDeclaration: TypeDeclaration;
Expand Down
23 changes: 16 additions & 7 deletions generators/php/codegen/src/asIs/SerializableType.Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ abstract class SerializableType
{
/**
* Serializes the object to a JSON string.
*
* @throws JsonException
*/
public function toJson(): string
{
Expand All @@ -35,6 +33,9 @@ public function toArray(): array

foreach ($reflectionClass->getProperties() as $property) {
$jsonKey = self::getJsonKey($property);
if ($jsonKey == null) {
continue;
}
$value = $property->getValue($this);

// Handle DateTime properties
Expand Down Expand Up @@ -121,6 +122,10 @@ private static function serializeSingleValue(mixed $data, string $type): mixed
return $type === 'date' ? $data->format(Constant::DateFormat) : $data->format(Constant::DateTimeFormat);
}

if ($type === 'mixed') {
return $data;
}

if (class_exists($type) && $data instanceof $type) {
if (method_exists($data, 'toArray')) {
return $data->toArray();
Expand Down Expand Up @@ -178,12 +183,12 @@ private static function serializeList(array $data, array $type): array
* Helper function to retrieve the JSON key for a property.
*
* @param ReflectionProperty $property
* @return string
* @return ?string
*/
private static function getJsonKey(ReflectionProperty $property): string
private static function getJsonKey(ReflectionProperty $property): ?string
{
$jsonPropertyAttr = $property->getAttributes(JsonProperty::class)[0] ?? null;
return $jsonPropertyAttr ? $jsonPropertyAttr->newInstance()->name : $property->getName();
return $jsonPropertyAttr?->newInstance()?->name;
}

/**
Expand Down Expand Up @@ -226,7 +231,7 @@ private static function isMapType(array $type): bool
*/
public static function fromJson(string $json): static
{
$arrayData = json_decode($json, true);
$arrayData = json_decode($json, true, 512, JSON_THROW_ON_ERROR);

if (!is_array($arrayData)) {
throw new InvalidArgumentException('Invalid JSON provided or JSON does not decode to an array.');
Expand Down Expand Up @@ -261,7 +266,7 @@ public static function fromArray(array $data): static
continue;
}

$jsonKey = self::getJsonKey($property);
$jsonKey = self::getJsonKey($property) ?? $property->getName();
if (array_key_exists($jsonKey, $data)) {
$value = $data[$jsonKey];

Expand Down Expand Up @@ -358,6 +363,10 @@ private static function deserializeSingleValue(mixed $data, string $type): mixed
return new DateTime($data);
}

if ($type === 'mixed') {
return $data;
}

if (class_exists($type) && is_array($data)) {
return $type::fromArray($data);
}
Expand Down
28 changes: 28 additions & 0 deletions generators/php/codegen/src/ast/Array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { AstNode } from "./core/AstNode";
import { Writer } from "./core/Writer";

export declare namespace Array_ {
interface Args {
entries: AstNode[] | undefined;
}
}

export class Array_ extends AstNode {
private entries: AstNode[];

constructor({ entries }: Array_.Args) {
super();
this.entries = entries ?? [];
}

public write(writer: Writer): void {
writer.write("[");
this.entries.forEach((entry, index) => {
if (index > 0) {
writer.write(", ");
}
entry.write(writer);
});
writer.write("]");
}
}
39 changes: 39 additions & 0 deletions generators/php/codegen/src/ast/Attribute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { AstNode } from "./core/AstNode";
import { ClassReference } from "./ClassReference";
import { Writer } from "./core/Writer";

export declare namespace Attribute {
interface Args {
/* Reference to the attribute class reference */
reference: ClassReference;

arguments?: (string | AstNode)[];
}
}

export class Attribute extends AstNode {
private reference: ClassReference;
private arguments: (string | AstNode)[];

constructor(args: Attribute.Args) {
super();
this.reference = args.reference;
this.arguments = args.arguments ?? [];
}

public write(writer: Writer): void {
writer.addReference(this.reference);
writer.write(`${this.reference.name}`);
if (this.arguments.length > 0) {
writer.write("(");
for (const argument of this.arguments) {
if (typeof argument === "string") {
writer.write(argument);
} else {
argument.write(writer);
}
}
writer.write(")");
}
}
}
12 changes: 10 additions & 2 deletions generators/php/codegen/src/ast/Class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export declare namespace Class {
abstract?: boolean;
/* Docs associated with the class */
docs?: string;
/* The class to inherit from if any */
parentClassReference?: AstNode;
}

interface Constructor {
Expand All @@ -33,16 +35,18 @@ export class Class extends AstNode {
public readonly namespace: string;
public readonly abstract: boolean;
public readonly docs: string | undefined;
public readonly parentClassReference: AstNode | undefined;

private fields: Field[] = [];
private constructor_: Class.Constructor | undefined;

constructor({ name, namespace, abstract, docs }: Class.Args) {
constructor({ name, namespace, abstract, docs, parentClassReference }: Class.Args) {
super();
this.name = name;
this.namespace = namespace;
this.abstract = abstract ?? false;
this.docs = docs;
this.parentClassReference = parentClassReference;
}

public addField(field: Field): void {
Expand All @@ -58,7 +62,11 @@ export class Class extends AstNode {
writer.write("abstract ");
}
this.writeComment(writer);
writer.writeLine(`class ${this.name}`);
writer.writeLine(`class ${this.name} `);
if (this.parentClassReference != null) {
writer.write("extends ");
this.parentClassReference.write(writer);
}
writer.writeLine("{");
writer.indent();
for (const field of this.fields) {
Expand Down
77 changes: 77 additions & 0 deletions generators/php/codegen/src/ast/Enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { Comment } from "./Comment";
import { AstNode } from "./core/AstNode";
import { Writer } from "./core/Writer";

export declare namespace Enum {
interface Args {
/* The name of the PHP enum */
name: string;
/* The namespace of the PHP enum*/
namespace: string;
/* If present, specifies that the enum is backed by this type */
backing?: "string" | "int";
/* Docs associated with the class */
docs?: string;
}

interface Member {
/* The name of the enum field */
name: string;
/* The value of the enum field */
value?: string | number;
}
}

export class Enum extends AstNode {
public readonly name: string;
public readonly namespace: string;
public readonly backing: "string" | "int" | undefined;
public readonly docs: string | undefined;
public readonly members: Enum.Member[] = [];

constructor({ name, namespace, backing, docs }: Enum.Args) {
super();
this.name = name;
this.namespace = namespace;
this.backing = backing;
this.docs = docs;
}

public addMember(member: Enum.Member): void {
this.members.push(member);
}

public write(writer: Writer): void {
this.writeComment(writer);
writer.write("enum ");
writer.writeLine(`${this.name}`);
if (this.backing != null) {
writer.write(` : ${this.backing}`);
}
writer.writeLine(" {");

writer.indent();
for (const member of this.members) {
writer.write(`case ${member.name}`);
if (member.value != null) {
if (typeof member.value === "string") {
writer.write(` = "${member.value}"`);
} else {
writer.write(` = ${member.value}`);
}
}
writer.writeTextStatement("");
}
writer.writeNewLineIfLastLineNot();
writer.dedent();
writer.writeLine("}");
}

public writeComment(writer: Writer): void {
if (this.docs == null) {
return undefined;
}
const comment = new Comment({ docs: this.docs });
comment.write(writer);
}
}
25 changes: 22 additions & 3 deletions generators/php/codegen/src/ast/Field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AstNode } from "./core/AstNode";
import { Writer } from "./core/Writer";
import { Type } from "./Type";
import { Comment } from "./Comment";
import { Attribute } from "./Attribute";

export declare namespace Field {
interface Args {
Expand All @@ -21,6 +22,8 @@ export declare namespace Field {
docs?: string;
/* Docs included in-line */
inlineDocs?: string;
/* Field attributes */
attributes?: Attribute[];
}
}

Expand All @@ -32,8 +35,9 @@ export class Field extends AstNode {
private initializer: CodeBlock | undefined;
private docs: string | undefined;
private inlineDocs: string | undefined;
private attributes: Attribute[];

constructor({ name, type, access, readonly_, initializer, docs, inlineDocs }: Field.Args) {
constructor({ name, type, access, readonly_, initializer, docs, inlineDocs, attributes }: Field.Args) {
super();
this.name = name;
this.type = type;
Expand All @@ -42,9 +46,11 @@ export class Field extends AstNode {
this.initializer = initializer;
this.docs = docs;
this.inlineDocs = inlineDocs;
this.attributes = attributes ?? [];
}

public write(writer: Writer): void {
this.writeAttributesIfPresent(writer);
this.writeComment(writer);

writer.write(`${this.access} `);
Expand All @@ -53,7 +59,7 @@ export class Field extends AstNode {
}

this.type.write(writer);
writer.write(` ${this.name}`);
writer.write(` ${this.name.startsWith("$") ? "" : "$"}${this.name}`);
dcb6 marked this conversation as resolved.
Show resolved Hide resolved

if (this.initializer != null) {
writer.write(" = ");
Expand All @@ -72,9 +78,22 @@ export class Field extends AstNode {
comment.addTag({
tagType: "var",
type: this.type,
name: this.name,
name: `${this.name.startsWith("$") ? "" : "$"}${this.name}`,
docs: this.docs
});
comment.write(writer);
}

private writeAttributesIfPresent(writer: Writer): void {
if (this.attributes.length > 0) {
writer.write("#[");
this.attributes.forEach((attribute, index) => {
if (index > 0) {
writer.write(", ");
}
attribute.write(writer);
});
writer.writeLine("]");
}
}
}
19 changes: 7 additions & 12 deletions generators/php/codegen/src/ast/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,23 @@ export declare namespace Map {
}

export class Map extends AstNode {
private entries: Map.Entry[] | undefined;
private entries: Map.Entry[];

constructor({ entries }: Map.Args) {
super();
this.entries = entries;
this.entries = entries ?? [];
}

public write(writer: Writer): void {
if (this.entries == null) {
writer.write("[]");
return;
}

writer.writeLine("[");
writer.indent();
for (const { key, value } of this.entries) {
writer.write("[");
for (const [index, { key, value }] of this.entries.entries()) {
if (index > 0) {
writer.write(",");
}
key.write(writer);
writer.write(" => ");
value.write(writer);
writer.writeLine(", ");
}
writer.dedent();
writer.write("]");
}
}
Loading
Loading