From 449c0d02ab3c32a695fa77655d7d30bd60eda2cb Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Wed, 1 Nov 2023 22:05:40 +0100 Subject: [PATCH 01/44] =?UTF-8?q?=F0=9F=92=84=20Remove=20Comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 559bcda14..b47c7f1fd 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -365,7 +365,6 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(...lhs, "array_map(function ($value) {"); this.indent(() => { this.phpToObjConvert(className, arrayType.items, ["return "], ["$value"]); - // this.emitLine("return $tmp;"); }); this.emitLine("}, ", ...args, ");"); }, @@ -407,9 +406,6 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitBlock(["if (!is_a(", scopeAttrName, ", 'DateTime'))"], () => this.emitLine("throw new Exception('Attribute Error:", className, "::", attrName, "');") ); - // if (lhs !== undefined) { - // this.emitLine(lhs, "$tmp;"); - // } } protected phpFromObjConvert(className: Name, t: Type, lhs: Sourcelike[], args: Sourcelike[]) { @@ -425,7 +421,6 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(...lhs, " array_map(function ($value) {"); this.indent(() => { this.phpFromObjConvert(className, arrayType.items, ["return "], ["$value"]); - // this.emitLine("return $tmp;"); }); this.emitLine("}, ", ...args, ");"); }, @@ -558,7 +553,6 @@ export class PhpRenderer extends ConvenienceRenderer { "')", suffix ); - // this.emitLine("return sample();"); return; } throw Error('transformedStringType.kind === "unknown"'); @@ -627,7 +621,6 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(" * ", desc); this.emitLine(" *"); } - // this.emitLine(" * @param ", this.phpType(false, p.type, false, "", "|null")); this.emitLine(" * @param ", this.phpConvertType(className, p.type), " $value"); this.emitLine(" * @throws Exception"); this.emitLine(" * @return ", this.phpType(false, p.type)); @@ -643,7 +636,6 @@ export class PhpRenderer extends ConvenienceRenderer { ], () => { this.phpFromObjConvert(className, p.type, ["return "], [`$value`]); - // this.emitLine("return $ret;"); } ); } From a22340e63dca9b56dccfb220ce580d97f5e7d14d Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Wed, 1 Nov 2023 22:35:26 +0100 Subject: [PATCH 02/44] =?UTF-8?q?=F0=9F=8E=A8=20Remove=20output=20Comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 105 +++++--------------- 1 file changed, 27 insertions(+), 78 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index b47c7f1fd..07cb31e82 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -355,12 +355,12 @@ export class PhpRenderer extends ConvenienceRenderer { protected phpToObjConvert(className: Name, t: Type, lhs: Sourcelike[], args: Sourcelike[]) { return matchType( t, - _anyType => this.emitLine(...lhs, ...args, "; /*any*/"), - _nullType => this.emitLine(...lhs, ...args, "; /*null*/"), - _boolType => this.emitLine(...lhs, ...args, "; /*bool*/"), - _integerType => this.emitLine(...lhs, ...args, "; /*int*/"), - _doubleType => this.emitLine(...lhs, ...args, "; /*float*/"), - _stringType => this.emitLine(...lhs, ...args, "; /*string*/"), + _anyType => this.emitLine(...lhs, ...args, ";"), + _nullType => this.emitLine(...lhs, ...args, ";"), + _boolType => this.emitLine(...lhs, ...args, ";"), + _integerType => this.emitLine(...lhs, ...args, ";"), + _doubleType => this.emitLine(...lhs, ...args, ";"), + _stringType => this.emitLine(...lhs, ...args, ";"), arrayType => { this.emitLine(...lhs, "array_map(function ($value) {"); this.indent(() => { @@ -368,7 +368,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine("}, ", ...args, ");"); }, - _classType => this.emitLine(...lhs, ...args, "->to(); ", "/*class*/"), + _classType => this.emitLine(...lhs, ...args, "->to();"), mapType => { this.emitBlock(["function to($my): stdClass"], () => { this.emitLine("$out = new stdClass();"); @@ -379,7 +379,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine("return to(", ...args, ");"); }, - enumType => this.emitLine(...lhs, this.nameForNamedType(enumType), "::to(", ...args, "); ", "/*enum*/"), + enumType => this.emitLine(...lhs, this.nameForNamedType(enumType), "::to(", ...args, ");"), unionType => { const nullable = nullableFromUnion(unionType); if (nullable !== null) { @@ -411,12 +411,12 @@ export class PhpRenderer extends ConvenienceRenderer { protected phpFromObjConvert(className: Name, t: Type, lhs: Sourcelike[], args: Sourcelike[]) { return matchType( t, - _anyType => this.emitLine(...lhs, ...args, "; /*any*/"), - _nullType => this.emitLine(...lhs, ...args, "; /*null*/"), - _boolType => this.emitLine(...lhs, ...args, "; /*bool*/"), - _integerType => this.emitLine(...lhs, ...args, "; /*int*/"), - _doubleType => this.emitLine(...lhs, ...args, "; /*float*/"), - _stringType => this.emitLine(...lhs, ...args, "; /*string*/"), + _anyType => this.emitLine(...lhs, ...args, ";"), + _nullType => this.emitLine(...lhs, ...args, ";"), + _boolType => this.emitLine(...lhs, ...args, ";"), + _integerType => this.emitLine(...lhs, ...args, ";"), + _doubleType => this.emitLine(...lhs, ...args, ";"), + _stringType => this.emitLine(...lhs, ...args, ";"), arrayType => { this.emitLine(...lhs, " array_map(function ($value) {"); this.indent(() => { @@ -424,8 +424,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine("}, ", ...args, ");"); }, - classType => - this.emitLine(...lhs, this.nameForNamedType(classType), "::from(", ...args, "); ", "/*class*/"), + classType => this.emitLine(...lhs, this.nameForNamedType(classType), "::from(", ...args, ");"), mapType => { this.emitBlock(["function from($my): stdClass"], () => { this.emitLine("$out = new stdClass();"); @@ -436,7 +435,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine("return from(", ...args, ");"); }, - enumType => this.emitLine(...lhs, this.nameForNamedType(enumType), "::from(", ...args, "); ", "/*enum*/"), + enumType => this.emitLine(...lhs, this.nameForNamedType(enumType), "::from(", ...args, ");"), unionType => { const nullable = nullableFromUnion(unionType); if (nullable !== null) { @@ -471,61 +470,20 @@ export class PhpRenderer extends ConvenienceRenderer { ) { return matchType( t, - _anyType => - this.emitLine( - ...lhs, - "'AnyType::", - className, - "::", - args, - "::" + idx, - "'", - suffix, - "/*", - "" + idx, - ":", - args, - "*/" - ), - _nullType => this.emitLine(...lhs, "null", suffix, " /*", "" + idx, ":", args, "*/"), - _boolType => this.emitLine(...lhs, "true", suffix, " /*", "" + idx, ":", args, "*/"), - _integerType => this.emitLine(...lhs, "" + idx, suffix, " /*", "" + idx, ":", args, "*/"), - _doubleType => this.emitLine(...lhs, "" + (idx + idx / 1000), suffix, " /*", "" + idx, ":", args, "*/"), - _stringType => - this.emitLine( - ...lhs, - "'", - className, - "::", - args, - "::" + idx, - "'", - suffix, - " /*", - "" + idx, - ":", - args, - "*/" - ), + _anyType => this.emitLine(...lhs, "'AnyType::", className, "::", args, "::" + idx, "'", suffix), + _nullType => this.emitLine(...lhs, "null", suffix), + _boolType => this.emitLine(...lhs, "true", suffix), + _integerType => this.emitLine(...lhs, "" + idx, suffix), + _doubleType => this.emitLine(...lhs, "" + (idx + idx / 1000), suffix), + _stringType => this.emitLine(...lhs, "'", className, "::", args, "::" + idx, "'", suffix), arrayType => { this.emitLine(...lhs, " array("); this.indent(() => { this.phpSampleConvert(className, arrayType.items, [], [], idx, ""); }); - this.emitLine("); /* ", "" + idx, ":", args, "*/"); + this.emitLine(");"); }, - classType => - this.emitLine( - ...lhs, - this.nameForNamedType(classType), - "::sample()", - suffix, - " /*", - "" + idx, - ":", - args, - "*/" - ), + classType => this.emitLine(...lhs, this.nameForNamedType(classType), "::sample()", suffix), mapType => { this.emitBlock(["function sample(): stdClass"], () => { this.emitLine("$out = new stdClass();"); @@ -534,7 +492,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine("return sample();"); }, - enumType => this.emitLine(...lhs, this.nameForNamedType(enumType), "::sample()", suffix, " /*enum*/"), + enumType => this.emitLine(...lhs, this.nameForNamedType(enumType), "::sample()", suffix), unionType => { const nullable = nullableFromUnion(unionType); if (nullable !== null) { @@ -750,17 +708,8 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitFileHeader(className, []); this.emitBlock(["class ", className], () => { - this.forEachClassProperty(c, "none", (name, jsonName, p) => { - this.emitLine( - "private ", - this.phpType(false, p.type), - " $", - name, - "; // json:", - jsonName, - " ", - p.type.isNullable ? "Optional" : "Required" - ); + this.forEachClassProperty(c, "none", (name, _jsonName, p) => { + this.emitLine("private ", this.phpType(false, p.type), " $", name, ";"); }); this.ensureBlankLine(); From 165d3e258368f3af8d53e3a89fb785d95d6502ca Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Wed, 1 Nov 2023 22:41:40 +0100 Subject: [PATCH 03/44] =?UTF-8?q?=F0=9F=97=91=20Sample=20Methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 124 +------------------- 1 file changed, 4 insertions(+), 120 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 07cb31e82..dc2250d0d 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -100,7 +100,6 @@ export interface FunctionNames { readonly validate: Name; readonly from: Name; readonly to: Name; - readonly sample: Name; } export class PhpRenderer extends ConvenienceRenderer { @@ -177,18 +176,12 @@ export class PhpRenderer extends ConvenienceRenderer { name.order, lookup => `to_${lookup(name)}` ); - const sampleName = new DependencyName( - this.getNameStyling("propertyNamingFunction"), - name.order, - lookup => `sample_${lookup(name)}` - ); return { getter: getterName, setter: setterName, validate: validateName, from: fromName, - to: toName, - sample: sampleName + to: toName }; } @@ -206,8 +199,7 @@ export class PhpRenderer extends ConvenienceRenderer { getterAndSetterNames.setter, getterAndSetterNames.validate, getterAndSetterNames.to, - getterAndSetterNames.from, - getterAndSetterNames.sample + getterAndSetterNames.from ]; } @@ -460,64 +452,6 @@ export class PhpRenderer extends ConvenienceRenderer { ); } - protected phpSampleConvert( - className: Name, - t: Type, - lhs: Sourcelike[], - args: Sourcelike[], - idx: number, - suffix: Sourcelike - ) { - return matchType( - t, - _anyType => this.emitLine(...lhs, "'AnyType::", className, "::", args, "::" + idx, "'", suffix), - _nullType => this.emitLine(...lhs, "null", suffix), - _boolType => this.emitLine(...lhs, "true", suffix), - _integerType => this.emitLine(...lhs, "" + idx, suffix), - _doubleType => this.emitLine(...lhs, "" + (idx + idx / 1000), suffix), - _stringType => this.emitLine(...lhs, "'", className, "::", args, "::" + idx, "'", suffix), - arrayType => { - this.emitLine(...lhs, " array("); - this.indent(() => { - this.phpSampleConvert(className, arrayType.items, [], [], idx, ""); - }); - this.emitLine(");"); - }, - classType => this.emitLine(...lhs, this.nameForNamedType(classType), "::sample()", suffix), - mapType => { - this.emitBlock(["function sample(): stdClass"], () => { - this.emitLine("$out = new stdClass();"); - this.phpSampleConvert(className, mapType.values, ["$out->{'", className, "'} = "], args, idx, ";"); - this.emitLine("return $out;"); - }); - this.emitLine("return sample();"); - }, - enumType => this.emitLine(...lhs, this.nameForNamedType(enumType), "::sample()", suffix), - unionType => { - const nullable = nullableFromUnion(unionType); - if (nullable !== null) { - this.phpSampleConvert(className, nullable, lhs, args, idx, suffix); - return; - } - throw Error("union are not supported:" + unionType); - }, - transformedStringType => { - if (transformedStringType.kind === "date-time") { - const x = _.pad("" + (1 + (idx % 31)), 2, "0"); - this.emitLine( - ...lhs, - "DateTime::createFromFormat(DateTimeInterface::ISO8601, '", - `2020-12-${x}T12:${x}:${x}+00:00`, - "')", - suffix - ); - return; - } - throw Error('transformedStringType.kind === "unknown"'); - } - ); - } - private phpValidate(className: Name, t: Type, attrName: Sourcelike, scopeAttrName: string) { const is = (isfn: string, myT: Name = className) => { this.emitBlock(["if (!", isfn, "(", scopeAttrName, "))"], () => @@ -681,28 +615,6 @@ export class PhpRenderer extends ConvenienceRenderer { }); } } - protected emitSampleMethod( - names: FunctionNames, - p: ClassProperty, - className: Name, - name: Name, - desc: string[] | undefined, - idx: number - ) { - if (this._options.withGet) { - this.emitLine("/**"); - if (desc !== undefined) { - this.emitLine(" * ", desc); - this.emitLine(" *"); - } - const rendered = this.phpType(false, p.type); - this.emitLine(" * @return ", rendered); - this.emitLine(" */"); - this.emitBlock(["public static function ", names.sample, "(): ", rendered], () => { - this.phpSampleConvert(className, p.type, ["return "], [name], idx, ";"); - }); - } - } protected emitClassDefinition(c: ClassType, className: Name): void { this.emitFileHeader(className, []); @@ -727,7 +639,6 @@ export class PhpRenderer extends ConvenienceRenderer { }); }); - let idx = 31; this.forEachClassProperty(c, "leading-and-interposing", (name, jsonName, p) => { const desc = this.descriptionForClassProperty(c, jsonName); const names = defined(this._gettersAndSettersForPropertyName.get(name)); @@ -742,8 +653,6 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitGetMethod(names, p, className, name, desc); this.ensureBlankLine(); this.emitSetMethod(names, p, className, name, desc); - this.ensureBlankLine(); - this.emitSampleMethod(names, p, className, name, desc, idx++); }); this.ensureBlankLine(); @@ -757,8 +666,8 @@ export class PhpRenderer extends ConvenienceRenderer { lines.push([p, className, "::", names.validate, "($this->", name, ")"]); p = "|| "; }); - lines.forEach((line, jdx) => { - this.emitLine(...line, lines.length === jdx + 1 ? ";" : ""); + lines.forEach((line, i) => { + this.emitLine(...line, lines.length === i + 1 ? ";" : ""); }); } ); @@ -812,20 +721,6 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(");"); } ); - this.ensureBlankLine(); - this.emitBlock( - ["/**\n", " * @return ", className, "\n", " */\n", "public static function sample(): ", className], - () => { - this.emitLine("return new ", className, "("); - let comma = " "; - this.forEachClassProperty(c, "none", name => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(comma, className, "::", names.sample, "()"); - comma = ","; - }); - this.emitLine(");"); - } - ); }); this.finishFile(); } @@ -938,17 +833,6 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine('throw new Exception("Cannot deserialize ', enumName, '");'); } ); - this.ensureBlankLine(); - this.emitBlock( - ["/**\n", ` * @return `, enumName, "\n", " */\n", "public static function sample(): ", enumName], - () => { - const lines: Sourcelike[] = []; - this.forEachEnumCase(e, "none", name => { - lines.push([enumName, "::$", name]); - }); - this.emitLine("return ", lines[0], ";"); - } - ); }); this.emitLine(enumName, "::init();"); this.finishFile(); From ea0c711173e7619d908633359ae78f5c45331f09 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Wed, 1 Nov 2023 23:14:36 +0100 Subject: [PATCH 04/44] =?UTF-8?q?=F0=9F=92=84=20Simplify=20emitEnumDefinit?= =?UTF-8?q?ion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index dc2250d0d..cfa8f0f99 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -748,9 +748,6 @@ export class PhpRenderer extends ConvenienceRenderer { protected emitEnumDefinition(e: EnumType, enumName: Name): void { this.emitFileHeader(enumName, []); this.emitDescription(this.descriptionForType(e)); - const caseNames: Sourcelike[] = []; - caseNames.push(";"); - const enumSerdeType = "string"; this.emitBlock(["class ", enumName], () => { this.forEachEnumCase(e, "none", (name, _jsonName) => { this.emitLine("public static ", enumName, " $", name, ";"); @@ -762,8 +759,8 @@ export class PhpRenderer extends ConvenienceRenderer { }); }); - this.emitLine("private ", enumSerdeType, " $enum;"); - this.emitBlock(["public function __construct(", enumSerdeType, " $enum)"], () => { + this.emitLine("private string $enum;"); + this.emitBlock(["public function __construct(string $enum)"], () => { this.emitLine("$this->enum = $enum;"); }); @@ -776,15 +773,13 @@ export class PhpRenderer extends ConvenienceRenderer { ` * @param `, enumName, `\n`, - ` * @return `, - enumSerdeType, + ` * @return string`, `\n`, ` * @throws Exception\n`, " */\n", "public static function to(", enumName, - " $obj): ", - enumSerdeType + " $obj): string" ], () => { this.emitLine("switch ($obj->enum) {"); From 9de411088a12c2ac5f8aa743d2e02663bcb86426 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Wed, 1 Nov 2023 23:17:45 +0100 Subject: [PATCH 05/44] =?UTF-8?q?=F0=9F=92=84=20Change=20Backticks=20to=20?= =?UTF-8?q?Double=20Quotes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 42 +++++++++------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index cfa8f0f99..1830317ef 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -527,7 +527,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.phpType(false, p.type) ], () => { - this.phpFromObjConvert(className, p.type, ["return "], [`$value`]); + this.phpFromObjConvert(className, p.type, ["return "], ["$value"]); } ); } @@ -560,7 +560,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitBlock( ["public static function ", names.validate, "(", this.phpType(false, p.type), " $value): bool"], () => { - this.phpValidate(className, p.type, name, `$value`); + this.phpValidate(className, p.type, name, "$value"); this.emitLine("return true;"); } ); @@ -573,7 +573,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(" *"); } if (!this._options.fastGet) { - this.emitLine(` * @throws Exception`); + this.emitLine(" * @throws Exception"); } const rendered = this.phpType(false, p.type); this.emitLine(" * @return ", rendered); @@ -606,7 +606,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(" *"); } this.emitLine(" * @param ", this.phpType(false, p.type, false, "", "|null")); - this.emitLine(` * @throws Exception`); + this.emitLine(" * @throws Exception"); this.emitLine(" */"); this.emitBlock(["public function ", names.setter, "(", this.phpType(false, p.type), " $value)"], () => { this.emitBlock(["if (", className, "::", names.validate, "($value)) "], () => { @@ -657,7 +657,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.ensureBlankLine(); this.emitBlock( - ["/**\n", ` * @throws Exception\n`, ` * @return bool\n`, " */\n", "public function validate(): bool"], + ["/**\n", " * @throws Exception\n", " * @return bool\n", " */\n", "public function validate(): bool"], () => { let lines: Sourcelike[][] = []; let p = "return "; @@ -674,13 +674,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.ensureBlankLine(); this.emitBlock( - [ - "/**\n", - ` * @return stdClass\n`, - ` * @throws Exception\n`, - " */\n", - "public function to(): stdClass " - ], + ["/**\n", " * @return stdClass\n", " * @throws Exception\n", " */\n", "public function to(): stdClass"], () => { this.emitLine("$out = new stdClass();"); this.forEachClassProperty(c, "none", (name, jsonName) => { @@ -695,11 +689,11 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitBlock( [ "/**\n", - ` * @param stdClass $obj\n`, - ` * @return `, + " * @param stdClass $obj\n", + " * @return ", className, - `\n`, - ` * @throws Exception\n`, + "\n", + " * @throws Exception\n", " */\n", "public static function from(stdClass $obj): ", className @@ -770,12 +764,12 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitBlock( [ "/**\n", - ` * @param `, + " * @param ", enumName, - `\n`, - ` * @return string`, - `\n`, - ` * @throws Exception\n`, + "\n", + " * @return string", + "\n", + " * @throws Exception\n", " */\n", "public static function to(", enumName, @@ -807,11 +801,11 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitBlock( [ "/**\n", - ` * @param mixed\n`, - ` * @return `, + " * @param mixed\n", + " * @return ", enumName, "\n", - ` * @throws Exception\n`, + " * @throws Exception\n", " */\n", "public static function from($obj): ", enumName From 8e5671fb785ac4eb1f578219982afe1522800474 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Wed, 1 Nov 2023 23:35:32 +0100 Subject: [PATCH 06/44] =?UTF-8?q?=F0=9F=8E=A8=20PSR-12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 148 ++++++++++++-------- 1 file changed, 90 insertions(+), 58 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 1830317ef..d656fad86 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -241,6 +241,13 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitCommentLines(lines, " * ", "/**", " */"); } + public emitBlockWithBraceOnNewLine(line: Sourcelike, f: () => void): void { + this.emitLine(line); + this.emitLine("{"); + this.indent(f); + this.emitLine("}"); + } + public emitBlock(line: Sourcelike, f: () => void): void { this.emitLine(line, " {"); this.indent(f); @@ -362,7 +369,7 @@ export class PhpRenderer extends ConvenienceRenderer { }, _classType => this.emitLine(...lhs, ...args, "->to();"), mapType => { - this.emitBlock(["function to($my): stdClass"], () => { + this.emitBlockWithBraceOnNewLine(["function to($my): stdClass"], () => { this.emitLine("$out = new stdClass();"); this.emitBlock(["foreach ($my as $k => $v)"], () => { this.phpToObjConvert(className, mapType.values, ["$my->$k = "], ["$v"]); @@ -410,7 +417,7 @@ export class PhpRenderer extends ConvenienceRenderer { _doubleType => this.emitLine(...lhs, ...args, ";"), _stringType => this.emitLine(...lhs, ...args, ";"), arrayType => { - this.emitLine(...lhs, " array_map(function ($value) {"); + this.emitLine(...lhs, "array_map(function ($value) {"); this.indent(() => { this.phpFromObjConvert(className, arrayType.items, ["return "], ["$value"]); }); @@ -418,7 +425,7 @@ export class PhpRenderer extends ConvenienceRenderer { }, classType => this.emitLine(...lhs, this.nameForNamedType(classType), "::from(", ...args, ");"), mapType => { - this.emitBlock(["function from($my): stdClass"], () => { + this.emitBlockWithBraceOnNewLine(["function from($my): stdClass"], () => { this.emitLine("$out = new stdClass();"); this.emitBlock(["foreach ($my as $k => $v)"], () => { this.phpFromObjConvert(className, mapType.values, ["$out->$k = "], ["$v"]); @@ -468,7 +475,7 @@ export class PhpRenderer extends ConvenienceRenderer { _stringType => is("is_string"), arrayType => { is("is_array"); - this.emitLine("array_walk(", scopeAttrName, ", function(", scopeAttrName, "_v) {"); + this.emitLine("array_walk(", scopeAttrName, ", function (", scopeAttrName, "_v) {"); this.indent(() => { this.phpValidate(className, arrayType.items, attrName, `${scopeAttrName}_v`); }); @@ -517,7 +524,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(" * @throws Exception"); this.emitLine(" * @return ", this.phpType(false, p.type)); this.emitLine(" */"); - this.emitBlock( + this.emitBlockWithBraceOnNewLine( [ "public static function ", names.from, @@ -540,12 +547,15 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(" * @throws Exception"); this.emitLine(" * @return ", this.phpConvertType(className, p.type)); this.emitLine(" */"); - this.emitBlock(["public function ", names.to, "(): ", this.phpConvertType(className, p.type)], () => { - this.emitBlock(["if (", className, "::", names.validate, "($this->", name, ")) "], () => { - this.phpToObjConvert(className, p.type, ["return "], ["$this->", name]); - }); - this.emitLine("throw new Exception('never get to this ", className, "::", name, "');"); - }); + this.emitBlockWithBraceOnNewLine( + ["public function ", names.to, "(): ", this.phpConvertType(className, p.type)], + () => { + this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { + this.phpToObjConvert(className, p.type, ["return "], ["$this->", name]); + }); + this.emitLine("throw new Exception('never get to this ", className, "::", name, "');"); + } + ); } protected emitValidateMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { this.emitLine("/**"); @@ -557,7 +567,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(" * @return bool"); this.emitLine(" * @throws Exception"); this.emitLine(" */"); - this.emitBlock( + this.emitBlockWithBraceOnNewLine( ["public static function ", names.validate, "(", this.phpType(false, p.type), " $value): bool"], () => { this.phpValidate(className, p.type, name, "$value"); @@ -578,9 +588,9 @@ export class PhpRenderer extends ConvenienceRenderer { const rendered = this.phpType(false, p.type); this.emitLine(" * @return ", rendered); this.emitLine(" */"); - this.emitBlock(["public function ", names.getter, "(): ", rendered], () => { + this.emitBlockWithBraceOnNewLine(["public function ", names.getter, "(): ", rendered], () => { if (!this._options.fastGet) { - this.emitBlock(["if (", className, "::", names.validate, "($this->", name, ")) "], () => { + this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { this.emitLine("return $this->", name, ";"); }); this.emitLine( @@ -608,32 +618,39 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(" * @param ", this.phpType(false, p.type, false, "", "|null")); this.emitLine(" * @throws Exception"); this.emitLine(" */"); - this.emitBlock(["public function ", names.setter, "(", this.phpType(false, p.type), " $value)"], () => { - this.emitBlock(["if (", className, "::", names.validate, "($value)) "], () => { - this.emitLine("$this->", name, " = $value;"); - }); - }); + this.emitBlockWithBraceOnNewLine( + ["public function ", names.setter, "(", this.phpType(false, p.type), " $value)"], + () => { + this.emitBlock(["if (", className, "::", names.validate, "($value))"], () => { + this.emitLine("$this->", name, " = $value;"); + }); + } + ); } } protected emitClassDefinition(c: ClassType, className: Name): void { this.emitFileHeader(className, []); - this.emitBlock(["class ", className], () => { + this.emitBlockWithBraceOnNewLine(["class ", className], () => { this.forEachClassProperty(c, "none", (name, _jsonName, p) => { this.emitLine("private ", this.phpType(false, p.type), " $", name, ";"); }); this.ensureBlankLine(); - const comments: Sourcelike[][] = []; - const args: Sourcelike[][] = []; - let prefix = ""; + this.emitLine("/**"); this.forEachClassProperty(c, "none", (name, __, p) => { - args.push([prefix, this.phpType(false, p.type), " $", name]); - prefix = ", "; - comments.push([" * @param ", this.phpType(false, p.type, false, "", "|null"), " $", name, "\n"]); + this.emitLine(" * @param ", this.phpType(false, p.type, false, "", "|null"), " $", name); + }); + this.emitLine(" */"); + this.emitLine("public function __construct("); + this.indent(() => { + this.forEachClassProperty(c, "none", (name, __, p, position) => { + const suffix = ["last", "only"].includes(position) ? "" : ","; + this.emitLine(this.phpType(false, p.type), " $", name, suffix); + }); }); - this.emitBlock(["/**\n", ...comments, " */\n", "public function __construct(", ...args, ")"], () => { + this.emitBlock(")", () => { this.forEachClassProperty(c, "none", name => { this.emitLine("$this->", name, " = $", name, ";"); }); @@ -656,24 +673,40 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.ensureBlankLine(); - this.emitBlock( + this.emitBlockWithBraceOnNewLine( ["/**\n", " * @throws Exception\n", " * @return bool\n", " */\n", "public function validate(): bool"], () => { - let lines: Sourcelike[][] = []; - let p = "return "; - this.forEachClassProperty(c, "none", (name, _jsonName, _p) => { + this.forEachClassProperty(c, "none", (name, _jsonName, _p, position) => { + const isFirstLine = ["first", "only"].includes(position); + const isLastLine = ["last", "only"].includes(position); + const prefix = isFirstLine ? "return " : "|| "; + const suffix = isLastLine ? ";" : ""; + const names = defined(this._gettersAndSettersForPropertyName.get(name)); - lines.push([p, className, "::", names.validate, "($this->", name, ")"]); - p = "|| "; - }); - lines.forEach((line, i) => { - this.emitLine(...line, lines.length === i + 1 ? ";" : ""); + const line: Sourcelike = [ + prefix, + className, + "::", + names.validate, + "($this->", + name, + ")", + suffix + ]; + + if (isFirstLine) { + this.emitLine(line); + } else { + this.indent(() => { + this.emitLine(line); + }); + } }); } ); this.ensureBlankLine(); - this.emitBlock( + this.emitBlockWithBraceOnNewLine( ["/**\n", " * @return stdClass\n", " * @throws Exception\n", " */\n", "public function to(): stdClass"], () => { this.emitLine("$out = new stdClass();"); @@ -686,7 +719,7 @@ export class PhpRenderer extends ConvenienceRenderer { ); this.ensureBlankLine(); - this.emitBlock( + this.emitBlockWithBraceOnNewLine( [ "/**\n", " * @param stdClass $obj\n", @@ -706,11 +739,12 @@ export class PhpRenderer extends ConvenienceRenderer { }); } this.emitLine("return new ", className, "("); - let comma = " "; - this.forEachClassProperty(c, "none", (name, jsonName) => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(comma, className, "::", names.from, "($obj->{'", jsonName, "'})"); - comma = ","; + this.indent(() => { + this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { + const suffix = ["last", "only"].includes(position) ? "" : ","; + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.from, "($obj->{'", jsonName, "'})", suffix); + }); }); this.emitLine(");"); } @@ -742,26 +776,26 @@ export class PhpRenderer extends ConvenienceRenderer { protected emitEnumDefinition(e: EnumType, enumName: Name): void { this.emitFileHeader(enumName, []); this.emitDescription(this.descriptionForType(e)); - this.emitBlock(["class ", enumName], () => { + this.emitBlockWithBraceOnNewLine(["class ", enumName], () => { this.forEachEnumCase(e, "none", (name, _jsonName) => { this.emitLine("public static ", enumName, " $", name, ";"); }); - this.emitBlock("public static function init()", () => { + this.emitBlockWithBraceOnNewLine("public static function init()", () => { this.forEachEnumCase(e, "none", (name, jsonName) => { this.emitLine(enumName, "::$", name, " = new ", enumName, "('", jsonName, "');"); }); }); this.emitLine("private string $enum;"); - this.emitBlock(["public function __construct(string $enum)"], () => { + this.emitBlockWithBraceOnNewLine(["public function __construct(string $enum)"], () => { this.emitLine("$this->enum = $enum;"); }); this.ensureBlankLine(); this.emitEnumSerializationAttributes(e); - this.emitBlock( + this.emitBlockWithBraceOnNewLine( [ "/**\n", " * @param ", @@ -780,15 +814,10 @@ export class PhpRenderer extends ConvenienceRenderer { this.indent(() => { this.forEachEnumCase(e, "none", (name, jsonName) => { // Todo String or Number - this.emitLine( - "case ", - enumName, - "::$", - name, - "->enum: return '", - stringEscape(jsonName), - "';" - ); + this.emitLine("case ", enumName, "::$", name, "->enum:"); + this.indent(() => { + this.emitLine("return '", stringEscape(jsonName), "';"); + }); }); }); this.emitLine("}"); @@ -798,7 +827,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.ensureBlankLine(); this.emitEnumDeserializationAttributes(e); - this.emitBlock( + this.emitBlockWithBraceOnNewLine( [ "/**\n", " * @param mixed\n", @@ -815,7 +844,10 @@ export class PhpRenderer extends ConvenienceRenderer { this.indent(() => { this.forEachEnumCase(e, "none", (name, jsonName) => { // Todo String or Enum - this.emitLine("case '", stringEscape(jsonName), "': return ", enumName, "::$", name, ";"); + this.emitLine("case '", stringEscape(jsonName), "':"); + this.indent(() => { + this.emitLine("return ", enumName, "::$", name, ";"); + }); }); }); this.emitLine("}"); From 0f73140f9ae9674ee789aab1251463847262d9d2 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Wed, 1 Nov 2023 23:39:54 +0100 Subject: [PATCH 07/44] =?UTF-8?q?=F0=9F=8E=A8=20Double=20Quotes=20>=20Sing?= =?UTF-8?q?le=20Quotes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index d656fad86..66d4d0582 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -462,7 +462,7 @@ export class PhpRenderer extends ConvenienceRenderer { private phpValidate(className: Name, t: Type, attrName: Sourcelike, scopeAttrName: string) { const is = (isfn: string, myT: Name = className) => { this.emitBlock(["if (!", isfn, "(", scopeAttrName, "))"], () => - this.emitLine('throw new Exception("Attribute Error:', myT, "::", attrName, '");') + this.emitLine("throw new Exception('Attribute Error:", myT, "::", attrName, "');") ); }; return matchType( @@ -851,7 +851,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); }); this.emitLine("}"); - this.emitLine('throw new Exception("Cannot deserialize ', enumName, '");'); + this.emitLine("throw new Exception('Cannot deserialize ", enumName, "');"); } ); }); From 5e6e11a3975f244bcf97ec109189ca5c245e4c7d Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Thu, 2 Nov 2023 00:15:08 +0100 Subject: [PATCH 08/44] =?UTF-8?q?=E2=9C=A8=20Simpler=20Enums?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 61 +++++++-------------- 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 66d4d0582..b5bbded3b 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -378,7 +378,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine("return to(", ...args, ");"); }, - enumType => this.emitLine(...lhs, this.nameForNamedType(enumType), "::to(", ...args, ");"), + _enumType => this.emitLine(...lhs, ...args, "->to();"), unionType => { const nullable = nullableFromUnion(unionType); if (nullable !== null) { @@ -491,8 +491,8 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine("}"); }, - enumType => { - this.emitLine(this.phpType(false, enumType), "::to(", scopeAttrName, ");"); + _enumType => { + this.emitLine(scopeAttrName, "->to();"); }, unionType => { const nullable = nullableFromUnion(unionType); @@ -777,17 +777,23 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitFileHeader(enumName, []); this.emitDescription(this.descriptionForType(e)); this.emitBlockWithBraceOnNewLine(["class ", enumName], () => { - this.forEachEnumCase(e, "none", (name, _jsonName) => { - this.emitLine("public static ", enumName, " $", name, ";"); + this.forEachEnumCase(e, "none", (name, jsonName) => { + this.emitLine("public const ", name, " = '", jsonName, "';"); }); + this.ensureBlankLine(); - this.emitBlockWithBraceOnNewLine("public static function init()", () => { - this.forEachEnumCase(e, "none", (name, jsonName) => { - this.emitLine(enumName, "::$", name, " = new ", enumName, "('", jsonName, "');"); + this.emitLine("public const VALUES = ["); + this.indent(() => { + this.forEachEnumCase(e, "none", name => { + this.emitLine(enumName, "::", name, ","); }); }); + this.emitLine("];"); + this.ensureBlankLine(); this.emitLine("private string $enum;"); + this.ensureBlankLine(); + this.emitBlockWithBraceOnNewLine(["public function __construct(string $enum)"], () => { this.emitLine("$this->enum = $enum;"); }); @@ -796,31 +802,11 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitEnumSerializationAttributes(e); this.emitBlockWithBraceOnNewLine( - [ - "/**\n", - " * @param ", - enumName, - "\n", - " * @return string", - "\n", - " * @throws Exception\n", - " */\n", - "public static function to(", - enumName, - " $obj): string" - ], + ["/**\n", " * @return string\n", " * @throws Exception\n", " */\n", "public function to(): string"], () => { - this.emitLine("switch ($obj->enum) {"); - this.indent(() => { - this.forEachEnumCase(e, "none", (name, jsonName) => { - // Todo String or Number - this.emitLine("case ", enumName, "::$", name, "->enum:"); - this.indent(() => { - this.emitLine("return '", stringEscape(jsonName), "';"); - }); - }); + this.emitBlock(["if (in_array($this->enum, ", enumName, "::VALUES, true))"], () => { + this.emitLine("return $this->enum;"); }); - this.emitLine("}"); this.emitLine("throw new Exception('the give value is not an enum-value.');"); } ); @@ -840,22 +826,13 @@ export class PhpRenderer extends ConvenienceRenderer { enumName ], () => { - this.emitLine("switch ($obj) {"); - this.indent(() => { - this.forEachEnumCase(e, "none", (name, jsonName) => { - // Todo String or Enum - this.emitLine("case '", stringEscape(jsonName), "':"); - this.indent(() => { - this.emitLine("return ", enumName, "::$", name, ";"); - }); - }); + this.emitBlock(["if (in_array($obj, ", enumName, "::VALUES, true))"], () => { + this.emitLine("return new ", enumName, "($obj);"); }); - this.emitLine("}"); this.emitLine("throw new Exception('Cannot deserialize ", enumName, "');"); } ); }); - this.emitLine(enumName, "::init();"); this.finishFile(); } From d5b93d4df2927830d583a3dbc6c6836c3005d31c Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Thu, 2 Nov 2023 00:19:52 +0100 Subject: [PATCH 09/44] =?UTF-8?q?=F0=9F=92=84=20Simplify=20stdClass=20"->"?= =?UTF-8?q?=20notation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index b5bbded3b..a8e3559dd 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -712,7 +712,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine("$out = new stdClass();"); this.forEachClassProperty(c, "none", (name, jsonName) => { const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine("$out->{'", jsonName, "'} = $this->", names.to, "();"); + this.emitLine("$out->", jsonName, " = $this->", names.to, "();"); }); this.emitLine("return $out;"); } @@ -743,7 +743,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { const suffix = ["last", "only"].includes(position) ? "" : ","; const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.from, "($obj->{'", jsonName, "'})", suffix); + this.emitLine(className, "::", names.from, "($obj->", jsonName, ")", suffix); }); }); this.emitLine(");"); From 98176b50ebf969fb50779982ce06fdc1d5142eb9 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Thu, 2 Nov 2023 00:40:23 +0100 Subject: [PATCH 10/44] =?UTF-8?q?=E2=9C=A8=20stdClass=20vs=20array=20Optio?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 157 ++++++++++++++------ 1 file changed, 114 insertions(+), 43 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index a8e3559dd..c133c92d6 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -2,7 +2,7 @@ import { anyTypeIssueAnnotation, nullTypeIssueAnnotation } from "../Annotation"; import { ConvenienceRenderer, ForbiddenWordsInfo } from "../ConvenienceRenderer"; import { DependencyName, funPrefixNamer, Name, Namer } from "../Naming"; import { RenderContext } from "../Renderer"; -import { BooleanOption, getOptionValues, Option, OptionValues } from "../RendererOptions"; +import { BooleanOption, EnumOption, getOptionValues, Option, OptionValues } from "../RendererOptions"; import { maybeAnnotated, Sourcelike } from "../Source"; import { acronymOption, acronymStyle, AcronymStyleOptions } from "../support/Acronyms"; import { @@ -26,11 +26,25 @@ import { directlyReachableSingleNamedType, matchType, nullableFromUnion } from " import { StringTypeMapping, TransformedStringTypeKind, PrimitiveStringTypeKind } from ".."; import * as _ from "lodash"; +export enum SerializeWith { + array = "Associative Array", + stdClass = "stdClass" +} + export const phpOptions = { withGet: new BooleanOption("with-get", "Create Getter", true), fastGet: new BooleanOption("fast-get", "getter without validation", false), withSet: new BooleanOption("with-set", "Create Setter", false), withClosing: new BooleanOption("with-closing", "PHP Closing Tag", false), + serializeWith: new EnumOption( + "serialize-with", + "Serialize with", + [ + ["array", SerializeWith.array], + ["stdClass", SerializeWith.stdClass] + ], + "array" + ), acronymStyle: acronymOption(AcronymStyleOptions.Pascal) }; @@ -268,7 +282,7 @@ export class PhpRenderer extends ConvenienceRenderer { _stringType => optionalize("string"), _arrayType => optionalize("array"), classType => optionalize(this.nameForNamedType(classType)), - _mapType => optionalize("stdClass"), + _mapType => optionalize(this._options.serializeWith === SerializeWith.stdClass ? "stdClass" : "array"), enumType => optionalize(this.nameForNamedType(enumType)), unionType => { const nullable = nullableFromUnion(unionType); @@ -304,7 +318,7 @@ export class PhpRenderer extends ConvenienceRenderer { _stringType => "string", arrayType => [this.phpDocConvertType(className, arrayType.items), "[]"], _classType => _classType.getCombinedName(), - _mapType => "stdClass", + _mapType => (this._options.serializeWith === SerializeWith.stdClass ? "stdClass" : "array"), enumType => this.nameForNamedType(enumType), unionType => { const nullable = nullableFromUnion(unionType); @@ -332,8 +346,8 @@ export class PhpRenderer extends ConvenienceRenderer { _doubleType => "float", _stringType => "string", _arrayType => "array", - _classType => "stdClass", - _mapType => "stdClass", + _classType => (this._options.serializeWith === SerializeWith.stdClass ? "stdClass" : "array"), + _mapType => (this._options.serializeWith === SerializeWith.stdClass ? "stdClass" : "array"), _enumType => "string", // TODO number this.nameForNamedType(enumType), unionType => { const nullable = nullableFromUnion(unionType); @@ -369,6 +383,7 @@ export class PhpRenderer extends ConvenienceRenderer { }, _classType => this.emitLine(...lhs, ...args, "->to();"), mapType => { + // TODO: this._options.serializeWith === SerializeWith.stdClass this.emitBlockWithBraceOnNewLine(["function to($my): stdClass"], () => { this.emitLine("$out = new stdClass();"); this.emitBlock(["foreach ($my as $k => $v)"], () => { @@ -425,6 +440,7 @@ export class PhpRenderer extends ConvenienceRenderer { }, classType => this.emitLine(...lhs, this.nameForNamedType(classType), "::from(", ...args, ");"), mapType => { + // TODO: this._options.serializeWith === SerializeWith.stdClass this.emitBlockWithBraceOnNewLine(["function from($my): stdClass"], () => { this.emitLine("$out = new stdClass();"); this.emitBlock(["foreach ($my as $k => $v)"], () => { @@ -706,49 +722,104 @@ export class PhpRenderer extends ConvenienceRenderer { ); this.ensureBlankLine(); - this.emitBlockWithBraceOnNewLine( - ["/**\n", " * @return stdClass\n", " * @throws Exception\n", " */\n", "public function to(): stdClass"], - () => { - this.emitLine("$out = new stdClass();"); - this.forEachClassProperty(c, "none", (name, jsonName) => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine("$out->", jsonName, " = $this->", names.to, "();"); - }); - this.emitLine("return $out;"); - } - ); + if (this._options.serializeWith === SerializeWith.stdClass) { + this.emitBlockWithBraceOnNewLine( + [ + "/**\n", + " * @return stdClass\n", + " * @throws Exception\n", + " */\n", + "public function to(): stdClass" + ], + () => { + this.emitLine("$out = new stdClass();"); + this.forEachClassProperty(c, "none", (name, jsonName) => { + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine("$out->", jsonName, " = $this->", names.to, "();"); + }); + this.emitLine("return $out;"); + } + ); + } else { + this.emitBlockWithBraceOnNewLine( + ["/**\n", " * @return array\n", " * @throws Exception\n", " */\n", "public function to(): array"], + () => { + this.emitLine("return ["); + this.indent(() => { + this.forEachClassProperty(c, "none", (name, jsonName) => { + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine("'", jsonName, "' => $this->", names.to, "(),"); + }); + }); + this.emitLine("];"); + } + ); + } this.ensureBlankLine(); - this.emitBlockWithBraceOnNewLine( - [ - "/**\n", - " * @param stdClass $obj\n", - " * @return ", - className, - "\n", - " * @throws Exception\n", - " */\n", - "public static function from(stdClass $obj): ", - className - ], - () => { - if (this._options.fastGet) { - this.forEachClassProperty(c, "none", name => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); + if (this._options.serializeWith === SerializeWith.stdClass) { + this.emitBlockWithBraceOnNewLine( + [ + "/**\n", + " * @param stdClass $obj\n", + " * @return ", + className, + "\n", + " * @throws Exception\n", + " */\n", + "public static function from(stdClass $obj): ", + className + ], + () => { + if (this._options.fastGet) { + this.forEachClassProperty(c, "none", name => { + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); + }); + } + this.emitLine("return new ", className, "("); + this.indent(() => { + this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { + const suffix = ["last", "only"].includes(position) ? "" : ","; + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.from, "($obj->", jsonName, ")", suffix); + }); }); + this.emitLine(");"); } - this.emitLine("return new ", className, "("); - this.indent(() => { - this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { - const suffix = ["last", "only"].includes(position) ? "" : ","; - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.from, "($obj->", jsonName, ")", suffix); + ); + } else { + this.emitBlockWithBraceOnNewLine( + [ + "/**\n", + " * @param array $arr\n", + " * @return ", + className, + "\n", + " * @throws Exception\n", + " */\n", + "public static function from(array $arr): ", + className + ], + () => { + if (this._options.fastGet) { + this.forEachClassProperty(c, "none", name => { + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); + }); + } + this.emitLine("return new ", className, "("); + this.indent(() => { + this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { + const suffix = ["last", "only"].includes(position) ? "" : ","; + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.from, "($arr['", jsonName, "'])", suffix); + }); }); - }); - this.emitLine(");"); - } - ); + this.emitLine(");"); + } + ); + } }); this.finishFile(); } From 6e27bb7931ad6a0369076c5a06017bbe34583246 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Thu, 2 Nov 2023 22:15:27 +0100 Subject: [PATCH 11/44] =?UTF-8?q?=E2=9C=A8=20Better=20PHPDocs=20(array)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 107 +++++++------------- 1 file changed, 39 insertions(+), 68 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index c133c92d6..40272a97e 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -307,7 +307,7 @@ export class PhpRenderer extends ConvenienceRenderer { ); } - protected phpDocConvertType(className: Name, t: Type): Sourcelike { + protected phpDocType(className: Name, t: Type): Sourcelike { return matchType( t, _anyType => "any", @@ -316,14 +316,14 @@ export class PhpRenderer extends ConvenienceRenderer { _integerType => "int", _doubleType => "float", _stringType => "string", - arrayType => [this.phpDocConvertType(className, arrayType.items), "[]"], - _classType => _classType.getCombinedName(), + arrayType => [this.phpDocType(className, arrayType.items), "[]"], + classType => this.nameForNamedType(classType), _mapType => (this._options.serializeWith === SerializeWith.stdClass ? "stdClass" : "array"), enumType => this.nameForNamedType(enumType), unionType => { const nullable = nullableFromUnion(unionType); if (nullable !== null) { - return [this.phpDocConvertType(className, nullable), "|null"]; + return ["?", this.phpDocType(className, nullable)]; } throw Error("union are not supported"); }, @@ -336,35 +336,6 @@ export class PhpRenderer extends ConvenienceRenderer { ); } - protected phpConvertType(className: Name, t: Type): Sourcelike { - return matchType( - t, - _anyType => "any", - _nullType => "null", - _boolType => "bool", - _integerType => "int", - _doubleType => "float", - _stringType => "string", - _arrayType => "array", - _classType => (this._options.serializeWith === SerializeWith.stdClass ? "stdClass" : "array"), - _mapType => (this._options.serializeWith === SerializeWith.stdClass ? "stdClass" : "array"), - _enumType => "string", // TODO number this.nameForNamedType(enumType), - unionType => { - const nullable = nullableFromUnion(unionType); - if (nullable !== null) { - return ["?", this.phpConvertType(className, nullable)]; - } - throw Error("union are not supported"); - }, - transformedStringType => { - if (transformedStringType.kind === "date-time") { - return "string"; - } - throw Error('transformedStringType.kind === "unknown"'); - } - ); - } - protected phpToObjConvert(className: Name, t: Type, lhs: Sourcelike[], args: Sourcelike[]) { return matchType( t, @@ -536,16 +507,17 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(" * ", desc); this.emitLine(" *"); } - this.emitLine(" * @param ", this.phpConvertType(className, p.type), " $value"); + const valueType = p.type.kind === "array" ? "mixed[]" : this.phpDocType(className, p.type); + this.emitLine(" * @param ", valueType, " $value"); this.emitLine(" * @throws Exception"); - this.emitLine(" * @return ", this.phpType(false, p.type)); + this.emitLine(" * @return ", this.phpDocType(className, p.type)); this.emitLine(" */"); this.emitBlockWithBraceOnNewLine( [ "public static function ", names.from, "(", - this.phpConvertType(className, p.type), + this.phpType(false, p.type), " $value): ", this.phpType(false, p.type) ], @@ -561,17 +533,14 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(" *"); } this.emitLine(" * @throws Exception"); - this.emitLine(" * @return ", this.phpConvertType(className, p.type)); + this.emitLine(" * @return ", this.phpDocType(className, p.type)); this.emitLine(" */"); - this.emitBlockWithBraceOnNewLine( - ["public function ", names.to, "(): ", this.phpConvertType(className, p.type)], - () => { - this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { - this.phpToObjConvert(className, p.type, ["return "], ["$this->", name]); - }); - this.emitLine("throw new Exception('never get to this ", className, "::", name, "');"); - } - ); + this.emitBlockWithBraceOnNewLine(["public function ", names.to, "(): ", this.phpType(false, p.type)], () => { + this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { + this.phpToObjConvert(className, p.type, ["return "], ["$this->", name]); + }); + this.emitLine("throw new Exception('never get to this ", className, "::", name, "');"); + }); } protected emitValidateMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { this.emitLine("/**"); @@ -579,7 +548,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(" * ", desc); this.emitLine(" *"); } - this.emitLine(" * @param ", this.phpType(false, p.type, false, "", "|null")); + this.emitLine(" * @param ", this.phpDocType(className, p.type)); this.emitLine(" * @return bool"); this.emitLine(" * @throws Exception"); this.emitLine(" */"); @@ -601,27 +570,29 @@ export class PhpRenderer extends ConvenienceRenderer { if (!this._options.fastGet) { this.emitLine(" * @throws Exception"); } - const rendered = this.phpType(false, p.type); - this.emitLine(" * @return ", rendered); + this.emitLine(" * @return ", this.phpDocType(className, p.type)); this.emitLine(" */"); - this.emitBlockWithBraceOnNewLine(["public function ", names.getter, "(): ", rendered], () => { - if (!this._options.fastGet) { - this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { + this.emitBlockWithBraceOnNewLine( + ["public function ", names.getter, "(): ", this.phpType(false, p.type)], + () => { + if (!this._options.fastGet) { + this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { + this.emitLine("return $this->", name, ";"); + }); + this.emitLine( + "throw new Exception('never get to ", + names.getter, + " ", + className, + "::", + name, + "');" + ); + } else { this.emitLine("return $this->", name, ";"); - }); - this.emitLine( - "throw new Exception('never get to ", - names.getter, - " ", - className, - "::", - name, - "');" - ); - } else { - this.emitLine("return $this->", name, ";"); + } } - }); + ); } } protected emitSetMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { @@ -631,7 +602,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(" * ", desc); this.emitLine(" *"); } - this.emitLine(" * @param ", this.phpType(false, p.type, false, "", "|null")); + this.emitLine(" * @param ", this.phpDocType(className, p.type)); this.emitLine(" * @throws Exception"); this.emitLine(" */"); this.emitBlockWithBraceOnNewLine( @@ -656,7 +627,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.ensureBlankLine(); this.emitLine("/**"); this.forEachClassProperty(c, "none", (name, __, p) => { - this.emitLine(" * @param ", this.phpType(false, p.type, false, "", "|null"), " $", name); + this.emitLine(" * @param ", this.phpDocType(className, p.type), " $", name); }); this.emitLine(" */"); this.emitLine("public function __construct("); @@ -792,7 +763,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitBlockWithBraceOnNewLine( [ "/**\n", - " * @param array $arr\n", + " * @param mixed[] $arr\n", " * @return ", className, "\n", From addd83e8871e6db32695fdb3df50c74f9ce5b211 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Thu, 2 Nov 2023 22:17:30 +0100 Subject: [PATCH 12/44] =?UTF-8?q?=E2=9C=A8=20Protected=20properties?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 40272a97e..c9cfecb45 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -621,7 +621,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitBlockWithBraceOnNewLine(["class ", className], () => { this.forEachClassProperty(c, "none", (name, _jsonName, p) => { - this.emitLine("private ", this.phpType(false, p.type), " $", name, ";"); + this.emitLine("protected ", this.phpType(false, p.type), " $", name, ";"); }); this.ensureBlankLine(); @@ -833,7 +833,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine("];"); this.ensureBlankLine(); - this.emitLine("private string $enum;"); + this.emitLine("protected string $enum;"); this.ensureBlankLine(); this.emitBlockWithBraceOnNewLine(["public function __construct(string $enum)"], () => { From a4375256d077eb28f337af6db492fca757f66567 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Thu, 2 Nov 2023 22:36:54 +0100 Subject: [PATCH 13/44] =?UTF-8?q?=E2=9C=A8=20Constructor=20Properties?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 48 +++++++++++++++------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index c9cfecb45..bcd93a8c9 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -36,6 +36,11 @@ export const phpOptions = { fastGet: new BooleanOption("fast-get", "getter without validation", false), withSet: new BooleanOption("with-set", "Create Setter", false), withClosing: new BooleanOption("with-closing", "PHP Closing Tag", false), + constructorProperties: new BooleanOption( + "constructor-properties", + "Declare class properties inside constructor", + true + ), serializeWith: new EnumOption( "serialize-with", "Serialize with", @@ -620,11 +625,15 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitFileHeader(className, []); this.emitBlockWithBraceOnNewLine(["class ", className], () => { - this.forEachClassProperty(c, "none", (name, _jsonName, p) => { - this.emitLine("protected ", this.phpType(false, p.type), " $", name, ";"); - }); + const { constructorProperties } = this._options; + + if (!constructorProperties) { + this.forEachClassProperty(c, "none", (name, _jsonName, p) => { + this.emitLine("protected ", this.phpType(false, p.type), " $", name, ";"); + }); + this.ensureBlankLine(); + } - this.ensureBlankLine(); this.emitLine("/**"); this.forEachClassProperty(c, "none", (name, __, p) => { this.emitLine(" * @param ", this.phpDocType(className, p.type), " $", name); @@ -633,14 +642,18 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine("public function __construct("); this.indent(() => { this.forEachClassProperty(c, "none", (name, __, p, position) => { + const prefix = constructorProperties ? "protected " : ""; const suffix = ["last", "only"].includes(position) ? "" : ","; - this.emitLine(this.phpType(false, p.type), " $", name, suffix); + + this.emitLine(prefix, this.phpType(false, p.type), " $", name, suffix); }); }); this.emitBlock(")", () => { - this.forEachClassProperty(c, "none", name => { - this.emitLine("$this->", name, " = $", name, ";"); - }); + if (!constructorProperties) { + this.forEachClassProperty(c, "none", name => { + this.emitLine("$this->", name, " = $", name, ";"); + }); + } }); this.forEachClassProperty(c, "leading-and-interposing", (name, jsonName, p) => { @@ -819,6 +832,8 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitFileHeader(enumName, []); this.emitDescription(this.descriptionForType(e)); this.emitBlockWithBraceOnNewLine(["class ", enumName], () => { + const { constructorProperties } = this._options; + this.forEachEnumCase(e, "none", (name, jsonName) => { this.emitLine("public const ", name, " = '", jsonName, "';"); }); @@ -833,12 +848,19 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine("];"); this.ensureBlankLine(); - this.emitLine("protected string $enum;"); - this.ensureBlankLine(); + if (!constructorProperties) { + this.emitLine("protected string $enum;"); + this.ensureBlankLine(); + } - this.emitBlockWithBraceOnNewLine(["public function __construct(string $enum)"], () => { - this.emitLine("$this->enum = $enum;"); - }); + this.emitBlockWithBraceOnNewLine( + ["public function __construct(", constructorProperties ? "protected " : "", "string $enum)"], + () => { + if (!constructorProperties) { + this.emitLine("$this->enum = $enum;"); + } + } + ); this.ensureBlankLine(); this.emitEnumSerializationAttributes(e); From 4b1dcb842e860cbab8a357e5d408907dc7e9cb70 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Thu, 2 Nov 2023 23:32:24 +0100 Subject: [PATCH 14/44] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20use=20emitDescriptio?= =?UTF-8?q?nBlock=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 361 ++++++++------------ 1 file changed, 147 insertions(+), 214 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index bcd93a8c9..235fc0e2d 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -507,16 +507,15 @@ export class PhpRenderer extends ConvenienceRenderer { } protected emitFromMethod(names: FunctionNames, p: ClassProperty, className: Name, _name: Name, desc?: string[]) { - this.emitLine("/**"); + const docBlock: Sourcelike[] = []; if (desc !== undefined) { - this.emitLine(" * ", desc); - this.emitLine(" *"); + docBlock.push([...desc, "\n *"]); } const valueType = p.type.kind === "array" ? "mixed[]" : this.phpDocType(className, p.type); - this.emitLine(" * @param ", valueType, " $value"); - this.emitLine(" * @throws Exception"); - this.emitLine(" * @return ", this.phpDocType(className, p.type)); - this.emitLine(" */"); + docBlock.push(["@param ", valueType, " $value"]); + docBlock.push(["@throws Exception"]); + docBlock.push(["@return ", this.phpDocType(className, p.type)]); + this.emitDescriptionBlock(docBlock); this.emitBlockWithBraceOnNewLine( [ "public static function ", @@ -532,14 +531,13 @@ export class PhpRenderer extends ConvenienceRenderer { ); } protected emitToMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { - this.emitLine("/**"); + const docBlock: Sourcelike[] = []; if (desc !== undefined) { - this.emitLine(" * ", desc); - this.emitLine(" *"); + docBlock.push([...desc, "\n *"]); } - this.emitLine(" * @throws Exception"); - this.emitLine(" * @return ", this.phpDocType(className, p.type)); - this.emitLine(" */"); + docBlock.push(["@throws Exception"]); + docBlock.push(["@return ", this.phpDocType(className, p.type)]); + this.emitDescriptionBlock(docBlock); this.emitBlockWithBraceOnNewLine(["public function ", names.to, "(): ", this.phpType(false, p.type)], () => { this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { this.phpToObjConvert(className, p.type, ["return "], ["$this->", name]); @@ -548,15 +546,14 @@ export class PhpRenderer extends ConvenienceRenderer { }); } protected emitValidateMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { - this.emitLine("/**"); + const docBlock: Sourcelike[] = []; if (desc !== undefined) { - this.emitLine(" * ", desc); - this.emitLine(" *"); + docBlock.push([...desc, "\n *"]); } - this.emitLine(" * @param ", this.phpDocType(className, p.type)); - this.emitLine(" * @return bool"); - this.emitLine(" * @throws Exception"); - this.emitLine(" */"); + docBlock.push(["@param ", this.phpDocType(className, p.type), " $value"]); + docBlock.push(["@throws Exception"]); + docBlock.push(["@return bool"]); + this.emitDescriptionBlock(docBlock); this.emitBlockWithBraceOnNewLine( ["public static function ", names.validate, "(", this.phpType(false, p.type), " $value): bool"], () => { @@ -566,59 +563,54 @@ export class PhpRenderer extends ConvenienceRenderer { ); } protected emitGetMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { - if (this._options.withGet) { - this.emitLine("/**"); - if (desc !== undefined) { - this.emitLine(" * ", desc); - this.emitLine(" *"); - } - if (!this._options.fastGet) { - this.emitLine(" * @throws Exception"); - } - this.emitLine(" * @return ", this.phpDocType(className, p.type)); - this.emitLine(" */"); - this.emitBlockWithBraceOnNewLine( - ["public function ", names.getter, "(): ", this.phpType(false, p.type)], - () => { - if (!this._options.fastGet) { - this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { - this.emitLine("return $this->", name, ";"); - }); - this.emitLine( - "throw new Exception('never get to ", - names.getter, - " ", - className, - "::", - name, - "');" - ); - } else { - this.emitLine("return $this->", name, ";"); - } - } - ); + if (!this._options.withGet) { + return; + } + + const docBlock: Sourcelike[] = []; + if (desc !== undefined) { + docBlock.push([...desc, "\n *"]); + } + if (!this._options.fastGet) { + docBlock.push(["@throws Exception"]); } + docBlock.push(["@return ", this.phpDocType(className, p.type)]); + this.emitDescriptionBlock(docBlock); + this.emitBlockWithBraceOnNewLine( + ["public function ", names.getter, "(): ", this.phpType(false, p.type)], + () => { + if (this._options.fastGet) { + return this.emitLine("return $this->", name, ";"); + } + + this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { + this.emitLine("return $this->", name, ";"); + }); + this.emitLine("throw new Exception('never get to ", names.getter, " ", className, "::", name, "');"); + } + ); } protected emitSetMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { - if (this._options.withSet) { - this.emitLine("/**"); - if (desc !== undefined) { - this.emitLine(" * ", desc); - this.emitLine(" *"); - } - this.emitLine(" * @param ", this.phpDocType(className, p.type)); - this.emitLine(" * @throws Exception"); - this.emitLine(" */"); - this.emitBlockWithBraceOnNewLine( - ["public function ", names.setter, "(", this.phpType(false, p.type), " $value)"], - () => { - this.emitBlock(["if (", className, "::", names.validate, "($value))"], () => { - this.emitLine("$this->", name, " = $value;"); - }); - } - ); + if (!this._options.withSet) { + return; } + + const docBlock: Sourcelike[] = []; + if (desc !== undefined) { + docBlock.push([...desc, "\n *"]); + } + docBlock.push(["@param ", this.phpDocType(className, p.type)]); + docBlock.push(["@throws Exception"]); + docBlock.push(["@return void"]); + this.emitDescriptionBlock(docBlock); + this.emitBlockWithBraceOnNewLine( + ["public function ", names.setter, "(", this.phpType(false, p.type), " $value): void"], + () => { + this.emitBlock(["if (", className, "::", names.validate, "($value))"], () => { + this.emitLine("$this->", name, " = $value;"); + }); + } + ); } protected emitClassDefinition(c: ClassType, className: Name): void { @@ -634,11 +626,11 @@ export class PhpRenderer extends ConvenienceRenderer { this.ensureBlankLine(); } - this.emitLine("/**"); + const docBlock: Sourcelike[] = []; this.forEachClassProperty(c, "none", (name, __, p) => { - this.emitLine(" * @param ", this.phpDocType(className, p.type), " $", name); + docBlock.push(["@param ", this.phpDocType(className, p.type), " $", name]); }); - this.emitLine(" */"); + this.emitDescriptionBlock(docBlock); this.emitLine("public function __construct("); this.indent(() => { this.forEachClassProperty(c, "none", (name, __, p, position) => { @@ -673,136 +665,91 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.ensureBlankLine(); - this.emitBlockWithBraceOnNewLine( - ["/**\n", " * @throws Exception\n", " * @return bool\n", " */\n", "public function validate(): bool"], - () => { - this.forEachClassProperty(c, "none", (name, _jsonName, _p, position) => { - const isFirstLine = ["first", "only"].includes(position); - const isLastLine = ["last", "only"].includes(position); - const prefix = isFirstLine ? "return " : "|| "; - const suffix = isLastLine ? ";" : ""; - - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - const line: Sourcelike = [ - prefix, - className, - "::", - names.validate, - "($this->", - name, - ")", - suffix - ]; - - if (isFirstLine) { + this.emitDescriptionBlock(["@throws Exception", "@return bool"]); + this.emitBlockWithBraceOnNewLine(["public function validate(): bool"], () => { + this.forEachClassProperty(c, "none", (name, _jsonName, _p, position) => { + const isFirstLine = ["first", "only"].includes(position); + const isLastLine = ["last", "only"].includes(position); + const prefix = isFirstLine ? "return " : "|| "; + const suffix = isLastLine ? ";" : ""; + + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + const line: Sourcelike = [prefix, className, "::", names.validate, "($this->", name, ")", suffix]; + + if (isFirstLine) { + this.emitLine(line); + } else { + this.indent(() => { this.emitLine(line); - } else { - this.indent(() => { - this.emitLine(line); - }); - } - }); - } - ); + }); + } + }); + }); this.ensureBlankLine(); if (this._options.serializeWith === SerializeWith.stdClass) { - this.emitBlockWithBraceOnNewLine( - [ - "/**\n", - " * @return stdClass\n", - " * @throws Exception\n", - " */\n", - "public function to(): stdClass" - ], - () => { - this.emitLine("$out = new stdClass();"); + this.emitDescriptionBlock(["@throws Exception", "@return stdClass"]); + this.emitBlockWithBraceOnNewLine(["public function to(): stdClass"], () => { + this.emitLine("$out = new stdClass();"); + this.forEachClassProperty(c, "none", (name, jsonName) => { + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine("$out->", jsonName, " = $this->", names.to, "();"); + }); + this.emitLine("return $out;"); + }); + } else { + this.emitDescriptionBlock(["@throws Exception", "@return mixed[]"]); + this.emitBlockWithBraceOnNewLine(["public function to(): array"], () => { + this.emitLine("return ["); + this.indent(() => { this.forEachClassProperty(c, "none", (name, jsonName) => { const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine("$out->", jsonName, " = $this->", names.to, "();"); - }); - this.emitLine("return $out;"); - } - ); - } else { - this.emitBlockWithBraceOnNewLine( - ["/**\n", " * @return array\n", " * @throws Exception\n", " */\n", "public function to(): array"], - () => { - this.emitLine("return ["); - this.indent(() => { - this.forEachClassProperty(c, "none", (name, jsonName) => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine("'", jsonName, "' => $this->", names.to, "(),"); - }); + this.emitLine("'", jsonName, "' => $this->", names.to, "(),"); }); - this.emitLine("];"); - } - ); + }); + this.emitLine("];"); + }); } this.ensureBlankLine(); if (this._options.serializeWith === SerializeWith.stdClass) { - this.emitBlockWithBraceOnNewLine( - [ - "/**\n", - " * @param stdClass $obj\n", - " * @return ", - className, - "\n", - " * @throws Exception\n", - " */\n", - "public static function from(stdClass $obj): ", - className - ], - () => { - if (this._options.fastGet) { - this.forEachClassProperty(c, "none", name => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); - }); - } - this.emitLine("return new ", className, "("); - this.indent(() => { - this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { - const suffix = ["last", "only"].includes(position) ? "" : ","; - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.from, "($obj->", jsonName, ")", suffix); - }); + this.emitDescriptionBlock(["@param stdClass $obj", "@throws Exception", ["@return ", className]]); + this.emitBlockWithBraceOnNewLine(["public static function from(stdClass $obj): ", className], () => { + if (this._options.fastGet) { + this.forEachClassProperty(c, "none", name => { + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); }); - this.emitLine(");"); } - ); + this.emitLine("return new ", className, "("); + this.indent(() => { + this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { + const suffix = ["last", "only"].includes(position) ? "" : ","; + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.from, "($obj->", jsonName, ")", suffix); + }); + }); + this.emitLine(");"); + }); } else { - this.emitBlockWithBraceOnNewLine( - [ - "/**\n", - " * @param mixed[] $arr\n", - " * @return ", - className, - "\n", - " * @throws Exception\n", - " */\n", - "public static function from(array $arr): ", - className - ], - () => { - if (this._options.fastGet) { - this.forEachClassProperty(c, "none", name => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); - }); - } - this.emitLine("return new ", className, "("); - this.indent(() => { - this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { - const suffix = ["last", "only"].includes(position) ? "" : ","; - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.from, "($arr['", jsonName, "'])", suffix); - }); + this.emitDescriptionBlock(["@param mixed[] $arr", "@throws Exception", ["@return ", className]]); + this.emitBlockWithBraceOnNewLine(["public static function from(array $arr): ", className], () => { + if (this._options.fastGet) { + this.forEachClassProperty(c, "none", name => { + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); }); - this.emitLine(");"); } - ); + this.emitLine("return new ", className, "("); + this.indent(() => { + this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { + const suffix = ["last", "only"].includes(position) ? "" : ","; + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.from, "($arr['", jsonName, "'])", suffix); + }); + }); + this.emitLine(");"); + }); } }); this.finishFile(); @@ -865,37 +812,23 @@ export class PhpRenderer extends ConvenienceRenderer { this.ensureBlankLine(); this.emitEnumSerializationAttributes(e); - this.emitBlockWithBraceOnNewLine( - ["/**\n", " * @return string\n", " * @throws Exception\n", " */\n", "public function to(): string"], - () => { - this.emitBlock(["if (in_array($this->enum, ", enumName, "::VALUES, true))"], () => { - this.emitLine("return $this->enum;"); - }); - this.emitLine("throw new Exception('the give value is not an enum-value.');"); - } - ); + this.emitDescriptionBlock(["@throws Exception", "@return string"]); + this.emitBlockWithBraceOnNewLine(["public function to(): string"], () => { + this.emitBlock(["if (in_array($this->enum, ", enumName, "::VALUES, true))"], () => { + this.emitLine("return $this->enum;"); + }); + this.emitLine("throw new Exception('the give value is not an enum-value.');"); + }); this.ensureBlankLine(); this.emitEnumDeserializationAttributes(e); - this.emitBlockWithBraceOnNewLine( - [ - "/**\n", - " * @param mixed\n", - " * @return ", - enumName, - "\n", - " * @throws Exception\n", - " */\n", - "public static function from($obj): ", - enumName - ], - () => { - this.emitBlock(["if (in_array($obj, ", enumName, "::VALUES, true))"], () => { - this.emitLine("return new ", enumName, "($obj);"); - }); - this.emitLine("throw new Exception('Cannot deserialize ", enumName, "');"); - } - ); + this.emitDescriptionBlock(["@param mixed", "@throws Exception", ["@return ", enumName]]); + this.emitBlockWithBraceOnNewLine(["public static function from($obj): ", enumName], () => { + this.emitBlock(["if (in_array($obj, ", enumName, "::VALUES, true))"], () => { + this.emitLine("return new ", enumName, "($obj);"); + }); + this.emitLine("throw new Exception('Cannot deserialize ", enumName, "');"); + }); }); this.finishFile(); } From 36927287434509c8608f5c214e1b61d8d5816a27 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Thu, 2 Nov 2023 23:35:29 +0100 Subject: [PATCH 15/44] =?UTF-8?q?=F0=9F=97=91=20remove=20useless=20phpType?= =?UTF-8?q?=20parameter=20=5Freference?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 46 +++++++++------------ 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 235fc0e2d..d78ad453f 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -273,7 +273,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine("}"); } - protected phpType(_reference: boolean, t: Type, isOptional = false, prefix = "?", suffix = ""): Sourcelike { + protected phpType(t: Type, isOptional = false, prefix = "?", suffix = ""): Sourcelike { function optionalize(s: Sourcelike) { return [isOptional ? prefix : "", s, isOptional ? suffix : ""]; } @@ -291,7 +291,9 @@ export class PhpRenderer extends ConvenienceRenderer { enumType => optionalize(this.nameForNamedType(enumType)), unionType => { const nullable = nullableFromUnion(unionType); - if (nullable !== null) return this.phpType(true, nullable, true, prefix, suffix); + if (nullable !== null) { + return this.phpType(nullable, true, prefix, suffix); + } return this.nameForNamedType(unionType); }, transformedStringType => { @@ -517,14 +519,7 @@ export class PhpRenderer extends ConvenienceRenderer { docBlock.push(["@return ", this.phpDocType(className, p.type)]); this.emitDescriptionBlock(docBlock); this.emitBlockWithBraceOnNewLine( - [ - "public static function ", - names.from, - "(", - this.phpType(false, p.type), - " $value): ", - this.phpType(false, p.type) - ], + ["public static function ", names.from, "(", this.phpType(p.type), " $value): ", this.phpType(p.type)], () => { this.phpFromObjConvert(className, p.type, ["return "], ["$value"]); } @@ -538,7 +533,7 @@ export class PhpRenderer extends ConvenienceRenderer { docBlock.push(["@throws Exception"]); docBlock.push(["@return ", this.phpDocType(className, p.type)]); this.emitDescriptionBlock(docBlock); - this.emitBlockWithBraceOnNewLine(["public function ", names.to, "(): ", this.phpType(false, p.type)], () => { + this.emitBlockWithBraceOnNewLine(["public function ", names.to, "(): ", this.phpType(p.type)], () => { this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { this.phpToObjConvert(className, p.type, ["return "], ["$this->", name]); }); @@ -555,7 +550,7 @@ export class PhpRenderer extends ConvenienceRenderer { docBlock.push(["@return bool"]); this.emitDescriptionBlock(docBlock); this.emitBlockWithBraceOnNewLine( - ["public static function ", names.validate, "(", this.phpType(false, p.type), " $value): bool"], + ["public static function ", names.validate, "(", this.phpType(p.type), " $value): bool"], () => { this.phpValidate(className, p.type, name, "$value"); this.emitLine("return true;"); @@ -576,19 +571,16 @@ export class PhpRenderer extends ConvenienceRenderer { } docBlock.push(["@return ", this.phpDocType(className, p.type)]); this.emitDescriptionBlock(docBlock); - this.emitBlockWithBraceOnNewLine( - ["public function ", names.getter, "(): ", this.phpType(false, p.type)], - () => { - if (this._options.fastGet) { - return this.emitLine("return $this->", name, ";"); - } - - this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { - this.emitLine("return $this->", name, ";"); - }); - this.emitLine("throw new Exception('never get to ", names.getter, " ", className, "::", name, "');"); + this.emitBlockWithBraceOnNewLine(["public function ", names.getter, "(): ", this.phpType(p.type)], () => { + if (this._options.fastGet) { + return this.emitLine("return $this->", name, ";"); } - ); + + this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { + this.emitLine("return $this->", name, ";"); + }); + this.emitLine("throw new Exception('never get to ", names.getter, " ", className, "::", name, "');"); + }); } protected emitSetMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { if (!this._options.withSet) { @@ -604,7 +596,7 @@ export class PhpRenderer extends ConvenienceRenderer { docBlock.push(["@return void"]); this.emitDescriptionBlock(docBlock); this.emitBlockWithBraceOnNewLine( - ["public function ", names.setter, "(", this.phpType(false, p.type), " $value): void"], + ["public function ", names.setter, "(", this.phpType(p.type), " $value): void"], () => { this.emitBlock(["if (", className, "::", names.validate, "($value))"], () => { this.emitLine("$this->", name, " = $value;"); @@ -621,7 +613,7 @@ export class PhpRenderer extends ConvenienceRenderer { if (!constructorProperties) { this.forEachClassProperty(c, "none", (name, _jsonName, p) => { - this.emitLine("protected ", this.phpType(false, p.type), " $", name, ";"); + this.emitLine("protected ", this.phpType(p.type), " $", name, ";"); }); this.ensureBlankLine(); } @@ -637,7 +629,7 @@ export class PhpRenderer extends ConvenienceRenderer { const prefix = constructorProperties ? "protected " : ""; const suffix = ["last", "only"].includes(position) ? "" : ","; - this.emitLine(prefix, this.phpType(false, p.type), " $", name, suffix); + this.emitLine(prefix, this.phpType(p.type), " $", name, suffix); }); }); this.emitBlock(")", () => { From 1644ce9c0878e39c43fefb6b75442515c734ebb1 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Thu, 2 Nov 2023 23:36:23 +0100 Subject: [PATCH 16/44] =?UTF-8?q?=F0=9F=97=91=20Remove=20"This=20is=20a=20?= =?UTF-8?q?autogenerated=20file"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index d78ad453f..3e80649e9 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -252,7 +252,6 @@ export class PhpRenderer extends ConvenienceRenderer { protected emitFileHeader(fileName: Sourcelike, _imports: string[]): void { this.startFile(fileName); - this.emitLine("// This is a autogenerated file:", fileName); this.ensureBlankLine(); } From a555d8e58490e8a8a3daf2f141c563e49b66bb0d Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Thu, 2 Nov 2023 23:53:13 +0100 Subject: [PATCH 17/44] =?UTF-8?q?=E2=9C=A8=20PHP=208.1=20Enums?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 3e80649e9..23b43e630 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -36,6 +36,7 @@ export const phpOptions = { fastGet: new BooleanOption("fast-get", "getter without validation", false), withSet: new BooleanOption("with-set", "Create Setter", false), withClosing: new BooleanOption("with-closing", "PHP Closing Tag", false), + nativeEnums: new BooleanOption("native-enums", "Use enums instead of enum classes", true), constructorProperties: new BooleanOption( "constructor-properties", "Declare class properties inside constructor", @@ -370,7 +371,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine("return to(", ...args, ");"); }, - _enumType => this.emitLine(...lhs, ...args, "->to();"), + _enumType => this.emitLine(...lhs, ...args, "->value;"), unionType => { const nullable = nullableFromUnion(unionType); if (nullable !== null) { @@ -485,7 +486,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine("}"); }, _enumType => { - this.emitLine(scopeAttrName, "->to();"); + this.emitLine(scopeAttrName, "->value;"); }, unionType => { const nullable = nullableFromUnion(unionType); @@ -769,6 +770,16 @@ export class PhpRenderer extends ConvenienceRenderer { protected emitEnumDefinition(e: EnumType, enumName: Name): void { this.emitFileHeader(enumName, []); this.emitDescription(this.descriptionForType(e)); + + if (this._options.nativeEnums) { + this.emitBlockWithBraceOnNewLine(["enum ", enumName, ": string"], () => { + this.forEachEnumCase(e, "none", (name, jsonName) => { + this.emitLine("case ", name, " = '", jsonName, "';"); + }); + }); + return this.finishFile(); + } + this.emitBlockWithBraceOnNewLine(["class ", enumName], () => { const { constructorProperties } = this._options; From e68caabce7a1484b64804b63199d6a01eeb373f9 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Thu, 2 Nov 2023 23:53:32 +0100 Subject: [PATCH 18/44] =?UTF-8?q?=F0=9F=94=A7=20Set=20Default=20with-get?= =?UTF-8?q?=20to=20false?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 23b43e630..b46ff8b90 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -32,7 +32,7 @@ export enum SerializeWith { } export const phpOptions = { - withGet: new BooleanOption("with-get", "Create Getter", true), + withGet: new BooleanOption("with-get", "Create Getter", false), fastGet: new BooleanOption("fast-get", "getter without validation", false), withSet: new BooleanOption("with-set", "Create Setter", false), withClosing: new BooleanOption("with-closing", "PHP Closing Tag", false), From daab8ad54424e68c9aa48a6fd2040158c66667c9 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 00:02:01 +0100 Subject: [PATCH 19/44] =?UTF-8?q?=E2=9C=A8=20Readonly=20Properties?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index b46ff8b90..cf44da8ba 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -36,6 +36,7 @@ export const phpOptions = { fastGet: new BooleanOption("fast-get", "getter without validation", false), withSet: new BooleanOption("with-set", "Create Setter", false), withClosing: new BooleanOption("with-closing", "PHP Closing Tag", false), + readonlyProperties: new BooleanOption("readonly-properties", "Use public readonly instead of protected", true), nativeEnums: new BooleanOption("native-enums", "Use enums instead of enum classes", true), constructorProperties: new BooleanOption( "constructor-properties", @@ -609,11 +610,12 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitFileHeader(className, []); this.emitBlockWithBraceOnNewLine(["class ", className], () => { - const { constructorProperties } = this._options; + const { constructorProperties, readonlyProperties } = this._options; + const accessor = readonlyProperties ? "public readonly" : "protected"; if (!constructorProperties) { this.forEachClassProperty(c, "none", (name, _jsonName, p) => { - this.emitLine("protected ", this.phpType(p.type), " $", name, ";"); + this.emitLine(accessor, " ", this.phpType(p.type), " $", name, ";"); }); this.ensureBlankLine(); } @@ -626,10 +628,10 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine("public function __construct("); this.indent(() => { this.forEachClassProperty(c, "none", (name, __, p, position) => { - const prefix = constructorProperties ? "protected " : ""; + const prefix = constructorProperties ? [accessor, " "] : [""]; const suffix = ["last", "only"].includes(position) ? "" : ","; - this.emitLine(prefix, this.phpType(p.type), " $", name, suffix); + this.emitLine(...prefix, this.phpType(p.type), " $", name, suffix); }); }); this.emitBlock(")", () => { @@ -781,7 +783,8 @@ export class PhpRenderer extends ConvenienceRenderer { } this.emitBlockWithBraceOnNewLine(["class ", enumName], () => { - const { constructorProperties } = this._options; + const { constructorProperties, readonlyProperties } = this._options; + const accessor = readonlyProperties ? "public readonly" : "protected"; this.forEachEnumCase(e, "none", (name, jsonName) => { this.emitLine("public const ", name, " = '", jsonName, "';"); @@ -798,12 +801,12 @@ export class PhpRenderer extends ConvenienceRenderer { this.ensureBlankLine(); if (!constructorProperties) { - this.emitLine("protected string $enum;"); + this.emitLine(accessor, " string $enum;"); this.ensureBlankLine(); } this.emitBlockWithBraceOnNewLine( - ["public function __construct(", constructorProperties ? "protected " : "", "string $enum)"], + ["public function __construct(", constructorProperties ? accessor + " " : "", "string $enum)"], () => { if (!constructorProperties) { this.emitLine("$this->enum = $enum;"); From 786be5dad3b2d2e7a2ff84df0a6d5077ab79925d Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 00:11:24 +0100 Subject: [PATCH 20/44] =?UTF-8?q?=F0=9F=92=84=20Simplify=20Methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 30 ++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index cf44da8ba..b1cb95040 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -315,7 +315,7 @@ export class PhpRenderer extends ConvenienceRenderer { ); } - protected phpDocType(className: Name, t: Type): Sourcelike { + protected phpDocType(t: Type): Sourcelike { return matchType( t, _anyType => "any", @@ -324,14 +324,14 @@ export class PhpRenderer extends ConvenienceRenderer { _integerType => "int", _doubleType => "float", _stringType => "string", - arrayType => [this.phpDocType(className, arrayType.items), "[]"], + arrayType => [this.phpDocType(arrayType.items), "[]"], classType => this.nameForNamedType(classType), _mapType => (this._options.serializeWith === SerializeWith.stdClass ? "stdClass" : "array"), enumType => this.nameForNamedType(enumType), unionType => { const nullable = nullableFromUnion(unionType); if (nullable !== null) { - return ["?", this.phpDocType(className, nullable)]; + return ["?", this.phpDocType(nullable)]; } throw Error("union are not supported"); }, @@ -344,7 +344,7 @@ export class PhpRenderer extends ConvenienceRenderer { ); } - protected phpToObjConvert(className: Name, t: Type, lhs: Sourcelike[], args: Sourcelike[]) { + protected phpToObjConvert(t: Type, lhs: Sourcelike[], args: Sourcelike[]) { return matchType( t, _anyType => this.emitLine(...lhs, ...args, ";"), @@ -356,7 +356,7 @@ export class PhpRenderer extends ConvenienceRenderer { arrayType => { this.emitLine(...lhs, "array_map(function ($value) {"); this.indent(() => { - this.phpToObjConvert(className, arrayType.items, ["return "], ["$value"]); + this.phpToObjConvert(arrayType.items, ["return "], ["$value"]); }); this.emitLine("}, ", ...args, ");"); }, @@ -366,7 +366,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitBlockWithBraceOnNewLine(["function to($my): stdClass"], () => { this.emitLine("$out = new stdClass();"); this.emitBlock(["foreach ($my as $k => $v)"], () => { - this.phpToObjConvert(className, mapType.values, ["$my->$k = "], ["$v"]); + this.phpToObjConvert(mapType.values, ["$my->$k = "], ["$v"]); }); this.emitLine("return $out;"); }); @@ -377,7 +377,7 @@ export class PhpRenderer extends ConvenienceRenderer { const nullable = nullableFromUnion(unionType); if (nullable !== null) { this.emitLine("if (!is_null(", ...args, ")) {"); - this.indent(() => this.phpToObjConvert(className, nullable, lhs, args)); + this.indent(() => this.phpToObjConvert(nullable, lhs, args)); this.emitLine("} else {"); this.indent(() => this.emitLine(...lhs, " null;")); this.emitLine("}"); @@ -514,10 +514,10 @@ export class PhpRenderer extends ConvenienceRenderer { if (desc !== undefined) { docBlock.push([...desc, "\n *"]); } - const valueType = p.type.kind === "array" ? "mixed[]" : this.phpDocType(className, p.type); + const valueType = p.type.kind === "array" ? "mixed[]" : this.phpDocType(p.type); docBlock.push(["@param ", valueType, " $value"]); docBlock.push(["@throws Exception"]); - docBlock.push(["@return ", this.phpDocType(className, p.type)]); + docBlock.push(["@return ", this.phpDocType(p.type)]); this.emitDescriptionBlock(docBlock); this.emitBlockWithBraceOnNewLine( ["public static function ", names.from, "(", this.phpType(p.type), " $value): ", this.phpType(p.type)], @@ -532,11 +532,11 @@ export class PhpRenderer extends ConvenienceRenderer { docBlock.push([...desc, "\n *"]); } docBlock.push(["@throws Exception"]); - docBlock.push(["@return ", this.phpDocType(className, p.type)]); + docBlock.push(["@return ", this.phpDocType(p.type)]); this.emitDescriptionBlock(docBlock); this.emitBlockWithBraceOnNewLine(["public function ", names.to, "(): ", this.phpType(p.type)], () => { this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { - this.phpToObjConvert(className, p.type, ["return "], ["$this->", name]); + this.phpToObjConvert(p.type, ["return "], ["$this->", name]); }); this.emitLine("throw new Exception('never get to this ", className, "::", name, "');"); }); @@ -546,7 +546,7 @@ export class PhpRenderer extends ConvenienceRenderer { if (desc !== undefined) { docBlock.push([...desc, "\n *"]); } - docBlock.push(["@param ", this.phpDocType(className, p.type), " $value"]); + docBlock.push(["@param ", this.phpDocType(p.type), " $value"]); docBlock.push(["@throws Exception"]); docBlock.push(["@return bool"]); this.emitDescriptionBlock(docBlock); @@ -570,7 +570,7 @@ export class PhpRenderer extends ConvenienceRenderer { if (!this._options.fastGet) { docBlock.push(["@throws Exception"]); } - docBlock.push(["@return ", this.phpDocType(className, p.type)]); + docBlock.push(["@return ", this.phpDocType(p.type)]); this.emitDescriptionBlock(docBlock); this.emitBlockWithBraceOnNewLine(["public function ", names.getter, "(): ", this.phpType(p.type)], () => { if (this._options.fastGet) { @@ -592,7 +592,7 @@ export class PhpRenderer extends ConvenienceRenderer { if (desc !== undefined) { docBlock.push([...desc, "\n *"]); } - docBlock.push(["@param ", this.phpDocType(className, p.type)]); + docBlock.push(["@param ", this.phpDocType(p.type)]); docBlock.push(["@throws Exception"]); docBlock.push(["@return void"]); this.emitDescriptionBlock(docBlock); @@ -622,7 +622,7 @@ export class PhpRenderer extends ConvenienceRenderer { const docBlock: Sourcelike[] = []; this.forEachClassProperty(c, "none", (name, __, p) => { - docBlock.push(["@param ", this.phpDocType(className, p.type), " $", name]); + docBlock.push(["@param ", this.phpDocType(p.type), " $", name]); }); this.emitDescriptionBlock(docBlock); this.emitLine("public function __construct("); From 4acad1976bd338e117a03bd4c3484ce057d5e860 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 18:55:00 +0100 Subject: [PATCH 21/44] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Add=20emitMethod=20m?= =?UTF-8?q?ethod?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 428 ++++++++++++-------- 1 file changed, 256 insertions(+), 172 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index b1cb95040..ee5495b1f 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -123,6 +123,19 @@ export interface FunctionNames { readonly to: Name; } +type Method = { + name: Sourcelike; + body: () => void; + desc?: string[]; + args?: Sourcelike[]; + returnType?: Type | Sourcelike; + docBlockArgs?: Sourcelike[]; + docBlockReturnType?: Type | Sourcelike; + throws?: boolean; + isStatic?: boolean; + isProtected?: boolean; +}; + export class PhpRenderer extends ConvenienceRenderer { private readonly _gettersAndSettersForPropertyName = new Map(); private _haveEmittedLeadingComments = false; @@ -509,101 +522,139 @@ export class PhpRenderer extends ConvenienceRenderer { ); } - protected emitFromMethod(names: FunctionNames, p: ClassProperty, className: Name, _name: Name, desc?: string[]) { + protected emitMethod(method: Method): void { + const { + name, + body, + desc, + args = [], + returnType = "void", + docBlockArgs = args, + docBlockReturnType = returnType, + throws = false, + isStatic = false, + isProtected = false + } = method; + const docBlock: Sourcelike[] = []; if (desc !== undefined) { docBlock.push([...desc, "\n *"]); } - const valueType = p.type.kind === "array" ? "mixed[]" : this.phpDocType(p.type); - docBlock.push(["@param ", valueType, " $value"]); - docBlock.push(["@throws Exception"]); - docBlock.push(["@return ", this.phpDocType(p.type)]); + for (const docBlockArg of docBlockArgs) { + docBlock.push(["@param ", docBlockArg]); + } + if (throws) { + docBlock.push(["@throws Exception"]); + } + docBlock.push([ + "@return ", + docBlockReturnType instanceof Type ? this.phpDocType(docBlockReturnType) : docBlockReturnType + ]); this.emitDescriptionBlock(docBlock); - this.emitBlockWithBraceOnNewLine( - ["public static function ", names.from, "(", this.phpType(p.type), " $value): ", this.phpType(p.type)], - () => { - this.phpFromObjConvert(className, p.type, ["return "], ["$value"]); + + const line: Sourcelike[] = []; + line.push(isProtected ? "protected" : "public"); + line.push(isStatic ? " static" : ""); + line.push(" function ", name, "("); + args.forEach((arg, i) => { + if (i > 0) { + line.push(", "); } - ); + line.push(arg); + }); + line.push("): ", returnType instanceof Type ? this.phpType(returnType) : returnType); + this.emitBlockWithBraceOnNewLine(line, body); + } + + protected emitFromMethod(names: FunctionNames, p: ClassProperty, className: Name, _name: Name, desc?: string[]) { + this.emitMethod({ + name: names.from, + body: () => { + this.phpFromObjConvert(className, p.type, ["return "], ["$value"]); + }, + desc, + args: [[this.phpType(p.type), " $value"]], + returnType: p.type, + docBlockArgs: [[p.type.kind === "array" ? "mixed[]" : this.phpDocType(p.type), " $value"]], + throws: true, + isStatic: true + }); } + protected emitToMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { - const docBlock: Sourcelike[] = []; - if (desc !== undefined) { - docBlock.push([...desc, "\n *"]); - } - docBlock.push(["@throws Exception"]); - docBlock.push(["@return ", this.phpDocType(p.type)]); - this.emitDescriptionBlock(docBlock); - this.emitBlockWithBraceOnNewLine(["public function ", names.to, "(): ", this.phpType(p.type)], () => { - this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { - this.phpToObjConvert(p.type, ["return "], ["$this->", name]); - }); - this.emitLine("throw new Exception('never get to this ", className, "::", name, "');"); + this.emitMethod({ + name: names.to, + body: () => { + this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { + this.phpToObjConvert(p.type, ["return "], ["$this->", name]); + }); + this.emitLine("throw new Exception('never get to this ", className, "::", name, "');"); + }, + desc, + returnType: p.type, + throws: true }); } + protected emitValidateMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { - const docBlock: Sourcelike[] = []; - if (desc !== undefined) { - docBlock.push([...desc, "\n *"]); - } - docBlock.push(["@param ", this.phpDocType(p.type), " $value"]); - docBlock.push(["@throws Exception"]); - docBlock.push(["@return bool"]); - this.emitDescriptionBlock(docBlock); - this.emitBlockWithBraceOnNewLine( - ["public static function ", names.validate, "(", this.phpType(p.type), " $value): bool"], - () => { + this.emitMethod({ + name: names.validate, + body: () => { this.phpValidate(className, p.type, name, "$value"); this.emitLine("return true;"); - } - ); + }, + desc, + args: [[this.phpType(p.type), " $value"]], + docBlockArgs: [[this.phpDocType(p.type), " $value"]], + returnType: "bool", + throws: true, + isStatic: true + }); } + protected emitGetMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { - if (!this._options.withGet) { + const { withGet, fastGet } = this._options; + + if (!withGet) { return; } - const docBlock: Sourcelike[] = []; - if (desc !== undefined) { - docBlock.push([...desc, "\n *"]); - } - if (!this._options.fastGet) { - docBlock.push(["@throws Exception"]); - } - docBlock.push(["@return ", this.phpDocType(p.type)]); - this.emitDescriptionBlock(docBlock); - this.emitBlockWithBraceOnNewLine(["public function ", names.getter, "(): ", this.phpType(p.type)], () => { - if (this._options.fastGet) { - return this.emitLine("return $this->", name, ";"); - } + this.emitMethod({ + name: names.getter, + body: () => { + if (fastGet) { + return this.emitLine("return $this->", name, ";"); + } - this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { - this.emitLine("return $this->", name, ";"); - }); - this.emitLine("throw new Exception('never get to ", names.getter, " ", className, "::", name, "');"); + this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { + this.emitLine("return $this->", name, ";"); + }); + this.emitLine("throw new Exception('never get to ", names.getter, " ", className, "::", name, "');"); + }, + desc, + returnType: p.type, + throws: true }); } + protected emitSetMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { if (!this._options.withSet) { return; } - const docBlock: Sourcelike[] = []; - if (desc !== undefined) { - docBlock.push([...desc, "\n *"]); - } - docBlock.push(["@param ", this.phpDocType(p.type)]); - docBlock.push(["@throws Exception"]); - docBlock.push(["@return void"]); - this.emitDescriptionBlock(docBlock); - this.emitBlockWithBraceOnNewLine( - ["public function ", names.setter, "(", this.phpType(p.type), " $value): void"], - () => { + this.emitMethod({ + name: names.setter, + body: () => { this.emitBlock(["if (", className, "::", names.validate, "($value))"], () => { this.emitLine("$this->", name, " = $value;"); }); - } - ); + }, + desc, + args: [[this.phpType(p.type), " $value"]], + docBlockArgs: [[this.phpDocType(p.type), " $value"]], + returnType: "void", + throws: true + }); } protected emitClassDefinition(c: ClassType, className: Name): void { @@ -659,116 +710,138 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.ensureBlankLine(); - this.emitDescriptionBlock(["@throws Exception", "@return bool"]); - this.emitBlockWithBraceOnNewLine(["public function validate(): bool"], () => { - this.forEachClassProperty(c, "none", (name, _jsonName, _p, position) => { - const isFirstLine = ["first", "only"].includes(position); - const isLastLine = ["last", "only"].includes(position); - const prefix = isFirstLine ? "return " : "|| "; - const suffix = isLastLine ? ";" : ""; - - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - const line: Sourcelike = [prefix, className, "::", names.validate, "($this->", name, ")", suffix]; - - if (isFirstLine) { - this.emitLine(line); - } else { - this.indent(() => { + this.emitMethod({ + name: "validate", + body: () => { + this.forEachClassProperty(c, "none", (name, _jsonName, _p, position) => { + const isFirstLine = ["first", "only"].includes(position); + const isLastLine = ["last", "only"].includes(position); + const prefix = isFirstLine ? "return " : "|| "; + const suffix = isLastLine ? ";" : ""; + + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + const line: Sourcelike = [ + prefix, + className, + "::", + names.validate, + "($this->", + name, + ")", + suffix + ]; + + if (isFirstLine) { this.emitLine(line); - }); - } - }); + } else { + this.indent(() => { + this.emitLine(line); + }); + } + }); + }, + returnType: "bool", + throws: true }); + const { serializeWith } = this._options; + const useStdClass = serializeWith === SerializeWith.stdClass; + this.ensureBlankLine(); - if (this._options.serializeWith === SerializeWith.stdClass) { - this.emitDescriptionBlock(["@throws Exception", "@return stdClass"]); - this.emitBlockWithBraceOnNewLine(["public function to(): stdClass"], () => { - this.emitLine("$out = new stdClass();"); - this.forEachClassProperty(c, "none", (name, jsonName) => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine("$out->", jsonName, " = $this->", names.to, "();"); - }); - this.emitLine("return $out;"); - }); - } else { - this.emitDescriptionBlock(["@throws Exception", "@return mixed[]"]); - this.emitBlockWithBraceOnNewLine(["public function to(): array"], () => { - this.emitLine("return ["); - this.indent(() => { + if (useStdClass) { + this.emitMethod({ + name: "to", + body: () => { + this.emitLine("$out = new stdClass();"); this.forEachClassProperty(c, "none", (name, jsonName) => { const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine("'", jsonName, "' => $this->", names.to, "(),"); + this.emitLine("$out->", jsonName, " = $this->", names.to, "();"); }); - }); - this.emitLine("];"); + this.emitLine("return $out;"); + }, + returnType: "stdClass", + throws: true + }); + } else { + this.emitMethod({ + name: "to", + body: () => { + this.emitLine("return ["); + this.indent(() => { + this.forEachClassProperty(c, "none", (name, jsonName) => { + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine("'", jsonName, "' => $this->", names.to, "(),"); + }); + }); + this.emitLine("];"); + }, + returnType: "array", + docBlockReturnType: "mixed[]", + throws: true }); } this.ensureBlankLine(); - if (this._options.serializeWith === SerializeWith.stdClass) { - this.emitDescriptionBlock(["@param stdClass $obj", "@throws Exception", ["@return ", className]]); - this.emitBlockWithBraceOnNewLine(["public static function from(stdClass $obj): ", className], () => { - if (this._options.fastGet) { - this.forEachClassProperty(c, "none", name => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); - }); - } - this.emitLine("return new ", className, "("); - this.indent(() => { - this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { - const suffix = ["last", "only"].includes(position) ? "" : ","; - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.from, "($obj->", jsonName, ")", suffix); + if (useStdClass) { + this.emitMethod({ + name: "from", + body: () => { + if (this._options.fastGet) { + this.forEachClassProperty(c, "none", name => { + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); + }); + } + this.emitLine("return new ", className, "("); + this.indent(() => { + this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { + const suffix = ["last", "only"].includes(position) ? "" : ","; + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.from, "($obj->", jsonName, ")", suffix); + }); }); - }); - this.emitLine(");"); + this.emitLine(");"); + }, + args: ["stdClass $obj"], + returnType: className, + throws: true, + isStatic: true }); } else { - this.emitDescriptionBlock(["@param mixed[] $arr", "@throws Exception", ["@return ", className]]); - this.emitBlockWithBraceOnNewLine(["public static function from(array $arr): ", className], () => { - if (this._options.fastGet) { - this.forEachClassProperty(c, "none", name => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); - }); - } - this.emitLine("return new ", className, "("); - this.indent(() => { - this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { - const suffix = ["last", "only"].includes(position) ? "" : ","; - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.from, "($arr['", jsonName, "'])", suffix); + this.emitMethod({ + name: "from", + body: () => { + if (this._options.fastGet) { + this.forEachClassProperty(c, "none", name => { + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); + }); + } + this.emitLine("return new ", className, "("); + this.indent(() => { + this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { + const suffix = ["last", "only"].includes(position) ? "" : ","; + const names = defined(this._gettersAndSettersForPropertyName.get(name)); + this.emitLine(className, "::", names.from, "($arr['", jsonName, "'])", suffix); + }); }); - }); - this.emitLine(");"); + this.emitLine(");"); + }, + args: ["array $arr"], + docBlockArgs: ["mixed[] $arr"], + returnType: className, + throws: true, + isStatic: true }); } }); this.finishFile(); } - protected emitUnionAttributes(_u: UnionType, _unionName: Name): void { - // empty - } - - protected emitUnionSerializer(_u: UnionType, _unionName: Name): void { - // empty - } - protected emitUnionDefinition(_u: UnionType, _unionName: Name): void { throw Error("emitUnionDefinition not implemented"); } - protected emitEnumSerializationAttributes(_e: EnumType) { - // Empty - } - - protected emitEnumDeserializationAttributes(_e: EnumType) { - // Empty - } - protected emitEnumDefinition(e: EnumType, enumName: Name): void { this.emitFileHeader(enumName, []); this.emitDescription(this.descriptionForType(e)); @@ -805,34 +878,45 @@ export class PhpRenderer extends ConvenienceRenderer { this.ensureBlankLine(); } - this.emitBlockWithBraceOnNewLine( - ["public function __construct(", constructorProperties ? accessor + " " : "", "string $enum)"], - () => { + // + this.emitMethod({ + name: "__construct", + body: () => { if (!constructorProperties) { this.emitLine("$this->enum = $enum;"); } - } - ); + }, + args: [[constructorProperties ? accessor + " " : "", "string $enum"]], + docBlockArgs: ["string $enum"] + }); this.ensureBlankLine(); - this.emitEnumSerializationAttributes(e); - - this.emitDescriptionBlock(["@throws Exception", "@return string"]); - this.emitBlockWithBraceOnNewLine(["public function to(): string"], () => { - this.emitBlock(["if (in_array($this->enum, ", enumName, "::VALUES, true))"], () => { - this.emitLine("return $this->enum;"); - }); - this.emitLine("throw new Exception('the give value is not an enum-value.');"); + this.emitMethod({ + name: "to", + body: () => { + this.emitBlock(["if (in_array($this->enum, ", enumName, "::VALUES, true))"], () => { + this.emitLine("return $this->enum;"); + }); + this.emitLine("throw new Exception('the give value is not an enum-value.');"); + }, + returnType: "string", + throws: true }); - this.ensureBlankLine(); - this.emitEnumDeserializationAttributes(e); - this.emitDescriptionBlock(["@param mixed", "@throws Exception", ["@return ", enumName]]); - this.emitBlockWithBraceOnNewLine(["public static function from($obj): ", enumName], () => { - this.emitBlock(["if (in_array($obj, ", enumName, "::VALUES, true))"], () => { - this.emitLine("return new ", enumName, "($obj);"); - }); - this.emitLine("throw new Exception('Cannot deserialize ", enumName, "');"); + this.ensureBlankLine(); + this.emitMethod({ + name: "from", + body: () => { + this.emitBlock(["if (in_array($obj, ", enumName, "::VALUES, true))"], () => { + this.emitLine("return new ", enumName, "($obj);"); + }); + this.emitLine("throw new Exception('Cannot deserialize ", enumName, "');"); + }, + args: ["$obj"], + docBlockArgs: ["mixed $obj"], + returnType: enumName, + throws: true, + isStatic: true }); }); this.finishFile(); From 7261670e26a2bb37a5ebf17892b98ed22ffcff18 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 19:59:07 +0100 Subject: [PATCH 22/44] =?UTF-8?q?=E2=9C=A8=20Simpler=20"to"=20API=20+=20re?= =?UTF-8?q?move=20"validate"=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 244 +++----------------- 1 file changed, 38 insertions(+), 206 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index ee5495b1f..4bc0a2fc2 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -33,7 +33,6 @@ export enum SerializeWith { export const phpOptions = { withGet: new BooleanOption("with-get", "Create Getter", false), - fastGet: new BooleanOption("fast-get", "getter without validation", false), withSet: new BooleanOption("with-set", "Create Setter", false), withClosing: new BooleanOption("with-closing", "PHP Closing Tag", false), readonlyProperties: new BooleanOption("readonly-properties", "Use public readonly instead of protected", true), @@ -118,7 +117,6 @@ export function phpNameStyle( export interface FunctionNames { readonly getter: Name; readonly setter: Name; - readonly validate: Name; readonly from: Name; readonly to: Name; } @@ -195,11 +193,6 @@ export class PhpRenderer extends ConvenienceRenderer { name.order, lookup => `set_${lookup(name)}` ); - const validateName = new DependencyName( - this.getNameStyling("propertyNamingFunction"), - name.order, - lookup => `validate_${lookup(name)}` - ); const fromName = new DependencyName( this.getNameStyling("propertyNamingFunction"), name.order, @@ -213,7 +206,6 @@ export class PhpRenderer extends ConvenienceRenderer { return { getter: getterName, setter: setterName, - validate: validateName, from: fromName, to: toName }; @@ -231,7 +223,6 @@ export class PhpRenderer extends ConvenienceRenderer { return [ getterAndSetterNames.getter, getterAndSetterNames.setter, - getterAndSetterNames.validate, getterAndSetterNames.to, getterAndSetterNames.from ]; @@ -357,51 +348,47 @@ export class PhpRenderer extends ConvenienceRenderer { ); } - protected phpToObjConvert(t: Type, lhs: Sourcelike[], args: Sourcelike[]) { - return matchType( + protected phpToObjConvert(t: Type, lhs: Sourcelike[], args: Sourcelike[]): Sourcelike { + return matchType( t, - _anyType => this.emitLine(...lhs, ...args, ";"), - _nullType => this.emitLine(...lhs, ...args, ";"), - _boolType => this.emitLine(...lhs, ...args, ";"), - _integerType => this.emitLine(...lhs, ...args, ";"), - _doubleType => this.emitLine(...lhs, ...args, ";"), - _stringType => this.emitLine(...lhs, ...args, ";"), + _anyType => [...lhs, ...args], + _nullType => [...lhs, ...args], + _boolType => [...lhs, ...args], + _integerType => [...lhs, ...args], + _doubleType => [...lhs, ...args], + _stringType => [...lhs, ...args], arrayType => { - this.emitLine(...lhs, "array_map(function ($value) {"); - this.indent(() => { - this.phpToObjConvert(arrayType.items, ["return "], ["$value"]); - }); - this.emitLine("}, ", ...args, ");"); + const to = this.phpToObjConvert(arrayType.items, ["return "], ["$value"]); + + if (this.sourcelikeToString(to) === "return $value") { + return [...lhs, ...args]; + } + + const type = this.phpType(arrayType.items); + + return [...lhs, "array_map(function (", type, " $value) {\n ", to, ";\n}, ", ...args, ")"]; }, - _classType => this.emitLine(...lhs, ...args, "->to();"), - mapType => { - // TODO: this._options.serializeWith === SerializeWith.stdClass - this.emitBlockWithBraceOnNewLine(["function to($my): stdClass"], () => { - this.emitLine("$out = new stdClass();"); - this.emitBlock(["foreach ($my as $k => $v)"], () => { - this.phpToObjConvert(mapType.values, ["$my->$k = "], ["$v"]); - }); - this.emitLine("return $out;"); - }); - this.emitLine("return to(", ...args, ");"); + _classType => [...lhs, ...args, "->to()"], + _mapType => { + throw Error("maps are not supported"); }, - _enumType => this.emitLine(...lhs, ...args, "->value;"), + _enumType => [...lhs, ...args, "->value"], unionType => { const nullable = nullableFromUnion(unionType); if (nullable !== null) { - this.emitLine("if (!is_null(", ...args, ")) {"); - this.indent(() => this.phpToObjConvert(nullable, lhs, args)); - this.emitLine("} else {"); - this.indent(() => this.emitLine(...lhs, " null;")); - this.emitLine("}"); - return; + const to = this.phpToObjConvert(nullable, lhs, args); + + if (this.sourcelikeToString(to) === this.sourcelikeToString(args)) { + return to; + } + + return [...args, " === null ? null : ", to]; } throw Error("union are not supported"); }, transformedStringType => { if (transformedStringType.kind === "date-time") { - this.emitLine(...lhs, ...args, "->format(DateTimeInterface::ISO8601);"); - return; + return [...lhs, ...args, "->format(DateTimeInterface::ISO8601)"]; } throw Error('transformedStringType.kind === "unknown"'); } @@ -581,39 +568,8 @@ export class PhpRenderer extends ConvenienceRenderer { }); } - protected emitToMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { - this.emitMethod({ - name: names.to, - body: () => { - this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { - this.phpToObjConvert(p.type, ["return "], ["$this->", name]); - }); - this.emitLine("throw new Exception('never get to this ", className, "::", name, "');"); - }, - desc, - returnType: p.type, - throws: true - }); - } - - protected emitValidateMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { - this.emitMethod({ - name: names.validate, - body: () => { - this.phpValidate(className, p.type, name, "$value"); - this.emitLine("return true;"); - }, - desc, - args: [[this.phpType(p.type), " $value"]], - docBlockArgs: [[this.phpDocType(p.type), " $value"]], - returnType: "bool", - throws: true, - isStatic: true - }); - } - - protected emitGetMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { - const { withGet, fastGet } = this._options; + protected emitGetMethod(names: FunctionNames, p: ClassProperty, _className: Name, name: Name, desc?: string[]) { + const { withGet } = this._options; if (!withGet) { return; @@ -622,14 +578,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitMethod({ name: names.getter, body: () => { - if (fastGet) { - return this.emitLine("return $this->", name, ";"); - } - - this.emitBlock(["if (", className, "::", names.validate, "($this->", name, "))"], () => { - this.emitLine("return $this->", name, ";"); - }); - this.emitLine("throw new Exception('never get to ", names.getter, " ", className, "::", name, "');"); + this.emitLine("return $this->", name, ";"); }, desc, returnType: p.type, @@ -637,7 +586,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); } - protected emitSetMethod(names: FunctionNames, p: ClassProperty, className: Name, name: Name, desc?: string[]) { + protected emitSetMethod(names: FunctionNames, p: ClassProperty, _className: Name, name: Name, desc?: string[]) { if (!this._options.withSet) { return; } @@ -645,9 +594,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitMethod({ name: names.setter, body: () => { - this.emitBlock(["if (", className, "::", names.validate, "($value))"], () => { - this.emitLine("$this->", name, " = $value;"); - }); + this.emitLine("$this->", name, " = $value;"); }, desc, args: [[this.phpType(p.type), " $value"]], @@ -700,50 +647,11 @@ export class PhpRenderer extends ConvenienceRenderer { this.ensureBlankLine(); this.emitFromMethod(names, p, className, name, desc); this.ensureBlankLine(); - this.emitToMethod(names, p, className, name, desc); - this.ensureBlankLine(); - this.emitValidateMethod(names, p, className, name, desc); - this.ensureBlankLine(); this.emitGetMethod(names, p, className, name, desc); this.ensureBlankLine(); this.emitSetMethod(names, p, className, name, desc); }); - this.ensureBlankLine(); - this.emitMethod({ - name: "validate", - body: () => { - this.forEachClassProperty(c, "none", (name, _jsonName, _p, position) => { - const isFirstLine = ["first", "only"].includes(position); - const isLastLine = ["last", "only"].includes(position); - const prefix = isFirstLine ? "return " : "|| "; - const suffix = isLastLine ? ";" : ""; - - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - const line: Sourcelike = [ - prefix, - className, - "::", - names.validate, - "($this->", - name, - ")", - suffix - ]; - - if (isFirstLine) { - this.emitLine(line); - } else { - this.indent(() => { - this.emitLine(line); - }); - } - }); - }, - returnType: "bool", - throws: true - }); - const { serializeWith } = this._options; const useStdClass = serializeWith === SerializeWith.stdClass; @@ -768,9 +676,9 @@ export class PhpRenderer extends ConvenienceRenderer { body: () => { this.emitLine("return ["); this.indent(() => { - this.forEachClassProperty(c, "none", (name, jsonName) => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine("'", jsonName, "' => $this->", names.to, "(),"); + this.forEachClassProperty(c, "none", (name, jsonName, p) => { + const to = this.phpToObjConvert(p.type, [], ["$this->", name]); + this.emitLine("'", jsonName, "' => ", to, ","); }); }); this.emitLine("];"); @@ -786,12 +694,6 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitMethod({ name: "from", body: () => { - if (this._options.fastGet) { - this.forEachClassProperty(c, "none", name => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); - }); - } this.emitLine("return new ", className, "("); this.indent(() => { this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { @@ -811,12 +713,6 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitMethod({ name: "from", body: () => { - if (this._options.fastGet) { - this.forEachClassProperty(c, "none", name => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.validate, "($this->", name, ", true);"); - }); - } this.emitLine("return new ", className, "("); this.indent(() => { this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { @@ -855,71 +751,7 @@ export class PhpRenderer extends ConvenienceRenderer { return this.finishFile(); } - this.emitBlockWithBraceOnNewLine(["class ", enumName], () => { - const { constructorProperties, readonlyProperties } = this._options; - const accessor = readonlyProperties ? "public readonly" : "protected"; - - this.forEachEnumCase(e, "none", (name, jsonName) => { - this.emitLine("public const ", name, " = '", jsonName, "';"); - }); - this.ensureBlankLine(); - - this.emitLine("public const VALUES = ["); - this.indent(() => { - this.forEachEnumCase(e, "none", name => { - this.emitLine(enumName, "::", name, ","); - }); - }); - this.emitLine("];"); - this.ensureBlankLine(); - - if (!constructorProperties) { - this.emitLine(accessor, " string $enum;"); - this.ensureBlankLine(); - } - - // - this.emitMethod({ - name: "__construct", - body: () => { - if (!constructorProperties) { - this.emitLine("$this->enum = $enum;"); - } - }, - args: [[constructorProperties ? accessor + " " : "", "string $enum"]], - docBlockArgs: ["string $enum"] - }); - - this.ensureBlankLine(); - this.emitMethod({ - name: "to", - body: () => { - this.emitBlock(["if (in_array($this->enum, ", enumName, "::VALUES, true))"], () => { - this.emitLine("return $this->enum;"); - }); - this.emitLine("throw new Exception('the give value is not an enum-value.');"); - }, - returnType: "string", - throws: true - }); - - this.ensureBlankLine(); - this.emitMethod({ - name: "from", - body: () => { - this.emitBlock(["if (in_array($obj, ", enumName, "::VALUES, true))"], () => { - this.emitLine("return new ", enumName, "($obj);"); - }); - this.emitLine("throw new Exception('Cannot deserialize ", enumName, "');"); - }, - args: ["$obj"], - docBlockArgs: ["mixed $obj"], - returnType: enumName, - throws: true, - isStatic: true - }); - }); - this.finishFile(); + this.emitBlockWithBraceOnNewLine(["class ", enumName], () => {}); } protected emitSourceStructure(givenFilename: string): void { From acff64edade4bbd8a3ab70cfabadc01701f6d8d0 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 22:10:20 +0100 Subject: [PATCH 23/44] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20phpFromObjConvert=20?= =?UTF-8?q?method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 78 +++++++++++---------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 4bc0a2fc2..3d4c14af6 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -395,59 +395,61 @@ export class PhpRenderer extends ConvenienceRenderer { ); } - private transformDateTime(className: Name, attrName: Sourcelike, scopeAttrName: Sourcelike[]) { + private transformDateTime(className: Name, attrName: Sourcelike, scopeAttrName: Sourcelike[]): Sourcelike { this.emitBlock(["if (!is_a(", scopeAttrName, ", 'DateTime'))"], () => this.emitLine("throw new Exception('Attribute Error:", className, "::", attrName, "');") ); + throw Error("datetime is not supported"); } - protected phpFromObjConvert(className: Name, t: Type, lhs: Sourcelike[], args: Sourcelike[]) { - return matchType( + protected phpFromObjConvert(className: Name, t: Type, lhs: Sourcelike[], args: Sourcelike[]): Sourcelike { + return matchType( t, - _anyType => this.emitLine(...lhs, ...args, ";"), - _nullType => this.emitLine(...lhs, ...args, ";"), - _boolType => this.emitLine(...lhs, ...args, ";"), - _integerType => this.emitLine(...lhs, ...args, ";"), - _doubleType => this.emitLine(...lhs, ...args, ";"), - _stringType => this.emitLine(...lhs, ...args, ";"), + _anyType => [...lhs, ...args], + _nullType => [...lhs, ...args], + _boolType => [...lhs, ...args], + _integerType => [...lhs, ...args], + _doubleType => [...lhs, ...args], + _stringType => [...lhs, ...args], arrayType => { - this.emitLine(...lhs, "array_map(function ($value) {"); - this.indent(() => { - this.phpFromObjConvert(className, arrayType.items, ["return "], ["$value"]); - }); - this.emitLine("}, ", ...args, ");"); + const from = this.phpFromObjConvert(className, arrayType.items, ["return "], ["$value"]); + + if (this.sourcelikeToString(from) === "return $value") { + return [...lhs, ...args]; + } + + return [...lhs, "array_map(function ($value) {\n ", from, ";\n}, ", ...args, ")"]; }, - classType => this.emitLine(...lhs, this.nameForNamedType(classType), "::from(", ...args, ");"), - mapType => { - // TODO: this._options.serializeWith === SerializeWith.stdClass - this.emitBlockWithBraceOnNewLine(["function from($my): stdClass"], () => { - this.emitLine("$out = new stdClass();"); - this.emitBlock(["foreach ($my as $k => $v)"], () => { - this.phpFromObjConvert(className, mapType.values, ["$out->$k = "], ["$v"]); - }); - this.emitLine("return $out;"); - }); - this.emitLine("return from(", ...args, ");"); + classType => [...lhs, this.nameForNamedType(classType), "::from(", ...args, ")"], + _mapType => { + throw Error("maps are not supported"); }, - enumType => this.emitLine(...lhs, this.nameForNamedType(enumType), "::from(", ...args, ");"), + enumType => [...lhs, this.nameForNamedType(enumType), "::from(", ...args, ")"], unionType => { const nullable = nullableFromUnion(unionType); if (nullable !== null) { - this.emitLine("if (!is_null(", ...args, ")) {"); - this.indent(() => this.phpFromObjConvert(className, nullable, lhs, args)); - this.emitLine("} else {"); - this.indent(() => this.emitLine("return null;")); - this.emitLine("}"); - return; + const from = this.phpFromObjConvert(className, nullable, [], args); + + if (this.sourcelikeToString(from) === this.sourcelikeToString(args)) { + return from; + } + + return [...lhs, ...args, " === null ? null : ", from]; } throw Error("union are not supported"); }, transformedStringType => { if (transformedStringType.kind === "date-time") { - this.emitLine("$tmp = ", "DateTime::createFromFormat(DateTimeInterface::ISO8601, ", args, ");"); - this.transformDateTime(className, "", ["$tmp"]); - this.emitLine("return $tmp;"); - return; + return [ + "$tmp = DateTime::createFromFormat(DateTimeInterface::ISO8601, ", + args, + ");", + className, + "", + ["$tmp"], + this.transformDateTime(className, "", ["$tmp"]), + "return $tmp;" + ]; } throw Error('transformedStringType.kind === "unknown"'); } @@ -557,7 +559,9 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitMethod({ name: names.from, body: () => { - this.phpFromObjConvert(className, p.type, ["return "], ["$value"]); + const from = this.phpFromObjConvert(className, p.type, ["return "], ["$value"]); + + this.emitLine(from, ";"); }, desc, args: [[this.phpType(p.type), " $value"]], From ff3596949f44a55b50c677f023ea5d703f5293f3 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 22:26:23 +0100 Subject: [PATCH 24/44] =?UTF-8?q?=E2=9C=A8=20Arrow=20Functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 3d4c14af6..d51cffd61 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -37,6 +37,7 @@ export const phpOptions = { withClosing: new BooleanOption("with-closing", "PHP Closing Tag", false), readonlyProperties: new BooleanOption("readonly-properties", "Use public readonly instead of protected", true), nativeEnums: new BooleanOption("native-enums", "Use enums instead of enum classes", true), + arrowFunctions: new BooleanOption("arrow-functions", "Use arrow functions whenever possible", true), constructorProperties: new BooleanOption( "constructor-properties", "Declare class properties inside constructor", @@ -358,6 +359,7 @@ export class PhpRenderer extends ConvenienceRenderer { _doubleType => [...lhs, ...args], _stringType => [...lhs, ...args], arrayType => { + const { arrowFunctions } = this._options; const to = this.phpToObjConvert(arrayType.items, ["return "], ["$value"]); if (this.sourcelikeToString(to) === "return $value") { @@ -366,6 +368,11 @@ export class PhpRenderer extends ConvenienceRenderer { const type = this.phpType(arrayType.items); + if (arrowFunctions) { + const to = this.phpToObjConvert(arrayType.items, [], ["$value"]); + return [...lhs, "array_map(fn(", type, " $value) => ", to, ", ", ...args, ")"]; + } + return [...lhs, "array_map(function (", type, " $value) {\n ", to, ";\n}, ", ...args, ")"]; }, _classType => [...lhs, ...args, "->to()"], @@ -412,12 +419,18 @@ export class PhpRenderer extends ConvenienceRenderer { _doubleType => [...lhs, ...args], _stringType => [...lhs, ...args], arrayType => { + const { arrowFunctions } = this._options; const from = this.phpFromObjConvert(className, arrayType.items, ["return "], ["$value"]); if (this.sourcelikeToString(from) === "return $value") { return [...lhs, ...args]; } + if (arrowFunctions) { + const from = this.phpFromObjConvert(className, arrayType.items, [], ["$value"]); + return [...lhs, "array_map(fn($value) => ", from, ", ", ...args, ")"]; + } + return [...lhs, "array_map(function ($value) {\n ", from, ";\n}, ", ...args, ")"]; }, classType => [...lhs, this.nameForNamedType(classType), "::from(", ...args, ")"], From 29522542ce51f8a585fe9aa4cfe8f254f660da65 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 22:42:46 +0100 Subject: [PATCH 25/44] =?UTF-8?q?=E2=9C=A8=20Callables?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 28 ++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index d51cffd61..8fc35e47e 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -38,6 +38,7 @@ export const phpOptions = { readonlyProperties: new BooleanOption("readonly-properties", "Use public readonly instead of protected", true), nativeEnums: new BooleanOption("native-enums", "Use enums instead of enum classes", true), arrowFunctions: new BooleanOption("arrow-functions", "Use arrow functions whenever possible", true), + callable: new BooleanOption("callable", "Use callable syntax whenever possible", true), constructorProperties: new BooleanOption( "constructor-properties", "Declare class properties inside constructor", @@ -419,7 +420,7 @@ export class PhpRenderer extends ConvenienceRenderer { _doubleType => [...lhs, ...args], _stringType => [...lhs, ...args], arrayType => { - const { arrowFunctions } = this._options; + const { arrowFunctions, callable } = this._options; const from = this.phpFromObjConvert(className, arrayType.items, ["return "], ["$value"]); if (this.sourcelikeToString(from) === "return $value") { @@ -427,17 +428,38 @@ export class PhpRenderer extends ConvenienceRenderer { } if (arrowFunctions) { + if (callable && ["class", "enum"].includes(arrayType.items.kind)) { + const from = this.phpFromObjConvert(className, arrayType.items, [], []); + return [...lhs, "array_map(", from, ", ", ...args, ")"]; + } + const from = this.phpFromObjConvert(className, arrayType.items, [], ["$value"]); return [...lhs, "array_map(fn($value) => ", from, ", ", ...args, ")"]; } return [...lhs, "array_map(function ($value) {\n ", from, ";\n}, ", ...args, ")"]; }, - classType => [...lhs, this.nameForNamedType(classType), "::from(", ...args, ")"], + classType => { + const { callable } = this._options; + + if (callable && args.length === 0) { + return ["[", this.nameForNamedType(classType), "::class, 'from']"]; + } + + return [...lhs, this.nameForNamedType(classType), "::from(", ...args, ")"]; + }, _mapType => { throw Error("maps are not supported"); }, - enumType => [...lhs, this.nameForNamedType(enumType), "::from(", ...args, ")"], + enumType => { + const { callable } = this._options; + + if (callable && args.length === 0) { + return ["[", this.nameForNamedType(enumType), "::class, 'from']"]; + } + + return [...lhs, this.nameForNamedType(enumType), "::from(", ...args, ")"]; + }, unionType => { const nullable = nullableFromUnion(unionType); if (nullable !== null) { From 57d2cf128bfbd932e30b4a6f76353b299b1bbf96 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 22:47:59 +0100 Subject: [PATCH 26/44] =?UTF-8?q?=E2=9C=A8=20First=20Callable=20Syntax?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 8fc35e47e..44daf172a 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -39,6 +39,7 @@ export const phpOptions = { nativeEnums: new BooleanOption("native-enums", "Use enums instead of enum classes", true), arrowFunctions: new BooleanOption("arrow-functions", "Use arrow functions whenever possible", true), callable: new BooleanOption("callable", "Use callable syntax whenever possible", true), + firstCallable: new BooleanOption("first-callable", "Use first callable syntax whenever possible", true), constructorProperties: new BooleanOption( "constructor-properties", "Declare class properties inside constructor", @@ -440,7 +441,11 @@ export class PhpRenderer extends ConvenienceRenderer { return [...lhs, "array_map(function ($value) {\n ", from, ";\n}, ", ...args, ")"]; }, classType => { - const { callable } = this._options; + const { callable, firstCallable } = this._options; + + if (firstCallable && args.length === 0) { + return [this.nameForNamedType(classType), "::from(...)"]; + } if (callable && args.length === 0) { return ["[", this.nameForNamedType(classType), "::class, 'from']"]; @@ -452,7 +457,11 @@ export class PhpRenderer extends ConvenienceRenderer { throw Error("maps are not supported"); }, enumType => { - const { callable } = this._options; + const { callable, firstCallable } = this._options; + + if (firstCallable && args.length === 0) { + return [this.nameForNamedType(enumType), "::from(...)"]; + } if (callable && args.length === 0) { return ["[", this.nameForNamedType(enumType), "::class, 'from']"]; From 6547a5a9f89c495abcbe35a6c4601b25584d86cc Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 22:52:01 +0100 Subject: [PATCH 27/44] =?UTF-8?q?=F0=9F=90=9B=20Mini=20Fix=20Bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 44daf172a..ff8e30fe4 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -475,7 +475,7 @@ export class PhpRenderer extends ConvenienceRenderer { const from = this.phpFromObjConvert(className, nullable, [], args); if (this.sourcelikeToString(from) === this.sourcelikeToString(args)) { - return from; + return [...lhs, from]; } return [...lhs, ...args, " === null ? null : ", from]; From fa1889087e46c983803408660eae070936069d5b Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 22:54:37 +0100 Subject: [PATCH 28/44] =?UTF-8?q?=F0=9F=97=91=20@throws=20docblock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index ff8e30fe4..584d645cf 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -132,7 +132,6 @@ type Method = { returnType?: Type | Sourcelike; docBlockArgs?: Sourcelike[]; docBlockReturnType?: Type | Sourcelike; - throws?: boolean; isStatic?: boolean; isProtected?: boolean; }; @@ -564,7 +563,6 @@ export class PhpRenderer extends ConvenienceRenderer { returnType = "void", docBlockArgs = args, docBlockReturnType = returnType, - throws = false, isStatic = false, isProtected = false } = method; @@ -576,9 +574,6 @@ export class PhpRenderer extends ConvenienceRenderer { for (const docBlockArg of docBlockArgs) { docBlock.push(["@param ", docBlockArg]); } - if (throws) { - docBlock.push(["@throws Exception"]); - } docBlock.push([ "@return ", docBlockReturnType instanceof Type ? this.phpDocType(docBlockReturnType) : docBlockReturnType @@ -611,7 +606,6 @@ export class PhpRenderer extends ConvenienceRenderer { args: [[this.phpType(p.type), " $value"]], returnType: p.type, docBlockArgs: [[p.type.kind === "array" ? "mixed[]" : this.phpDocType(p.type), " $value"]], - throws: true, isStatic: true }); } @@ -629,8 +623,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine("return $this->", name, ";"); }, desc, - returnType: p.type, - throws: true + returnType: p.type }); } @@ -647,8 +640,7 @@ export class PhpRenderer extends ConvenienceRenderer { desc, args: [[this.phpType(p.type), " $value"]], docBlockArgs: [[this.phpDocType(p.type), " $value"]], - returnType: "void", - throws: true + returnType: "void" }); } @@ -715,8 +707,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine("return $out;"); }, - returnType: "stdClass", - throws: true + returnType: "stdClass" }); } else { this.emitMethod({ @@ -732,8 +723,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine("];"); }, returnType: "array", - docBlockReturnType: "mixed[]", - throws: true + docBlockReturnType: "mixed[]" }); } @@ -754,7 +744,6 @@ export class PhpRenderer extends ConvenienceRenderer { }, args: ["stdClass $obj"], returnType: className, - throws: true, isStatic: true }); } else { @@ -774,7 +763,6 @@ export class PhpRenderer extends ConvenienceRenderer { args: ["array $arr"], docBlockArgs: ["mixed[] $arr"], returnType: className, - throws: true, isStatic: true }); } From 92b2ba26ce307280f9ee81de6b5bc8fe234ed92f Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 23:05:18 +0100 Subject: [PATCH 29/44] =?UTF-8?q?=E2=9C=A8=20Inline=20From=20Methods=20&?= =?UTF-8?q?=20Remove=20from=20/=20to=20property=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 62 +++++---------------- 1 file changed, 14 insertions(+), 48 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 584d645cf..041a99755 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -120,8 +120,6 @@ export function phpNameStyle( export interface FunctionNames { readonly getter: Name; readonly setter: Name; - readonly from: Name; - readonly to: Name; } type Method = { @@ -195,21 +193,10 @@ export class PhpRenderer extends ConvenienceRenderer { name.order, lookup => `set_${lookup(name)}` ); - const fromName = new DependencyName( - this.getNameStyling("propertyNamingFunction"), - name.order, - lookup => `from_${lookup(name)}` - ); - const toName = new DependencyName( - this.getNameStyling("propertyNamingFunction"), - name.order, - lookup => `to_${lookup(name)}` - ); + return { getter: getterName, - setter: setterName, - from: fromName, - to: toName + setter: setterName }; } @@ -222,12 +209,7 @@ export class PhpRenderer extends ConvenienceRenderer { ): Name[] { const getterAndSetterNames = this.makeNamesForPropertyGetterAndSetter(c, className, p, jsonName, name); this._gettersAndSettersForPropertyName.set(name, getterAndSetterNames); - return [ - getterAndSetterNames.getter, - getterAndSetterNames.setter, - getterAndSetterNames.to, - getterAndSetterNames.from - ]; + return [getterAndSetterNames.getter, getterAndSetterNames.setter]; } private getNameStyling(convention: string): Namer { @@ -594,22 +576,6 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitBlockWithBraceOnNewLine(line, body); } - protected emitFromMethod(names: FunctionNames, p: ClassProperty, className: Name, _name: Name, desc?: string[]) { - this.emitMethod({ - name: names.from, - body: () => { - const from = this.phpFromObjConvert(className, p.type, ["return "], ["$value"]); - - this.emitLine(from, ";"); - }, - desc, - args: [[this.phpType(p.type), " $value"]], - returnType: p.type, - docBlockArgs: [[p.type.kind === "array" ? "mixed[]" : this.phpDocType(p.type), " $value"]], - isStatic: true - }); - } - protected emitGetMethod(names: FunctionNames, p: ClassProperty, _className: Name, name: Name, desc?: string[]) { const { withGet } = this._options; @@ -684,8 +650,6 @@ export class PhpRenderer extends ConvenienceRenderer { const desc = this.descriptionForClassProperty(c, jsonName); const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.ensureBlankLine(); - this.emitFromMethod(names, p, className, name, desc); this.ensureBlankLine(); this.emitGetMethod(names, p, className, name, desc); this.ensureBlankLine(); @@ -701,9 +665,9 @@ export class PhpRenderer extends ConvenienceRenderer { name: "to", body: () => { this.emitLine("$out = new stdClass();"); - this.forEachClassProperty(c, "none", (name, jsonName) => { - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine("$out->", jsonName, " = $this->", names.to, "();"); + this.forEachClassProperty(c, "none", (name, jsonName, p) => { + const to = this.phpToObjConvert(p.type, [], ["$this->", name]); + this.emitLine("$out->", jsonName, " = $this->", to, ";"); }); this.emitLine("return $out;"); }, @@ -734,10 +698,11 @@ export class PhpRenderer extends ConvenienceRenderer { body: () => { this.emitLine("return new ", className, "("); this.indent(() => { - this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { + this.forEachClassProperty(c, "none", (_name, jsonName, p, position) => { const suffix = ["last", "only"].includes(position) ? "" : ","; - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.from, "($obj->", jsonName, ")", suffix); + const from = this.phpFromObjConvert(className, p.type, [], ["$obj->", jsonName]); + + this.emitLine(from, suffix); }); }); this.emitLine(");"); @@ -752,10 +717,11 @@ export class PhpRenderer extends ConvenienceRenderer { body: () => { this.emitLine("return new ", className, "("); this.indent(() => { - this.forEachClassProperty(c, "none", (name, jsonName, _, position) => { + this.forEachClassProperty(c, "none", (_name, jsonName, p, position) => { const suffix = ["last", "only"].includes(position) ? "" : ","; - const names = defined(this._gettersAndSettersForPropertyName.get(name)); - this.emitLine(className, "::", names.from, "($arr['", jsonName, "'])", suffix); + const from = this.phpFromObjConvert(className, p.type, [], ["$arr['", jsonName, "']"]); + + this.emitLine(from, suffix); }); }); this.emitLine(");"); From 070416239bf71b3f2376493fd8178d031bf0b070 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 3 Nov 2023 23:38:55 +0100 Subject: [PATCH 30/44] =?UTF-8?q?=E2=9C=A8=20self=20&=20static?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 27 ++++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 041a99755..a36709138 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -31,6 +31,8 @@ export enum SerializeWith { stdClass = "stdClass" } +export type SelfNameType = "default" | "self" | "static"; + export const phpOptions = { withGet: new BooleanOption("with-get", "Create Getter", false), withSet: new BooleanOption("with-set", "Create Setter", false), @@ -40,6 +42,7 @@ export const phpOptions = { arrowFunctions: new BooleanOption("arrow-functions", "Use arrow functions whenever possible", true), callable: new BooleanOption("callable", "Use callable syntax whenever possible", true), firstCallable: new BooleanOption("first-callable", "Use first callable syntax whenever possible", true), + staticTypeAnnotation: new BooleanOption("static-type-annotation", "Use static type hinting on methods", true), constructorProperties: new BooleanOption( "constructor-properties", "Declare class properties inside constructor", @@ -54,6 +57,16 @@ export const phpOptions = { ], "array" ), + selfNameType: new EnumOption( + "self-name-type", + "How to refer to a class from inside itself", + [ + ["default", "default"], + ["self", "self"], + ["static", "static"] + ], + "static" + ), acronymStyle: acronymOption(AcronymStyleOptions.Pascal) }; @@ -691,12 +704,16 @@ export class PhpRenderer extends ConvenienceRenderer { }); } + const { selfNameType, staticTypeAnnotation } = this._options; + const self = selfNameType === "default" ? className : selfNameType; + const returnType = staticTypeAnnotation ? "static" : selfNameType === "static" ? "self" : self; + this.ensureBlankLine(); if (useStdClass) { this.emitMethod({ name: "from", body: () => { - this.emitLine("return new ", className, "("); + this.emitLine("return new ", self, "("); this.indent(() => { this.forEachClassProperty(c, "none", (_name, jsonName, p, position) => { const suffix = ["last", "only"].includes(position) ? "" : ","; @@ -708,14 +725,15 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine(");"); }, args: ["stdClass $obj"], - returnType: className, + returnType, + docBlockReturnType: self, isStatic: true }); } else { this.emitMethod({ name: "from", body: () => { - this.emitLine("return new ", className, "("); + this.emitLine("return new ", self, "("); this.indent(() => { this.forEachClassProperty(c, "none", (_name, jsonName, p, position) => { const suffix = ["last", "only"].includes(position) ? "" : ","; @@ -728,7 +746,8 @@ export class PhpRenderer extends ConvenienceRenderer { }, args: ["array $arr"], docBlockArgs: ["mixed[] $arr"], - returnType: className, + returnType, + docBlockReturnType: self, isStatic: true }); } From 9b3c0aa877ca56354ec9ca5b0d14bcb38adca361 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sat, 4 Nov 2023 00:14:22 +0100 Subject: [PATCH 31/44] =?UTF-8?q?=E2=9C=A8=20Validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 180 ++++++++++++++------ 1 file changed, 125 insertions(+), 55 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index a36709138..78823cb27 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -494,61 +494,6 @@ export class PhpRenderer extends ConvenienceRenderer { ); } - private phpValidate(className: Name, t: Type, attrName: Sourcelike, scopeAttrName: string) { - const is = (isfn: string, myT: Name = className) => { - this.emitBlock(["if (!", isfn, "(", scopeAttrName, "))"], () => - this.emitLine("throw new Exception('Attribute Error:", myT, "::", attrName, "');") - ); - }; - return matchType( - t, - _anyType => is("defined"), - _nullType => is("is_null"), - _boolType => is("is_bool"), - _integerType => is("is_integer"), - _doubleType => is("is_float"), - _stringType => is("is_string"), - arrayType => { - is("is_array"); - this.emitLine("array_walk(", scopeAttrName, ", function (", scopeAttrName, "_v) {"); - this.indent(() => { - this.phpValidate(className, arrayType.items, attrName, `${scopeAttrName}_v`); - }); - this.emitLine("});"); - }, - _classType => { - this.emitLine(scopeAttrName, "->validate();"); - }, - mapType => { - this.emitLine("foreach (", scopeAttrName, " as $k => $v) {"); - this.indent(() => { - this.phpValidate(className, mapType.values, attrName, "$v"); - }); - this.emitLine("}"); - }, - _enumType => { - this.emitLine(scopeAttrName, "->value;"); - }, - unionType => { - const nullable = nullableFromUnion(unionType); - if (nullable !== null) { - this.emitBlock(["if (!is_null(", scopeAttrName, "))"], () => { - this.phpValidate(className, nullable, attrName, scopeAttrName); - }); - return; - } - throw Error("not implemented"); - }, - transformedStringType => { - if (transformedStringType.kind === "date-time") { - this.transformDateTime(className, attrName, [scopeAttrName]); - return; - } - throw Error(`transformedStringType.kind === ${transformedStringType.kind}`); - } - ); - } - protected emitMethod(method: Method): void { const { name, @@ -713,6 +658,9 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitMethod({ name: "from", body: () => { + this.emitAsserts(c, className); + this.ensureBlankLine(); + this.emitLine("return new ", self, "("); this.indent(() => { this.forEachClassProperty(c, "none", (_name, jsonName, p, position) => { @@ -733,6 +681,9 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitMethod({ name: "from", body: () => { + this.emitAsserts(c, className); + this.ensureBlankLine(); + this.emitLine("return new ", self, "("); this.indent(() => { this.forEachClassProperty(c, "none", (_name, jsonName, p, position) => { @@ -755,6 +706,125 @@ export class PhpRenderer extends ConvenienceRenderer { this.finishFile(); } + protected emitAsserts(classType: ClassType, className: Name): void { + const { selfNameType } = this._options; + const self = selfNameType === "default" ? className : selfNameType; + + this.emitLine("assert(is_array($arr), ", self, "::class . '::from expects array');"); + + this.forEachClassProperty(classType, "none", (name, jsonName, classProperty) => { + this.emitTypeAsserts(name, jsonName, className, classProperty.type, !classProperty.isOptional); + }); + } + + protected emitTypeAsserts( + name: Name, + jsonName: string, + className: Name, + type: Type, + required: boolean = true, + isNullable: boolean = false + ): void { + const { selfNameType } = this._options; + const self = selfNameType === "default" ? className : selfNameType; + + const emitAssert = ( + func: Sourcelike, + expected: string, + value: Sourcelike = ["$arr['", jsonName, "']", required ? "" : " ?? null"] + ) => { + const line: Sourcelike[] = []; + + line.push("assert(", func, "(", value, ")"); + if (isNullable) { + line.push(" || is_null(", value, ")"); + } + line.push(", ", self, "::class . '.", name, " must be "); + + if (isNullable) { + line.push("either ", expected, " or null');"); + } else { + line.push(expected, "');"); + } + + this.emitLine(...line); + }; + + const emitRequired = () => { + if (!required) { + return; + } + + this.emitLine( + "assert(array_key_exists('", + jsonName, + "', $arr), ", + self, + "::class . '.", + name, + " is required');" + ); + }; + + matchType( + type, + _anyType => { + emitRequired(); + }, + _nullType => { + emitRequired(); + emitAssert("is_null", "null"); + }, + _boolType => { + emitRequired(); + emitAssert("is_bool", "boolean"); + }, + _integerType => { + emitRequired(); + emitAssert("is_int", "integer"); + }, + _doubleType => { + emitRequired(); + emitAssert("is_numeric", "float"); + }, + _stringType => { + emitRequired(); + emitAssert("is_string", "a string"); + }, + arrayType => { + emitRequired(); + emitAssert("is_array", "an array"); + + if (arrayType.items.kind === "enum") { + this.emitBlock(["foreach ($arr['", jsonName, "'] as $el)"], () => { + emitAssert("is_string", "an array of strings", "$el"); + }); + } + }, + _classType => { + emitRequired(); + }, + _mapType => {}, + enumType => { + const enumName = this.nameForNamedType(enumType); + + emitRequired(); + emitAssert("is_string", "a string"); + emitAssert(["!!", enumName, "::tryFrom"], "a valid enum option"); + }, + unionType => { + const nullable = nullableFromUnion(unionType); + + if (nullable !== null) { + return this.emitTypeAsserts(name, jsonName, className, nullable, required, true); + } + + emitRequired(); + }, + _transformedStringType => {} + ); + } + protected emitUnionDefinition(_u: UnionType, _unionName: Name): void { throw Error("emitUnionDefinition not implemented"); } From c4e8ebb6f7d995b494c97f894aecbb8317a1c20b Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sat, 4 Nov 2023 00:14:53 +0100 Subject: [PATCH 32/44] =?UTF-8?q?=F0=9F=90=9B=20Fix=20Warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 78823cb27..7670bf030 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -688,7 +688,13 @@ export class PhpRenderer extends ConvenienceRenderer { this.indent(() => { this.forEachClassProperty(c, "none", (_name, jsonName, p, position) => { const suffix = ["last", "only"].includes(position) ? "" : ","; - const from = this.phpFromObjConvert(className, p.type, [], ["$arr['", jsonName, "']"]); + + const from = this.phpFromObjConvert( + className, + p.type, + [], + ["$arr['", jsonName, "']", p.isOptional ? " ?? null" : ""] + ); this.emitLine(from, suffix); }); From 63f0b92b79e85fe3e512f011c60a8254eae2a7b4 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sat, 4 Nov 2023 00:18:05 +0100 Subject: [PATCH 33/44] =?UTF-8?q?=F0=9F=90=9B=20Fix=20mixed=20typehint=20f?= =?UTF-8?q?or=20from=20method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 7670bf030..4dd7917e3 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -672,7 +672,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine(");"); }, - args: ["stdClass $obj"], + args: ["mixed $obj"], returnType, docBlockReturnType: self, isStatic: true @@ -701,8 +701,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine(");"); }, - args: ["array $arr"], - docBlockArgs: ["mixed[] $arr"], + args: ["mixed $arr"], returnType, docBlockReturnType: self, isStatic: true From 3c3a44bfd1815cec25685029597d867a198d6b1d Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sat, 4 Nov 2023 09:53:51 +0100 Subject: [PATCH 34/44] =?UTF-8?q?=E2=9C=A8=20Mixed=20Type=20Annotation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 4dd7917e3..fab0b4448 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -43,6 +43,7 @@ export const phpOptions = { callable: new BooleanOption("callable", "Use callable syntax whenever possible", true), firstCallable: new BooleanOption("first-callable", "Use first callable syntax whenever possible", true), staticTypeAnnotation: new BooleanOption("static-type-annotation", "Use static type hinting on methods", true), + mixedTypeAnnotation: new BooleanOption("mixed-type-annotation", "Use mixed type hinting on methods", true), constructorProperties: new BooleanOption( "constructor-properties", "Declare class properties inside constructor", @@ -281,8 +282,8 @@ export class PhpRenderer extends ConvenienceRenderer { } return matchType( t, - _anyType => maybeAnnotated(isOptional, anyTypeIssueAnnotation, "Object"), - _nullType => maybeAnnotated(isOptional, nullTypeIssueAnnotation, "Object"), + _anyType => maybeAnnotated(isOptional, anyTypeIssueAnnotation, "mixed"), + _nullType => maybeAnnotated(isOptional, nullTypeIssueAnnotation, "null"), _boolType => optionalize("bool"), _integerType => optionalize("int"), _doubleType => optionalize("float"), @@ -319,7 +320,7 @@ export class PhpRenderer extends ConvenienceRenderer { protected phpDocType(t: Type): Sourcelike { return matchType( t, - _anyType => "any", + _anyType => "mixed", _nullType => "null", _boolType => "bool", _integerType => "int", @@ -334,7 +335,7 @@ export class PhpRenderer extends ConvenienceRenderer { if (nullable !== null) { return ["?", this.phpDocType(nullable)]; } - throw Error("union are not supported"); + return "mixed"; }, transformedStringType => { if (transformedStringType.kind === "date-time") { @@ -649,7 +650,7 @@ export class PhpRenderer extends ConvenienceRenderer { }); } - const { selfNameType, staticTypeAnnotation } = this._options; + const { selfNameType, staticTypeAnnotation, mixedTypeAnnotation } = this._options; const self = selfNameType === "default" ? className : selfNameType; const returnType = staticTypeAnnotation ? "static" : selfNameType === "static" ? "self" : self; @@ -672,7 +673,8 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine(");"); }, - args: ["mixed $obj"], + args: mixedTypeAnnotation ? ["mixed $obj"] : ["$obj"], + docBlockArgs: ["mixed $obj"], returnType, docBlockReturnType: self, isStatic: true @@ -701,7 +703,8 @@ export class PhpRenderer extends ConvenienceRenderer { }); this.emitLine(");"); }, - args: ["mixed $arr"], + args: mixedTypeAnnotation ? ["mixed $arr"] : ["$arr"], + docBlockArgs: ["mixed $arr"], returnType, docBlockReturnType: self, isStatic: true From 51e6f14997f7bab985291c745fc9d134f2f44051 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sun, 5 Nov 2023 08:45:01 +0100 Subject: [PATCH 35/44] =?UTF-8?q?=E2=9C=A8=20PHP=20Version=20Option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 40 ++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index fab0b4448..ce6f8ee35 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -34,6 +34,18 @@ export enum SerializeWith { export type SelfNameType = "default" | "self" | "static"; export const phpOptions = { + phpVersion: new EnumOption( + "php-version", + "PHP Version to target", + [ + ["7.3", 7.3], + ["7.4", 7.4], + ["8.0", 8.0], + ["8.1", 8.1], + ["8.2", 8.2] + ], + "8.2" + ), withGet: new BooleanOption("with-get", "Create Getter", false), withSet: new BooleanOption("with-set", "Create Setter", false), withClosing: new BooleanOption("with-closing", "PHP Closing Tag", false), @@ -86,7 +98,7 @@ export class PhpTargetLanguage extends TargetLanguage { protected makeRenderer(renderContext: RenderContext, untypedOptionValues: { [name: string]: any }): PhpRenderer { const options = getOptionValues(phpOptions, untypedOptionValues); - return new PhpRenderer(this, renderContext, options); + return new PhpRenderer(this, renderContext, this.fixOptionsByPhpVersion(options)); } get stringTypeMapping(): StringTypeMapping { @@ -97,6 +109,32 @@ export class PhpTargetLanguage extends TargetLanguage { mapping.set("date-time", "date-time"); return mapping; } + + protected fixOptionsByPhpVersion(options: OptionValues): OptionValues { + const { phpVersion } = options; + + if (phpVersion < 8.2) { + // no features yet + } + + if (phpVersion < 8.1) { + options.nativeEnums = false; + options.readonlyProperties = false; + options.firstCallable = false; + } + + if (phpVersion < 8.0) { + options.constructorProperties = false; + options.mixedTypeAnnotation = false; + options.staticTypeAnnotation = false; + } + + if (phpVersion < 7.4) { + options.arrowFunctions = false; + } + + return options; + } } export const stringEscape = utf16ConcatMap(escapeNonPrintableMapper(isAscii, standardUnicodeHexEscape)); From b66e7a9bdb53c957fe3e53e447f7a9d183831726 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sun, 5 Nov 2023 08:59:19 +0100 Subject: [PATCH 36/44] =?UTF-8?q?=F0=9F=90=9B=20Emit=20enum=20body=20myste?= =?UTF-8?q?riously=20disappeared?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 61 ++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index ce6f8ee35..c5e31f387 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -888,7 +888,66 @@ export class PhpRenderer extends ConvenienceRenderer { return this.finishFile(); } - this.emitBlockWithBraceOnNewLine(["class ", enumName], () => {}); + this.emitBlockWithBraceOnNewLine(["class ", enumName], () => { + const { constructorProperties, readonlyProperties } = this._options; + const accessor = readonlyProperties ? "public readonly" : "protected"; + + this.forEachEnumCase(e, "none", (name, jsonName) => { + this.emitLine("public const ", name, " = '", jsonName, "';"); + }); + this.ensureBlankLine(); + + this.emitLine("public const VALUES = ["); + this.indent(() => { + this.forEachEnumCase(e, "none", name => { + this.emitLine(enumName, "::", name, ","); + }); + }); + this.emitLine("];"); + this.ensureBlankLine(); + + if (!constructorProperties) { + this.emitLine(accessor, " string $enum;"); + this.ensureBlankLine(); + } + + this.emitDescriptionBlock(["@param string $enum"]); + this.emitBlockWithBraceOnNewLine( + ["public function __construct(", constructorProperties ? accessor + " " : "", "string $enum)"], + () => { + if (!constructorProperties) { + this.emitLine("$this->enum = $enum;"); + } + } + ); + + this.ensureBlankLine(); + this.emitMethod({ + name: "to", + body: () => { + this.emitBlock(["if (in_array($this->enum, ", enumName, "::VALUES, true))"], () => { + this.emitLine("return $this->enum;"); + }); + this.emitLine("throw new Exception('the give value is not an enum-value.');"); + }, + returnType: "string" + }); + + this.ensureBlankLine(); + this.emitMethod({ + name: "from", + body: () => { + this.emitBlock(["if (in_array($obj, ", enumName, "::VALUES, true))"], () => { + this.emitLine("return new ", enumName, "($obj);"); + }); + this.emitLine("throw new Exception('Cannot deserialize ", enumName, "');"); + }, + args: ["$obj"], + docBlockArgs: ["mixed $obj"], + returnType: enumName, + isStatic: true + }); + }); } protected emitSourceStructure(givenFilename: string): void { From 0f2e486dceef0766344bebd0a1f53ca80c93b372 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sun, 5 Nov 2023 09:06:58 +0100 Subject: [PATCH 37/44] =?UTF-8?q?=F0=9F=90=9B=20Allow=20callable=20option?= =?UTF-8?q?=20without=20arrow=20function=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index c5e31f387..63dc7aff6 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -461,12 +461,12 @@ export class PhpRenderer extends ConvenienceRenderer { return [...lhs, ...args]; } - if (arrowFunctions) { - if (callable && ["class", "enum"].includes(arrayType.items.kind)) { - const from = this.phpFromObjConvert(className, arrayType.items, [], []); - return [...lhs, "array_map(", from, ", ", ...args, ")"]; - } + if (callable && ["class", "enum"].includes(arrayType.items.kind)) { + const from = this.phpFromObjConvert(className, arrayType.items, [], []); + return [...lhs, "array_map(", from, ", ", ...args, ")"]; + } + if (arrowFunctions) { const from = this.phpFromObjConvert(className, arrayType.items, [], ["$value"]); return [...lhs, "array_map(fn($value) => ", from, ", ", ...args, ")"]; } From aa179aed384332eb42c2cca3ad6591aeece91e84 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sun, 5 Nov 2023 11:00:40 +0100 Subject: [PATCH 38/44] =?UTF-8?q?=F0=9F=90=9B=20Fix=20Class=20Enums?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 63dc7aff6..7f1376dca 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -414,7 +414,7 @@ export class PhpRenderer extends ConvenienceRenderer { _mapType => { throw Error("maps are not supported"); }, - _enumType => [...lhs, ...args, "->value"], + _enumType => [...lhs, ...args, this._options.nativeEnums ? "->value" : "->to()"], unionType => { const nullable = nullableFromUnion(unionType); if (nullable !== null) { From fd56b1d4f30565b5c96853620a50b72271bdd804 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sun, 5 Nov 2023 11:07:37 +0100 Subject: [PATCH 39/44] =?UTF-8?q?=F0=9F=97=91=20Remove=20"with=20closing"?= =?UTF-8?q?=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 7f1376dca..aa142f282 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -48,7 +48,6 @@ export const phpOptions = { ), withGet: new BooleanOption("with-get", "Create Getter", false), withSet: new BooleanOption("with-set", "Create Setter", false), - withClosing: new BooleanOption("with-closing", "PHP Closing Tag", false), readonlyProperties: new BooleanOption("readonly-properties", "Use public readonly instead of protected", true), nativeEnums: new BooleanOption("native-enums", "Use enums instead of enum classes", true), arrowFunctions: new BooleanOption("arrow-functions", "Use arrow functions whenever possible", true), @@ -958,9 +957,7 @@ export class PhpRenderer extends ConvenienceRenderer { (e, n) => this.emitEnumDefinition(e, n), (u, n) => this.emitUnionDefinition(u, n) ); - if (this._options.withClosing) { - this.emitLine("?>"); - } + super.finishFile(defined(givenFilename)); } } From 9d3799bb06f8e3ee864e50f240517dcb500ea70b Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sun, 5 Nov 2023 11:31:57 +0100 Subject: [PATCH 40/44] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Conform=20Enum=20cla?= =?UTF-8?q?ss=20to=20other=20Classes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 62 ++++++++++++++------- 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index aa142f282..9edab180c 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -696,7 +696,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitMethod({ name: "from", body: () => { - this.emitAsserts(c, className); + this.emitClassAsserts(c, className); this.ensureBlankLine(); this.emitLine("return new ", self, "("); @@ -720,7 +720,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitMethod({ name: "from", body: () => { - this.emitAsserts(c, className); + this.emitClassAsserts(c, className); this.ensureBlankLine(); this.emitLine("return new ", self, "("); @@ -751,7 +751,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.finishFile(); } - protected emitAsserts(classType: ClassType, className: Name): void { + protected emitClassAsserts(classType: ClassType, className: Name): void { const { selfNameType } = this._options; const self = selfNameType === "default" ? className : selfNameType; @@ -762,6 +762,20 @@ export class PhpRenderer extends ConvenienceRenderer { }); } + protected emitEnumAsserts(enumName: Name): void { + const { selfNameType } = this._options; + const self = selfNameType === "default" ? enumName : selfNameType; + + this.emitLine("assert(is_string($obj), ", self, "::class . '::from expects string');"); + this.emitLine( + "assert(in_array($obj, ", + self, + "::VALUES, true), ", + self, + "::class . '::from expects valid enum value');" + ); + } + protected emitTypeAsserts( name: Name, jsonName: string, @@ -875,10 +889,20 @@ export class PhpRenderer extends ConvenienceRenderer { } protected emitEnumDefinition(e: EnumType, enumName: Name): void { + const { + nativeEnums, + selfNameType, + staticTypeAnnotation, + mixedTypeAnnotation, + constructorProperties, + readonlyProperties + } = this._options; + const self = selfNameType === "default" ? enumName : selfNameType; + this.emitFileHeader(enumName, []); this.emitDescription(this.descriptionForType(e)); - if (this._options.nativeEnums) { + if (nativeEnums) { this.emitBlockWithBraceOnNewLine(["enum ", enumName, ": string"], () => { this.forEachEnumCase(e, "none", (name, jsonName) => { this.emitLine("case ", name, " = '", jsonName, "';"); @@ -888,8 +912,8 @@ export class PhpRenderer extends ConvenienceRenderer { } this.emitBlockWithBraceOnNewLine(["class ", enumName], () => { - const { constructorProperties, readonlyProperties } = this._options; const accessor = readonlyProperties ? "public readonly" : "protected"; + const returnType = staticTypeAnnotation ? "static" : selfNameType === "static" ? "self" : self; this.forEachEnumCase(e, "none", (name, jsonName) => { this.emitLine("public const ", name, " = '", jsonName, "';"); @@ -899,23 +923,23 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine("public const VALUES = ["); this.indent(() => { this.forEachEnumCase(e, "none", name => { - this.emitLine(enumName, "::", name, ","); + this.emitLine(self === "static" ? "self" : self, "::", name, ","); }); }); this.emitLine("];"); this.ensureBlankLine(); if (!constructorProperties) { - this.emitLine(accessor, " string $enum;"); + this.emitLine(accessor, " string $value;"); this.ensureBlankLine(); } - this.emitDescriptionBlock(["@param string $enum"]); + this.emitDescriptionBlock(["@param string $value"]); this.emitBlockWithBraceOnNewLine( - ["public function __construct(", constructorProperties ? accessor + " " : "", "string $enum)"], + ["public function __construct(", constructorProperties ? accessor + " " : "", "string $value)"], () => { if (!constructorProperties) { - this.emitLine("$this->enum = $enum;"); + this.emitLine("$this->value = $value;"); } } ); @@ -924,10 +948,7 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitMethod({ name: "to", body: () => { - this.emitBlock(["if (in_array($this->enum, ", enumName, "::VALUES, true))"], () => { - this.emitLine("return $this->enum;"); - }); - this.emitLine("throw new Exception('the give value is not an enum-value.');"); + this.emitLine("return $this->value;"); }, returnType: "string" }); @@ -936,14 +957,15 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitMethod({ name: "from", body: () => { - this.emitBlock(["if (in_array($obj, ", enumName, "::VALUES, true))"], () => { - this.emitLine("return new ", enumName, "($obj);"); - }); - this.emitLine("throw new Exception('Cannot deserialize ", enumName, "');"); + this.emitEnumAsserts(enumName); + this.ensureBlankLine(); + + this.emitLine("return new ", self, "($obj);"); }, - args: ["$obj"], + args: mixedTypeAnnotation ? ["mixed $obj"] : ["$obj"], docBlockArgs: ["mixed $obj"], - returnType: enumName, + returnType, + docBlockReturnType: self, isStatic: true }); }); From 4c74f9f99e7a18fbf9ad4182a8aaec1f157baa96 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sun, 5 Nov 2023 11:37:37 +0100 Subject: [PATCH 41/44] =?UTF-8?q?=E2=9C=A8=20Class=20Property=20Annotation?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 9edab180c..1bb35a467 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -615,9 +615,10 @@ export class PhpRenderer extends ConvenienceRenderer { if (!constructorProperties) { this.forEachClassProperty(c, "none", (name, _jsonName, p) => { + this.emitDescriptionBlock([["@var ", this.phpDocType(p.type)]]); this.emitLine(accessor, " ", this.phpType(p.type), " $", name, ";"); + this.ensureBlankLine(); }); - this.ensureBlankLine(); } const docBlock: Sourcelike[] = []; From c89247fcc350469b3efd51bac012e804ac8d8d79 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sun, 5 Nov 2023 17:10:01 +0100 Subject: [PATCH 42/44] =?UTF-8?q?=E2=9C=A8=20Class=20Property=20Type=20Ann?= =?UTF-8?q?otations=20Option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 81 +++++++++++++++------ 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 1bb35a467..91a7a994d 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -55,6 +55,11 @@ export const phpOptions = { firstCallable: new BooleanOption("first-callable", "Use first callable syntax whenever possible", true), staticTypeAnnotation: new BooleanOption("static-type-annotation", "Use static type hinting on methods", true), mixedTypeAnnotation: new BooleanOption("mixed-type-annotation", "Use mixed type hinting on methods", true), + classPropertyTypeAnnotations: new BooleanOption( + "class-property-type-annotations", + "Use type annotations on class properties", + true + ), constructorProperties: new BooleanOption( "constructor-properties", "Declare class properties inside constructor", @@ -130,6 +135,7 @@ export class PhpTargetLanguage extends TargetLanguage { if (phpVersion < 7.4) { options.arrowFunctions = false; + options.classPropertyTypeAnnotations = false; } return options; @@ -606,17 +612,35 @@ export class PhpRenderer extends ConvenienceRenderer { }); } + protected emitClassProperty( + propertyType: Type, + propertyName: Name, + withAccessor: boolean, + withType: boolean, + suffix: Sourcelike + ): void { + const { readonlyProperties } = this._options; + + const accessor = withAccessor ? (readonlyProperties ? "public readonly" : "protected") : ""; + const type = + withType || accessor === "public readonly" ? this.sourcelikeToString(this.phpType(propertyType)) : ""; + const variable = "$" + this.sourcelikeToString(propertyName); + + const line = [accessor, type, variable].filter(Boolean).join(" "); + + this.emitLine(line, suffix); + } + protected emitClassDefinition(c: ClassType, className: Name): void { this.emitFileHeader(className, []); this.emitBlockWithBraceOnNewLine(["class ", className], () => { - const { constructorProperties, readonlyProperties } = this._options; - const accessor = readonlyProperties ? "public readonly" : "protected"; + const { constructorProperties, classPropertyTypeAnnotations } = this._options; if (!constructorProperties) { this.forEachClassProperty(c, "none", (name, _jsonName, p) => { this.emitDescriptionBlock([["@var ", this.phpDocType(p.type)]]); - this.emitLine(accessor, " ", this.phpType(p.type), " $", name, ";"); + this.emitClassProperty(p.type, name, true, classPropertyTypeAnnotations, ";"); this.ensureBlankLine(); }); } @@ -629,18 +653,19 @@ export class PhpRenderer extends ConvenienceRenderer { this.emitLine("public function __construct("); this.indent(() => { this.forEachClassProperty(c, "none", (name, __, p, position) => { - const prefix = constructorProperties ? [accessor, " "] : [""]; const suffix = ["last", "only"].includes(position) ? "" : ","; - this.emitLine(...prefix, this.phpType(p.type), " $", name, suffix); + this.emitClassProperty(p.type, name, constructorProperties, true, suffix); }); }); this.emitBlock(")", () => { - if (!constructorProperties) { - this.forEachClassProperty(c, "none", name => { - this.emitLine("$this->", name, " = $", name, ";"); - }); + if (constructorProperties) { + return; } + + this.forEachClassProperty(c, "none", name => { + this.emitLine("$this->", name, " = $", name, ";"); + }); }); this.forEachClassProperty(c, "leading-and-interposing", (name, jsonName, p) => { @@ -889,6 +914,17 @@ export class PhpRenderer extends ConvenienceRenderer { throw Error("emitUnionDefinition not implemented"); } + protected emitEnumProperty(withAccessor: boolean, withType: boolean, suffix: Sourcelike): void { + const { readonlyProperties } = this._options; + + const accessor = withAccessor ? (readonlyProperties ? "public readonly" : "protected") : ""; + const type = withType || accessor === "public readonly" ? "string" : ""; + + const line = [accessor, type, "$value"].filter(Boolean).join(" "); + + this.emitLine(line, suffix); + } + protected emitEnumDefinition(e: EnumType, enumName: Name): void { const { nativeEnums, @@ -896,7 +932,7 @@ export class PhpRenderer extends ConvenienceRenderer { staticTypeAnnotation, mixedTypeAnnotation, constructorProperties, - readonlyProperties + classPropertyTypeAnnotations } = this._options; const self = selfNameType === "default" ? enumName : selfNameType; @@ -913,9 +949,6 @@ export class PhpRenderer extends ConvenienceRenderer { } this.emitBlockWithBraceOnNewLine(["class ", enumName], () => { - const accessor = readonlyProperties ? "public readonly" : "protected"; - const returnType = staticTypeAnnotation ? "static" : selfNameType === "static" ? "self" : self; - this.forEachEnumCase(e, "none", (name, jsonName) => { this.emitLine("public const ", name, " = '", jsonName, "';"); }); @@ -931,19 +964,23 @@ export class PhpRenderer extends ConvenienceRenderer { this.ensureBlankLine(); if (!constructorProperties) { - this.emitLine(accessor, " string $value;"); + this.emitDescriptionBlock([["@var string $value"]]); + this.emitEnumProperty(true, classPropertyTypeAnnotations, ";"); this.ensureBlankLine(); } this.emitDescriptionBlock(["@param string $value"]); - this.emitBlockWithBraceOnNewLine( - ["public function __construct(", constructorProperties ? accessor + " " : "", "string $value)"], - () => { - if (!constructorProperties) { - this.emitLine("$this->value = $value;"); - } + this.emitLine("public function __construct("); + this.indent(() => { + this.emitEnumProperty(constructorProperties, true, ""); + }); + this.emitBlock(")", () => { + if (constructorProperties) { + return; } - ); + + this.emitLine("$this->value = $value;"); + }); this.ensureBlankLine(); this.emitMethod({ @@ -965,7 +1002,7 @@ export class PhpRenderer extends ConvenienceRenderer { }, args: mixedTypeAnnotation ? ["mixed $obj"] : ["$obj"], docBlockArgs: ["mixed $obj"], - returnType, + returnType: staticTypeAnnotation ? "static" : selfNameType === "static" ? "self" : self, docBlockReturnType: self, isStatic: true }); From 6114e7266632c62fff67b67f9faa6cac92d819bb Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Sun, 5 Nov 2023 17:22:23 +0100 Subject: [PATCH 43/44] =?UTF-8?q?=E2=9C=A8=20Readonly=20Classes=20Option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/quicktype-core/src/language/Php.ts | 35 ++++++++++++++------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/quicktype-core/src/language/Php.ts b/packages/quicktype-core/src/language/Php.ts index 91a7a994d..2b82b414d 100644 --- a/packages/quicktype-core/src/language/Php.ts +++ b/packages/quicktype-core/src/language/Php.ts @@ -49,6 +49,7 @@ export const phpOptions = { withGet: new BooleanOption("with-get", "Create Getter", false), withSet: new BooleanOption("with-set", "Create Setter", false), readonlyProperties: new BooleanOption("readonly-properties", "Use public readonly instead of protected", true), + readonlyClasses: new BooleanOption("readonly-classes", "Make classes readonly", true), nativeEnums: new BooleanOption("native-enums", "Use enums instead of enum classes", true), arrowFunctions: new BooleanOption("arrow-functions", "Use arrow functions whenever possible", true), callable: new BooleanOption("callable", "Use callable syntax whenever possible", true), @@ -118,7 +119,7 @@ export class PhpTargetLanguage extends TargetLanguage { const { phpVersion } = options; if (phpVersion < 8.2) { - // no features yet + options.readonlyClasses = false; } if (phpVersion < 8.1) { @@ -619,9 +620,15 @@ export class PhpRenderer extends ConvenienceRenderer { withType: boolean, suffix: Sourcelike ): void { - const { readonlyProperties } = this._options; - - const accessor = withAccessor ? (readonlyProperties ? "public readonly" : "protected") : ""; + const { readonlyProperties, readonlyClasses } = this._options; + + const accessor = withAccessor + ? readonlyClasses + ? "public" + : readonlyProperties + ? "public readonly" + : "protected" + : ""; const type = withType || accessor === "public readonly" ? this.sourcelikeToString(this.phpType(propertyType)) : ""; const variable = "$" + this.sourcelikeToString(propertyName); @@ -632,11 +639,10 @@ export class PhpRenderer extends ConvenienceRenderer { } protected emitClassDefinition(c: ClassType, className: Name): void { + const { constructorProperties, classPropertyTypeAnnotations, readonlyClasses } = this._options; this.emitFileHeader(className, []); - this.emitBlockWithBraceOnNewLine(["class ", className], () => { - const { constructorProperties, classPropertyTypeAnnotations } = this._options; - + this.emitBlockWithBraceOnNewLine([readonlyClasses ? "readonly " : "", "class ", className], () => { if (!constructorProperties) { this.forEachClassProperty(c, "none", (name, _jsonName, p) => { this.emitDescriptionBlock([["@var ", this.phpDocType(p.type)]]); @@ -915,9 +921,15 @@ export class PhpRenderer extends ConvenienceRenderer { } protected emitEnumProperty(withAccessor: boolean, withType: boolean, suffix: Sourcelike): void { - const { readonlyProperties } = this._options; - - const accessor = withAccessor ? (readonlyProperties ? "public readonly" : "protected") : ""; + const { readonlyProperties, readonlyClasses } = this._options; + + const accessor = withAccessor + ? readonlyClasses + ? "public" + : readonlyProperties + ? "public readonly" + : "protected" + : ""; const type = withType || accessor === "public readonly" ? "string" : ""; const line = [accessor, type, "$value"].filter(Boolean).join(" "); @@ -932,6 +944,7 @@ export class PhpRenderer extends ConvenienceRenderer { staticTypeAnnotation, mixedTypeAnnotation, constructorProperties, + readonlyClasses, classPropertyTypeAnnotations } = this._options; const self = selfNameType === "default" ? enumName : selfNameType; @@ -948,7 +961,7 @@ export class PhpRenderer extends ConvenienceRenderer { return this.finishFile(); } - this.emitBlockWithBraceOnNewLine(["class ", enumName], () => { + this.emitBlockWithBraceOnNewLine([readonlyClasses ? "readonly " : "", "class ", enumName], () => { this.forEachEnumCase(e, "none", (name, jsonName) => { this.emitLine("public const ", name, " = '", jsonName, "';"); }); From c848638be640f1237dcd5b5e23af67074e17e1f1 Mon Sep 17 00:00:00 2001 From: Luca Di Fazio Date: Fri, 12 Jan 2024 18:34:55 +0100 Subject: [PATCH 44/44] =?UTF-8?q?=F0=9F=90=9B=20Fix=20json=5Fdecode=20in?= =?UTF-8?q?=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/fixtures/php/main.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/php/main.php b/test/fixtures/php/main.php index 13daa62d7..a273bcfdb 100644 --- a/test/fixtures/php/main.php +++ b/test/fixtures/php/main.php @@ -5,7 +5,7 @@ require_once("./TopLevel.php"); $json_string_in = file_get_contents($argv[1]); -$json_in = json_decode($json_string_in); +$json_in = json_decode($json_string_in, true); $data = TopLevel::from($json_in); $json_out = $data->to(); $json_string_out = json_encode($json_out);