diff --git a/generators/php/codegen/src/AsIs.ts b/generators/php/codegen/src/AsIs.ts index 648935c3bdb..bb332e401a2 100644 --- a/generators/php/codegen/src/AsIs.ts +++ b/generators/php/codegen/src/AsIs.ts @@ -25,6 +25,7 @@ export enum AsIsFiles { DateArrayTypeTest = "Json/DateArrayTypeTest.Template.php", EmptyArraysTest = "Json/EmptyArraysTest.Template.php", EnumTest = "Json/EnumTest.Template.php", + TraitTest = "Json/TraitTest.Template.php", InvalidTypesTest = "Json/InvalidTypesTest.Template.php", MixedDateArrayTypeTest = "Json/MixedDateArrayTypeTest.Template.php", NestedUnionArrayTypeTest = "Json/NestedUnionArrayTypeTest.Template.php", diff --git a/generators/php/codegen/src/asIs/Json/TraitTest.Template.php b/generators/php/codegen/src/asIs/Json/TraitTest.Template.php new file mode 100644 index 00000000000..26178b5ea81 --- /dev/null +++ b/generators/php/codegen/src/asIs/Json/TraitTest.Template.php @@ -0,0 +1,61 @@ +; + +use <%= coreNamespace%>\Json\SerializableType; +use <%= coreNamespace%>\Json\JsonProperty; +use PHPUnit\Framework\TestCase; + +trait IntegerPropertyTrait +{ + /** + * @var int $integerProperty + */ + #[JsonProperty('integer_property')] + public int $integerProperty; +} + +class TypeWithTrait extends SerializableType +{ + use IntegerPropertyTrait; + + /** + * @var string $stringProperty + */ + #[JsonProperty('string_property')] + public string $stringProperty; + + /** + * @param array{ + * integerProperty: int, + * stringProperty: string, + * } $values + */ + public function __construct(array $values) + { + $this->integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} \ No newline at end of file diff --git a/generators/php/codegen/src/ast/Class.ts b/generators/php/codegen/src/ast/Class.ts index abe94e66edf..7b26e5a8813 100644 --- a/generators/php/codegen/src/ast/Class.ts +++ b/generators/php/codegen/src/ast/Class.ts @@ -7,6 +7,8 @@ import { Field } from "./Field"; import { Method } from "./Method"; import { Comment } from "./Comment"; import { orderByAccess } from "./utils/orderByAccess"; +import { ClassReference } from "./ClassReference"; +import { Trait } from "./Trait"; export declare namespace Class { interface Args { @@ -20,6 +22,8 @@ export declare namespace Class { docs?: string; /* The class to inherit from if any */ parentClassReference?: AstNode; + /* The traits that this class uses, if any */ + traits?: ClassReference[]; } interface Constructor { @@ -38,18 +42,20 @@ export class Class extends AstNode { public readonly abstract: boolean; public readonly docs: string | undefined; public readonly parentClassReference: AstNode | undefined; + public readonly traits: ClassReference[]; public readonly fields: Field[] = []; public readonly methods: Method[] = []; private constructor_: Class.Constructor | undefined; - constructor({ name, namespace, abstract, docs, parentClassReference }: Class.Args) { + constructor({ name, namespace, abstract, docs, parentClassReference, traits }: Class.Args) { super(); this.name = name; this.namespace = namespace; this.abstract = abstract ?? false; this.docs = docs; this.parentClassReference = parentClassReference; + this.traits = traits ?? []; } public addConstructor(constructor: Class.Constructor): void { @@ -64,6 +70,10 @@ export class Class extends AstNode { this.methods.push(method); } + public addTrait(traitClassReference: ClassReference): void { + this.traits.push(traitClassReference); + } + public write(writer: Writer): void { if (this.abstract) { writer.write("abstract "); @@ -77,6 +87,17 @@ export class Class extends AstNode { writer.newLine(); writer.writeLine("{"); writer.indent(); + if (this.traits.length > 0) { + writer.write("use "); + this.traits.forEach((trait, index) => { + if (index > 0) { + writer.write(","); + } + writer.writeNode(trait); + }); + writer.writeTextStatement(""); + writer.newLine(); + } this.writeFields({ writer, fields: orderByAccess(this.fields) }); if (this.constructor != null || this.methods.length > 0) { @@ -141,13 +162,15 @@ export class Class extends AstNode { } private writeFields({ writer, fields }: { writer: Writer; fields: Field[] }): void { - fields.forEach((field, index) => { - if (index > 0) { - writer.newLine(); - } - field.write(writer); - writer.writeNewLineIfLastLineNot(); - }); + fields + .filter((field) => !field.inherited) + .forEach((field, index) => { + if (index > 0) { + writer.newLine(); + } + field.write(writer); + writer.writeNewLineIfLastLineNot(); + }); } private writeMethods({ writer, methods }: { writer: Writer; methods: Method[] }): void { diff --git a/generators/php/codegen/src/ast/DataClass.ts b/generators/php/codegen/src/ast/DataClass.ts index f3c5e3efa9a..f2e4b3a6f8c 100644 --- a/generators/php/codegen/src/ast/DataClass.ts +++ b/generators/php/codegen/src/ast/DataClass.ts @@ -9,6 +9,8 @@ import { Type } from "./Type"; import { orderByAccess } from "./utils/orderByAccess"; import { php } from ".."; import { convertFromPhpVariableName } from "./utils/convertFromPhpVariableName"; +import { Trait } from "./Trait"; +import { ClassReference } from "../php"; const CONSTRUCTOR_PARAMETER_NAME = "values"; @@ -21,11 +23,11 @@ export class DataClass extends AstNode { public readonly namespace: string; private class_: Class; - constructor({ name, namespace, abstract, docs, parentClassReference }: DataClass.Args) { + constructor({ name, namespace, abstract, docs, parentClassReference, traits }: DataClass.Args) { super(); this.name = name; this.namespace = namespace; - this.class_ = new Class({ name, namespace, abstract, docs, parentClassReference }); + this.class_ = new Class({ name, namespace, abstract, docs, parentClassReference, traits }); } public addField(field: Field): void { @@ -35,6 +37,9 @@ export class DataClass extends AstNode { public addMethod(method: Method): void { this.class_.addMethod(method); } + public addTrait(traitClassRefeference: ClassReference): void { + this.class_.addTrait(traitClassRefeference); + } public write(writer: Writer): void { const orderedFields = orderByAccess(this.class_.fields).map( diff --git a/generators/php/codegen/src/ast/Field.ts b/generators/php/codegen/src/ast/Field.ts index 8675cb4a10c..fd836a7f359 100644 --- a/generators/php/codegen/src/ast/Field.ts +++ b/generators/php/codegen/src/ast/Field.ts @@ -25,6 +25,8 @@ export declare namespace Field { inlineDocs?: string; /* Field attributes */ attributes?: Attribute[]; + /* Indicates that this field is inherited and should not be written to the class. */ + inherited?: boolean; } } @@ -37,8 +39,9 @@ export class Field extends AstNode { private docs: string | undefined; private inlineDocs: string | undefined; private attributes: Attribute[]; + public readonly inherited: boolean; - constructor({ name, type, access, readonly_, initializer, docs, inlineDocs, attributes }: Field.Args) { + constructor({ name, type, access, readonly_, initializer, docs, inlineDocs, attributes, inherited }: Field.Args) { super(); this.name = convertToPhpVariableName(name); this.type = type; @@ -48,6 +51,7 @@ export class Field extends AstNode { this.docs = docs; this.inlineDocs = inlineDocs; this.attributes = attributes ?? []; + this.inherited = inherited ?? false; } public write(writer: Writer): void { diff --git a/generators/php/codegen/src/ast/Trait.ts b/generators/php/codegen/src/ast/Trait.ts new file mode 100644 index 00000000000..a3a10a2264b --- /dev/null +++ b/generators/php/codegen/src/ast/Trait.ts @@ -0,0 +1,101 @@ +import { AstNode } from "./core/AstNode"; +import { Writer } from "./core/Writer"; +import { Field } from "./Field"; +import { Method } from "./Method"; +import { Comment } from "./Comment"; +import { orderByAccess } from "./utils/orderByAccess"; +import { ClassReference } from "./ClassReference"; + +export declare namespace Trait { + interface Args { + /* The name of the PHP trait */ + name: string; + /* The namespace of the PHP trait */ + namespace: string; + /* Docs associated with the trait */ + docs?: string; + /* The traits that this trait uses, if any */ + traits?: ClassReference[]; + } +} + +export class Trait extends AstNode { + public readonly name: string; + public readonly namespace: string; + public readonly docs: string | undefined; + public readonly traits: ClassReference[]; + + public readonly fields: Field[] = []; + public readonly methods: Method[] = []; + + constructor({ name, namespace, docs, traits }: Trait.Args) { + super(); + this.name = name; + this.namespace = namespace; + this.docs = docs; + this.traits = traits ?? []; + } + + public addField(field: Field): void { + this.fields.push(field); + } + + public addMethod(method: Method): void { + this.methods.push(method); + } + + public write(writer: Writer): void { + this.writeComment(writer); + writer.write(`trait ${this.name} `); + writer.newLine(); + writer.writeLine("{"); + writer.indent(); + + if (this.traits.length > 0) { + writer.write("use "); + this.traits.forEach((trait, index) => { + if (index > 0) { + writer.write(","); + } + writer.writeNode(trait); + }); + writer.writeTextStatement(""); + writer.newLine(); + } + + this.writeFields({ writer, fields: orderByAccess(this.fields) }); + this.writeMethods({ writer, methods: orderByAccess(this.methods) }); + + writer.dedent(); + writer.writeLine("}"); + return; + } + + private writeComment(writer: Writer): void { + if (this.docs == null) { + return undefined; + } + const comment = new Comment({ docs: this.docs }); + comment.write(writer); + } + + private writeFields({ writer, fields }: { writer: Writer; fields: Field[] }): void { + fields.forEach((field, index) => { + if (index > 0) { + writer.newLine(); + } + field.write(writer); + writer.writeNewLineIfLastLineNot(); + }); + } + + private writeMethods({ writer, methods }: { writer: Writer; methods: Method[] }): void { + methods.forEach((method, index) => { + if (index > 0) { + writer.newLine(); + } + method.write(writer); + writer.writeNewLineIfLastLineNot(); + }); + } +} diff --git a/generators/php/codegen/src/ast/index.ts b/generators/php/codegen/src/ast/index.ts index f681bee66e4..eeec5368649 100644 --- a/generators/php/codegen/src/ast/index.ts +++ b/generators/php/codegen/src/ast/index.ts @@ -1,6 +1,7 @@ export { Array_ as Array } from "./Array"; export { Attribute } from "./Attribute"; export { Class } from "./Class"; +export { Trait } from "./Trait"; export { ClassInstantiation } from "./ClassInstantiation"; export { ClassReference } from "./ClassReference"; export { CodeBlock } from "./CodeBlock"; diff --git a/generators/php/codegen/src/constants.ts b/generators/php/codegen/src/constants.ts new file mode 100644 index 00000000000..80d5bc5b5a8 --- /dev/null +++ b/generators/php/codegen/src/constants.ts @@ -0,0 +1 @@ +export const TRAITS_DIRECTORY = "Traits"; diff --git a/generators/php/codegen/src/context/AbstractPhpGeneratorContext.ts b/generators/php/codegen/src/context/AbstractPhpGeneratorContext.ts index 12460deb336..a707a65234b 100644 --- a/generators/php/codegen/src/context/AbstractPhpGeneratorContext.ts +++ b/generators/php/codegen/src/context/AbstractPhpGeneratorContext.ts @@ -9,7 +9,8 @@ import { Subpackage, SubpackageId, FernFilepath, - PrimitiveTypeV1 + PrimitiveTypeV1, + ObjectTypeDeclaration } from "@fern-fern/ir-sdk/api"; import { BasePhpCustomConfigSchema } from "../custom-config/BasePhpCustomConfigSchema"; import { PhpProject } from "../project"; @@ -20,6 +21,7 @@ import { AsIsFiles } from "../AsIs"; import { RelativeFilePath } from "@fern-api/fs-utils"; import { php } from ".."; import { GLOBAL_NAMESPACE } from "../ast/core/Constant"; +import { TRAITS_DIRECTORY } from "../constants"; export interface FileLocation { namespace: string; @@ -283,6 +285,35 @@ export abstract class AbstractPhpGeneratorContext< } } + public getUnderlyingObjectTypeDeclaration(typeReference: TypeReference): ObjectTypeDeclaration { + switch (typeReference.type) { + case "named": { + const declaration = this.getTypeDeclarationOrThrow(typeReference.typeId); + if (declaration.shape.type === "alias") { + return this.getUnderlyingObjectTypeDeclaration(declaration.shape.aliasOf); + } + if (declaration.shape.type === "object") { + return declaration.shape; + } + throw new Error("Type is not an object type"); + } + case "primitive": + case "unknown": + case "container": + } + throw new Error("Type is not an object type"); + } + + public getUnderlyingObjectTypeDeclarationOrThrow(typeDeclaration: TypeDeclaration): ObjectTypeDeclaration { + if (typeDeclaration.shape.type === "alias") { + return this.getUnderlyingObjectTypeDeclaration(typeDeclaration.shape.aliasOf); + } + if (typeDeclaration.shape.type === "object") { + return typeDeclaration.shape; + } + throw new Error("Type is not an object type"); + } + public maybeLiteral(typeReference: TypeReference): Literal | undefined { if (typeReference.type === "container" && typeReference.container.type === "literal") { return typeReference.container.literal; @@ -327,6 +358,7 @@ export abstract class AbstractPhpGeneratorContext< AsIsFiles.DateArrayTypeTest, AsIsFiles.EmptyArraysTest, AsIsFiles.InvalidTypesTest, + AsIsFiles.TraitTest, AsIsFiles.MixedDateArrayTypeTest, AsIsFiles.NestedUnionArrayTypeTest, AsIsFiles.NullableArrayTypeTest, @@ -341,6 +373,11 @@ export abstract class AbstractPhpGeneratorContext< public abstract getLocationForTypeId(typeId: TypeId): FileLocation; + public getTraitLocationForTypeId(typeId: TypeId): FileLocation { + const typeDeclaration = this.getTypeDeclarationOrThrow(typeId); + return this.getFileLocation(typeDeclaration.name.fernFilepath, TRAITS_DIRECTORY); + } + protected getFileLocation(filepath: FernFilepath, suffix?: string): FileLocation { let parts = filepath.allParts.map((path) => path.pascalCase.safeName); parts = suffix != null ? [...parts, suffix] : parts; diff --git a/generators/php/codegen/src/context/PhpTypeMapper.ts b/generators/php/codegen/src/context/PhpTypeMapper.ts index 305d5836886..ba5d9b4780e 100644 --- a/generators/php/codegen/src/context/PhpTypeMapper.ts +++ b/generators/php/codegen/src/context/PhpTypeMapper.ts @@ -58,6 +58,13 @@ export class PhpTypeMapper { }); } + public convertToTraitClassReference(declaredTypeName: { typeId: TypeId; name: Name }): ClassReference { + return new php.ClassReference({ + name: this.context.getClassName(declaredTypeName.name), + namespace: this.context.getTraitLocationForTypeId(declaredTypeName.typeId).namespace + }); + } + private convertContainer({ container, preserveEnums }: { container: ContainerType; preserveEnums: boolean }): Type { switch (container.type) { case "list": diff --git a/generators/php/codegen/src/php.ts b/generators/php/codegen/src/php.ts index f96f5f538df..3536da4f6f8 100644 --- a/generators/php/codegen/src/php.ts +++ b/generators/php/codegen/src/php.ts @@ -3,6 +3,7 @@ import { Array as Array_, Attribute, Class, + Trait, ClassInstantiation, ClassReference, CodeBlock, @@ -27,6 +28,10 @@ export function class_(args: Class.Args): Class { return new Class(args); } +export function trait(args: Trait.Args): Trait { + return new Trait(args); +} + export function classReference(args: ClassReference.Args): ClassReference { return new ClassReference(args); } @@ -76,6 +81,7 @@ export { Array, Attribute, Class, + Trait, ClassInstantiation, ClassReference, CodeBlock, diff --git a/generators/php/codegen/src/project/PhpFile.ts b/generators/php/codegen/src/project/PhpFile.ts index 7603120518a..bd584e4d1f2 100644 --- a/generators/php/codegen/src/project/PhpFile.ts +++ b/generators/php/codegen/src/project/PhpFile.ts @@ -4,7 +4,7 @@ import path from "path"; import { BasePhpCustomConfigSchema } from "../custom-config/BasePhpCustomConfigSchema"; import { File } from "@fern-api/generator-commons"; import { Class } from "../ast/Class"; -import { Enum } from "../ast"; +import { Enum, Trait } from "../ast"; import { DataClass } from "../ast/DataClass"; export type Namespace = string; @@ -12,7 +12,7 @@ export type Namespace = string; export declare namespace PhpFile { interface Args { /* The class to be written to the PHP File */ - clazz: Class | DataClass | Enum; + clazz: Class | DataClass | Trait | Enum; /* Directory of the filepath */ directory: RelativeFilePath; /* The root namespace of the project. Can be pulled directly from context. */ @@ -49,7 +49,7 @@ function phpFileContent({ rootNamespace, customConfig }: { - clazz: Class | DataClass | Enum; + clazz: Class | DataClass | Trait | Enum; rootNamespace: string; customConfig: BasePhpCustomConfigSchema; }): string { diff --git a/generators/php/model/src/ModelGeneratorCli.ts b/generators/php/model/src/ModelGeneratorCli.ts index ccf11dfd6fd..1307d0bca5b 100644 --- a/generators/php/model/src/ModelGeneratorCli.ts +++ b/generators/php/model/src/ModelGeneratorCli.ts @@ -4,6 +4,7 @@ import { IntermediateRepresentation } from "@fern-fern/ir-sdk/api"; import { ModelCustomConfigSchema } from "./ModelCustomConfig"; import { ModelGeneratorContext } from "./ModelGeneratorContext"; import { generateModels } from "./generateModels"; +import { generateTraits } from "./generateTraits"; export class ModelGeneratorCLI extends AbstractPhpGeneratorCli { protected constructContext({ @@ -38,6 +39,7 @@ export class ModelGeneratorCLI extends AbstractPhpGeneratorCli { generateModels(context); + generateTraits(context); await context.project.persist(); } } diff --git a/generators/php/model/src/generateTraits.ts b/generators/php/model/src/generateTraits.ts new file mode 100644 index 00000000000..a06f26bfc68 --- /dev/null +++ b/generators/php/model/src/generateTraits.ts @@ -0,0 +1,28 @@ +import { ModelGeneratorContext } from "./ModelGeneratorContext"; +import { TraitGenerator } from "./trait/TraitGenerator"; + +export function generateTraits(context: ModelGeneratorContext): void { + const extendedTypeIdsFromTypes = Object.values(context.ir.types).flatMap((typeDeclaration) => + typeDeclaration.shape._visit({ + alias: () => [], + enum: () => [], + object: (objectDeclaration) => objectDeclaration.extends.map((declaredTypeName) => declaredTypeName.typeId), + undiscriminatedUnion: () => [], + union: () => [], + _other: () => [] + }) + ); + const extendedTypeIdsFromInlinedRequests = Object.values(context.ir.services) + .flatMap((service) => service.endpoints) + .flatMap((endpoint) => { + return endpoint.requestBody?.type === "inlinedRequestBody" + ? endpoint.requestBody.extends.map((declaredTypeName) => declaredTypeName.typeId) + : []; + }); + for (const typeId of new Set([...extendedTypeIdsFromTypes, ...extendedTypeIdsFromInlinedRequests])) { + const typeDeclaration = context.getTypeDeclarationOrThrow(typeId); + const objectTypeDeclaration = context.getUnderlyingObjectTypeDeclarationOrThrow(typeDeclaration); + const file = new TraitGenerator(context, typeDeclaration, objectTypeDeclaration).generate(); + context.project.addSourceFiles(file); + } +} diff --git a/generators/php/model/src/index.ts b/generators/php/model/src/index.ts index 14e382f1a72..f7ba0831e8d 100644 --- a/generators/php/model/src/index.ts +++ b/generators/php/model/src/index.ts @@ -1,2 +1,3 @@ export * from "./ModelGeneratorCli"; export { generateModels } from "./generateModels"; +export { generateTraits } from "./generateTraits"; diff --git a/generators/php/model/src/object/ObjectGenerator.ts b/generators/php/model/src/object/ObjectGenerator.ts index 3437940bc93..719b6257f8a 100644 --- a/generators/php/model/src/object/ObjectGenerator.ts +++ b/generators/php/model/src/object/ObjectGenerator.ts @@ -2,7 +2,7 @@ import { join, RelativeFilePath } from "@fern-api/fs-utils"; import { PhpFile } from "@fern-api/php-codegen"; import { FileGenerator } from "@fern-api/php-codegen"; import { php } from "@fern-api/php-codegen"; -import { ObjectTypeDeclaration, TypeDeclaration } from "@fern-fern/ir-sdk/api"; +import { ObjectProperty, ObjectTypeDeclaration, TypeDeclaration } from "@fern-fern/ir-sdk/api"; import { ModelCustomConfigSchema } from "../ModelCustomConfig"; import { ModelGeneratorContext } from "../ModelGeneratorContext"; @@ -23,24 +23,17 @@ export class ObjectGenerator extends FileGenerator + this.context.phpTypeMapper.convertToTraitClassReference(declaredTypeName) + ) }); - // TODO: handle extended properties for (const property of this.objectDeclaration.properties) { - const convertedType = this.context.phpTypeMapper.convert({ reference: property.valueType }); - clazz.addField( - php.field({ - type: convertedType, - name: this.context.getPropertyName(property.name.name), - access: "public", - docs: property.docs, - attributes: this.context.phpAttributeMapper.convert({ - type: convertedType, - property - }) - }) - ); + clazz.addField(this.toField({ property })); + } + for (const property of this.objectDeclaration.extendedProperties ?? []) { + clazz.addField(this.toField({ property, inherited: true })); } return new PhpFile({ @@ -51,6 +44,21 @@ export class ObjectGenerator extends FileGenerator { + private readonly typeDeclaration: TypeDeclaration; + private readonly classReference: php.ClassReference; + constructor( + context: ModelGeneratorContext, + typeDeclaration: TypeDeclaration, + private readonly objectDeclaration: ObjectTypeDeclaration + ) { + super(context); + this.typeDeclaration = typeDeclaration; + this.classReference = this.context.phpTypeMapper.convertToTraitClassReference(this.typeDeclaration.name); + } + + public doGenerate(): PhpFile { + const clazz = php.trait({ + ...this.classReference, + docs: this.typeDeclaration.docs, + traits: this.objectDeclaration.extends.map((declaredTypeName) => + this.context.phpTypeMapper.convertToTraitClassReference(declaredTypeName) + ) + }); + + for (const property of this.objectDeclaration.properties) { + const convertedType = this.context.phpTypeMapper.convert({ reference: property.valueType }); + clazz.addField( + php.field({ + type: convertedType, + name: this.context.getPropertyName(property.name.name), + access: "public", + docs: property.docs, + attributes: this.context.phpAttributeMapper.convert({ + type: convertedType, + property + }) + }) + ); + } + + return new PhpFile({ + clazz, + rootNamespace: this.context.getRootNamespace(), + directory: this.context.getTraitLocationForTypeId(this.typeDeclaration.name.typeId).directory, + customConfig: this.context.customConfig + }); + } + + protected getFilepath(): RelativeFilePath { + return this.context.getTraitLocationForTypeId(this.typeDeclaration.name.typeId).directory; + } +} diff --git a/generators/php/sdk/src/SdkGeneratorCli.ts b/generators/php/sdk/src/SdkGeneratorCli.ts index 31628921fdb..5c77b1f7dfe 100644 --- a/generators/php/sdk/src/SdkGeneratorCli.ts +++ b/generators/php/sdk/src/SdkGeneratorCli.ts @@ -4,7 +4,7 @@ import { FernGeneratorExec } from "@fern-fern/generator-exec-sdk"; import { HttpService, IntermediateRepresentation } from "@fern-fern/ir-sdk/api"; import { SdkCustomConfigSchema } from "./SdkCustomConfig"; import { SdkGeneratorContext } from "./SdkGeneratorContext"; -import { generateModels } from "@fern-api/php-model"; +import { generateModels, generateTraits } from "@fern-api/php-model"; import { RootClientGenerator } from "./root-client/RootClientGenerator"; import { SubPackageClientGenerator } from "./subpackage-client/SubPackageClientGenerator"; import { WrappedEndpointRequestGenerator } from "./endpoint/request/WrappedEndpointRequestGenerator"; @@ -49,6 +49,7 @@ export class SdkGeneratorCLI extends AbstractPhpGeneratorCli { generateModels(context); + generateTraits(context); this.generateRootClient(context); this.generateSubpackages(context); this.generateEnvironment(context); diff --git a/generators/php/sdk/src/endpoint/request/WrappedEndpointRequestGenerator.ts b/generators/php/sdk/src/endpoint/request/WrappedEndpointRequestGenerator.ts index f25032cfa60..787fdf30b0c 100644 --- a/generators/php/sdk/src/endpoint/request/WrappedEndpointRequestGenerator.ts +++ b/generators/php/sdk/src/endpoint/request/WrappedEndpointRequestGenerator.ts @@ -1,6 +1,12 @@ import { php, PhpFile, FileGenerator, FileLocation } from "@fern-api/php-codegen"; import { join, RelativeFilePath } from "@fern-api/fs-utils"; -import { HttpEndpoint, SdkRequestWrapper, ServiceId } from "@fern-fern/ir-sdk/api"; +import { + HttpEndpoint, + InlinedRequestBodyProperty, + ObjectProperty, + SdkRequestWrapper, + ServiceId +} from "@fern-fern/ir-sdk/api"; import { SdkCustomConfigSchema } from "../../SdkCustomConfig"; import { SdkGeneratorContext } from "../../SdkGeneratorContext"; @@ -78,16 +84,13 @@ export class WrappedEndpointRequestGenerator extends FileGenerator< }, inlinedRequestBody: (request) => { for (const property of request.properties) { - const type = this.context.phpTypeMapper.convert({ reference: property.valueType }); - clazz.addField( - php.field({ - name: this.context.getPropertyName(property.name.name), - type, - access: "public", - docs: property.docs, - attributes: this.context.phpAttributeMapper.convert({ type, property }) - }) - ); + clazz.addField(this.toField({ property })); + } + for (const property of request.extendedProperties ?? []) { + clazz.addField(this.toField({ property, inherited: true })); + } + for (const declaredTypeName of request.extends) { + clazz.addTrait(this.context.phpTypeMapper.convertToTraitClassReference(declaredTypeName)); } }, fileUpload: () => undefined, @@ -103,6 +106,21 @@ export class WrappedEndpointRequestGenerator extends FileGenerator< }); } + private toField({ property, inherited }: { property: InlinedRequestBodyProperty; inherited?: boolean }): php.Field { + const convertedType = this.context.phpTypeMapper.convert({ reference: property.valueType }); + return php.field({ + type: convertedType, + name: this.context.getPropertyName(property.name.name), + access: "public", + docs: property.docs, + attributes: this.context.phpAttributeMapper.convert({ + type: convertedType, + property + }), + inherited + }); + } + protected getFilepath(): RelativeFilePath { return join( this.context.project.filepaths.getSourceDirectory(), diff --git a/generators/php/sdk/versions.yml b/generators/php/sdk/versions.yml index c9a63e645a9..736c756accb 100644 --- a/generators/php/sdk/versions.yml +++ b/generators/php/sdk/versions.yml @@ -1,3 +1,8 @@ +- version: 0.1.6 + changelogEntry: + - type: feat + summary: >- + Support inheritance for types and inlined requests. - version: 0.1.5 changelogEntry: - type: feat diff --git a/seed/php-model/alias-extends/src/Child.php b/seed/php-model/alias-extends/src/Child.php index b8c31ff899d..7bee8fcda65 100644 --- a/seed/php-model/alias-extends/src/Child.php +++ b/seed/php-model/alias-extends/src/Child.php @@ -3,10 +3,13 @@ namespace Seed; use Seed\Core\Json\SerializableType; +use Seed\Traits\Parent_; use Seed\Core\Json\JsonProperty; class Child extends SerializableType { + use Parent_; + /** * @var string $child */ @@ -16,11 +19,13 @@ class Child extends SerializableType /** * @param array{ * child: string, + * parent: string, * } $values */ public function __construct( array $values, ) { $this->child = $values['child']; + $this->parent = $values['parent']; } } diff --git a/seed/php-model/alias-extends/src/Traits/AliasType.php b/seed/php-model/alias-extends/src/Traits/AliasType.php new file mode 100644 index 00000000000..6ec816f3e3a --- /dev/null +++ b/seed/php-model/alias-extends/src/Traits/AliasType.php @@ -0,0 +1,14 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/alias/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/alias/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/alias/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/any-auth/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/any-auth/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/any-auth/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/api-wide-base-path/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/api-wide-base-path/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/api-wide-base-path/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/audiences/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/audiences/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/audiences/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/auth-environment-variables/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/auth-environment-variables/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/auth-environment-variables/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/basic-auth-environment-variables/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/basic-auth-environment-variables/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/basic-auth-environment-variables/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/basic-auth/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/basic-auth/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/basic-auth/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/bearer-token-environment-variable/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/bearer-token-environment-variable/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/bearer-token-environment-variable/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/bytes/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/bytes/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/bytes/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/circular-references-advanced/src/A/A.php b/seed/php-model/circular-references-advanced/src/A/A.php index ad86a0bec70..4cb6b867841 100644 --- a/seed/php-model/circular-references-advanced/src/A/A.php +++ b/seed/php-model/circular-references-advanced/src/A/A.php @@ -3,7 +3,21 @@ namespace Seed\A; use Seed\Core\Json\SerializableType; +use Seed\Traits\RootType; class A extends SerializableType { + use RootType; + + + /** + * @param array{ + * s: string, + * } $values + */ + public function __construct( + array $values, + ) { + $this->s = $values['s']; + } } diff --git a/seed/php-model/circular-references-advanced/src/Traits/RootType.php b/seed/php-model/circular-references-advanced/src/Traits/RootType.php new file mode 100644 index 00000000000..44f28a09118 --- /dev/null +++ b/seed/php-model/circular-references-advanced/src/Traits/RootType.php @@ -0,0 +1,14 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/circular-references/src/A/A.php b/seed/php-model/circular-references/src/A/A.php index ad86a0bec70..4cb6b867841 100644 --- a/seed/php-model/circular-references/src/A/A.php +++ b/seed/php-model/circular-references/src/A/A.php @@ -3,7 +3,21 @@ namespace Seed\A; use Seed\Core\Json\SerializableType; +use Seed\Traits\RootType; class A extends SerializableType { + use RootType; + + + /** + * @param array{ + * s: string, + * } $values + */ + public function __construct( + array $values, + ) { + $this->s = $values['s']; + } } diff --git a/seed/php-model/circular-references/src/Traits/RootType.php b/seed/php-model/circular-references/src/Traits/RootType.php new file mode 100644 index 00000000000..44f28a09118 --- /dev/null +++ b/seed/php-model/circular-references/src/Traits/RootType.php @@ -0,0 +1,14 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/cross-package-type-names/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/cross-package-type-names/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/cross-package-type-names/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/custom-auth/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/custom-auth/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/custom-auth/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/enum/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/enum/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/enum/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/error-property/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/error-property/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/error-property/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/examples/src/Types/ExtendedMovie.php b/seed/php-model/examples/src/Types/ExtendedMovie.php index 4c313c65c61..03ac0fe7d8a 100644 --- a/seed/php-model/examples/src/Types/ExtendedMovie.php +++ b/seed/php-model/examples/src/Types/ExtendedMovie.php @@ -3,11 +3,14 @@ namespace Seed\Types; use Seed\Core\Json\SerializableType; +use Seed\Types\Traits\Movie; use Seed\Core\Json\JsonProperty; use Seed\Core\Types\ArrayType; class ExtendedMovie extends SerializableType { + use Movie; + /** * @var array $cast */ @@ -17,11 +20,31 @@ class ExtendedMovie extends SerializableType /** * @param array{ * cast: array, + * id: string, + * prequel?: ?string, + * title: string, + * from: string, + * rating: float, + * type: string, + * tag: string, + * book?: ?string, + * metadata: array, + * revenue: int, * } $values */ public function __construct( array $values, ) { $this->cast = $values['cast']; + $this->id = $values['id']; + $this->prequel = $values['prequel'] ?? null; + $this->title = $values['title']; + $this->from = $values['from']; + $this->rating = $values['rating']; + $this->type = $values['type']; + $this->tag = $values['tag']; + $this->book = $values['book'] ?? null; + $this->metadata = $values['metadata']; + $this->revenue = $values['revenue']; } } diff --git a/seed/php-model/examples/src/Types/Traits/Movie.php b/seed/php-model/examples/src/Types/Traits/Movie.php new file mode 100644 index 00000000000..2f40680e303 --- /dev/null +++ b/seed/php-model/examples/src/Types/Traits/Movie.php @@ -0,0 +1,69 @@ + $metadata + */ + #[JsonProperty('metadata'), ArrayType(['string' => 'mixed'])] + public array $metadata; + + /** + * @var int $revenue + */ + #[JsonProperty('revenue')] + public int $revenue; +} diff --git a/seed/php-model/examples/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/examples/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/examples/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/exhaustive/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/exhaustive/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/exhaustive/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/extends/src/ExampleType.php b/seed/php-model/extends/src/ExampleType.php index 7bc916aea74..f1d3ea4de79 100644 --- a/seed/php-model/extends/src/ExampleType.php +++ b/seed/php-model/extends/src/ExampleType.php @@ -3,10 +3,13 @@ namespace Seed; use Seed\Core\Json\SerializableType; +use Seed\Traits\Docs; use Seed\Core\Json\JsonProperty; class ExampleType extends SerializableType { + use Docs; + /** * @var string $name */ @@ -16,11 +19,13 @@ class ExampleType extends SerializableType /** * @param array{ * name: string, + * docs: string, * } $values */ public function __construct( array $values, ) { $this->name = $values['name']; + $this->docs = $values['docs']; } } diff --git a/seed/php-model/extends/src/Json.php b/seed/php-model/extends/src/Json.php index 9bc11b9c494..c62a6c629f3 100644 --- a/seed/php-model/extends/src/Json.php +++ b/seed/php-model/extends/src/Json.php @@ -3,10 +3,13 @@ namespace Seed; use Seed\Core\Json\SerializableType; +use Seed\Traits\Docs; use Seed\Core\Json\JsonProperty; class Json extends SerializableType { + use Docs; + /** * @var string $raw */ @@ -16,11 +19,13 @@ class Json extends SerializableType /** * @param array{ * raw: string, + * docs: string, * } $values */ public function __construct( array $values, ) { $this->raw = $values['raw']; + $this->docs = $values['docs']; } } diff --git a/seed/php-model/extends/src/NestedType.php b/seed/php-model/extends/src/NestedType.php index 2955b61bdc5..74cecd3ca25 100644 --- a/seed/php-model/extends/src/NestedType.php +++ b/seed/php-model/extends/src/NestedType.php @@ -3,10 +3,13 @@ namespace Seed; use Seed\Core\Json\SerializableType; +use Seed\Traits\Json; use Seed\Core\Json\JsonProperty; class NestedType extends SerializableType { + use Json; + /** * @var string $name */ @@ -16,11 +19,15 @@ class NestedType extends SerializableType /** * @param array{ * name: string, + * raw: string, + * docs: string, * } $values */ public function __construct( array $values, ) { $this->name = $values['name']; + $this->raw = $values['raw']; + $this->docs = $values['docs']; } } diff --git a/seed/php-model/extends/src/Traits/Docs.php b/seed/php-model/extends/src/Traits/Docs.php new file mode 100644 index 00000000000..17eaf8aa4ff --- /dev/null +++ b/seed/php-model/extends/src/Traits/Docs.php @@ -0,0 +1,14 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/extra-properties/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/extra-properties/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/extra-properties/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/file-download/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/file-download/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/file-download/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/file-upload/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/file-upload/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/file-upload/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/folders/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/folders/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/folders/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/grpc-proto-exhaustive/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/grpc-proto-exhaustive/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/grpc-proto-exhaustive/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/grpc-proto/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/grpc-proto/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/grpc-proto/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/idempotency-headers/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/idempotency-headers/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/idempotency-headers/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/imdb/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/imdb/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/imdb/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/literal/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/literal/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/literal/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/mixed-case/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/mixed-case/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/mixed-case/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/mixed-file-directory/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/mixed-file-directory/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/mixed-file-directory/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/multi-line-docs/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/multi-line-docs/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/multi-line-docs/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/multi-url-environment-no-default/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/multi-url-environment-no-default/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/multi-url-environment-no-default/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/multi-url-environment/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/multi-url-environment/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/multi-url-environment/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/no-environment/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/no-environment/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/no-environment/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/oauth-client-credentials-default/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/oauth-client-credentials-default/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/oauth-client-credentials-default/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/oauth-client-credentials-environment-variables/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/oauth-client-credentials-environment-variables/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/oauth-client-credentials-environment-variables/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/oauth-client-credentials-nested-root/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/oauth-client-credentials-nested-root/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/oauth-client-credentials-nested-root/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/oauth-client-credentials/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/oauth-client-credentials/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/oauth-client-credentials/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/object/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/object/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/object/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/objects-with-imports/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/objects-with-imports/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/objects-with-imports/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/optional/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/optional/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/optional/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/package-yml/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/package-yml/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/package-yml/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/pagination/src/Users/ListUsersExtendedOptionalListResponse.php b/seed/php-model/pagination/src/Users/ListUsersExtendedOptionalListResponse.php index 00810b43f46..e454dd175bf 100644 --- a/seed/php-model/pagination/src/Users/ListUsersExtendedOptionalListResponse.php +++ b/seed/php-model/pagination/src/Users/ListUsersExtendedOptionalListResponse.php @@ -3,10 +3,13 @@ namespace Seed\Users; use Seed\Core\Json\SerializableType; +use Seed\Users\Traits\UserOptionalListPage; use Seed\Core\Json\JsonProperty; class ListUsersExtendedOptionalListResponse extends SerializableType { + use UserOptionalListPage; + /** * @var int $totalCount The totall number of /users */ @@ -16,11 +19,15 @@ class ListUsersExtendedOptionalListResponse extends SerializableType /** * @param array{ * totalCount: int, + * data: UserOptionalListContainer, + * next?: ?string, * } $values */ public function __construct( array $values, ) { $this->totalCount = $values['totalCount']; + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } } diff --git a/seed/php-model/pagination/src/Users/ListUsersExtendedResponse.php b/seed/php-model/pagination/src/Users/ListUsersExtendedResponse.php index dc600ccd164..93df8c38ec8 100644 --- a/seed/php-model/pagination/src/Users/ListUsersExtendedResponse.php +++ b/seed/php-model/pagination/src/Users/ListUsersExtendedResponse.php @@ -3,10 +3,13 @@ namespace Seed\Users; use Seed\Core\Json\SerializableType; +use Seed\Users\Traits\UserPage; use Seed\Core\Json\JsonProperty; class ListUsersExtendedResponse extends SerializableType { + use UserPage; + /** * @var int $totalCount The totall number of /users */ @@ -16,11 +19,15 @@ class ListUsersExtendedResponse extends SerializableType /** * @param array{ * totalCount: int, + * data: UserListContainer, + * next?: ?string, * } $values */ public function __construct( array $values, ) { $this->totalCount = $values['totalCount']; + $this->data = $values['data']; + $this->next = $values['next'] ?? null; } } diff --git a/seed/php-model/pagination/src/Users/Traits/UserOptionalListPage.php b/seed/php-model/pagination/src/Users/Traits/UserOptionalListPage.php new file mode 100644 index 00000000000..3056830dc17 --- /dev/null +++ b/seed/php-model/pagination/src/Users/Traits/UserOptionalListPage.php @@ -0,0 +1,21 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/plain-text/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/plain-text/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/plain-text/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/query-parameters/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/query-parameters/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/query-parameters/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/reserved-keywords/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/reserved-keywords/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/reserved-keywords/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/response-property/src/Service/Response.php b/seed/php-model/response-property/src/Service/Response.php index 46eaf9deee5..ec80232ba7a 100644 --- a/seed/php-model/response-property/src/Service/Response.php +++ b/seed/php-model/response-property/src/Service/Response.php @@ -3,10 +3,15 @@ namespace Seed\Service; use Seed\Core\Json\SerializableType; +use Seed\Traits\WithMetadata; +use Seed\Service\Traits\WithDocs; use Seed\Core\Json\JsonProperty; class Response extends SerializableType { + use WithMetadata; + use WithDocs; + /** * @var Movie $data */ @@ -16,11 +21,15 @@ class Response extends SerializableType /** * @param array{ * data: Movie, + * metadata: array, + * docs: string, * } $values */ public function __construct( array $values, ) { $this->data = $values['data']; + $this->metadata = $values['metadata']; + $this->docs = $values['docs']; } } diff --git a/seed/php-model/response-property/src/Service/Traits/WithDocs.php b/seed/php-model/response-property/src/Service/Traits/WithDocs.php new file mode 100644 index 00000000000..429bb4dd850 --- /dev/null +++ b/seed/php-model/response-property/src/Service/Traits/WithDocs.php @@ -0,0 +1,14 @@ + $metadata + */ + #[JsonProperty('metadata'), ArrayType(['string' => 'string'])] + public array $metadata; +} diff --git a/seed/php-model/response-property/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/response-property/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/response-property/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/server-sent-event-examples/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/server-sent-event-examples/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/server-sent-event-examples/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/server-sent-events/tests/Seed/Core/Json/TraitTest.php b/seed/php-model/server-sent-events/tests/Seed/Core/Json/TraitTest.php new file mode 100644 index 00000000000..e23e5c049cc --- /dev/null +++ b/seed/php-model/server-sent-events/tests/Seed/Core/Json/TraitTest.php @@ -0,0 +1,61 @@ +integerProperty = $values['integerProperty']; + $this->stringProperty = $values['stringProperty']; + } +} + +class TraitTest extends TestCase +{ + public function testTraitPropertyAndString(): void + { + $data = [ + 'integer_property' => 42, + 'string_property' => 'Hello, World!', + ]; + + $json = json_encode($data, JSON_THROW_ON_ERROR); + + $object = TypeWithTrait::fromJson($json); + + $serializedJson = $object->toJson(); + + $this->assertJsonStringEqualsJsonString($json, $serializedJson, 'Serialized JSON does not match original JSON for ScalarTypesTestWithTrait.'); + + $this->assertEquals(42, $object->integerProperty, 'integer_property should be 42.'); + $this->assertEquals('Hello, World!', $object->stringProperty, 'string_property should be "Hello, World!".'); + } +} diff --git a/seed/php-model/simple-fhir/src/Account.php b/seed/php-model/simple-fhir/src/Account.php index d845b0822f6..422d2ee66b5 100644 --- a/seed/php-model/simple-fhir/src/Account.php +++ b/seed/php-model/simple-fhir/src/Account.php @@ -3,10 +3,13 @@ namespace Seed; use Seed\Core\Json\SerializableType; +use Seed\Traits\BaseResource; use Seed\Core\Json\JsonProperty; class Account extends SerializableType { + use BaseResource; + /** * @var string $resourceType */ @@ -37,6 +40,9 @@ class Account extends SerializableType * name: string, * patient?: ?Patient, * practitioner?: ?Practitioner, + * id: string, + * relatedResources: array, + * memo: Memo, * } $values */ public function __construct( @@ -46,5 +52,8 @@ public function __construct( $this->name = $values['name']; $this->patient = $values['patient'] ?? null; $this->practitioner = $values['practitioner'] ?? null; + $this->id = $values['id']; + $this->relatedResources = $values['relatedResources']; + $this->memo = $values['memo']; } } diff --git a/seed/php-model/simple-fhir/src/Patient.php b/seed/php-model/simple-fhir/src/Patient.php index 277551e5508..bb245c25d64 100644 --- a/seed/php-model/simple-fhir/src/Patient.php +++ b/seed/php-model/simple-fhir/src/Patient.php @@ -3,11 +3,14 @@ namespace Seed; use Seed\Core\Json\SerializableType; +use Seed\Traits\BaseResource; use Seed\Core\Json\JsonProperty; use Seed\Core\Types\ArrayType; class Patient extends SerializableType { + use BaseResource; + /** * @var string $resourceType */ @@ -31,6 +34,9 @@ class Patient extends SerializableType * resourceType: string, * name: string, * scripts: array