Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,6 @@
"NODE_ENV": "development",
"PNPM_VERSION": "9.4.0"
},
"includeCoAuthoredBy": false,
"includeCoAuthoredBy": true,
"model": "claude-sonnet-4-20250514"
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AbstractGeneratorContext, FernGeneratorExec, GeneratorNotificationService } from "@fern-api/base-generator";
import { assertNever } from "@fern-api/core-utils";
import { RelativeFilePath } from "@fern-api/fs-utils";
import { BasePhpCustomConfigSchema, GLOBAL_NAMESPACE, php, SELF } from "@fern-api/php-codegen";
import { BasePhpCustomConfigSchema, GLOBAL_NAMESPACE, getSafeClassName, php, SELF } from "@fern-api/php-codegen";
import {
FernFilepath,
IntermediateRepresentation,
Expand Down Expand Up @@ -82,7 +82,7 @@ export abstract class AbstractPhpGeneratorContext<
}

public getClassName(name: Name): string {
return name.pascalCase.safeName;
return getSafeClassName(name.pascalCase.safeName);
}

public getGlobalNamespace(): string {
Expand Down
4 changes: 3 additions & 1 deletion generators/php/codegen/src/ast/Enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ export class Enum extends AstNode {
writer.write(`case ${member.name}`);
if (member.value != null) {
if (typeof member.value === "string") {
writer.write(` = "${member.value}"`);
// Escape backslashes and double quotes in the string value
const escapedValue = member.value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
writer.write(` = "${escapedValue}"`);
} else {
writer.write(` = ${member.value}`);
}
Expand Down
75 changes: 75 additions & 0 deletions generators/php/codegen/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// PHP reserved keywords (case-insensitive) that cannot be used as class names
// Source: https://www.php.net/manual/en/reserved.keywords.php
export const PHP_RESERVED_KEYWORDS = new Set([
"abstract",
"and",
"array",
"as",
"break",
"callable",
"case",
"catch",
"class",
"clone",
"const",
"continue",
"declare",
"default",
"die",
"do",
"echo",
"else",
"elseif",
"empty",
"enddeclare",
"endfor",
"endforeach",
"endif",
"endswitch",
"endwhile",
"eval",
"exit",
"extends",
"final",
"finally",
"fn",
"for",
"foreach",
"function",
"global",
"goto",
"if",
"implements",
"include",
"include_once",
"instanceof",
"insteadof",
"interface",
"isset",
"list",
"match",
"namespace",
"new",
"or",
"print",
"private",
"protected",
"public",
"readonly",
"require",
"require_once",
"return",
"static",
"switch",
"throw",
"trait",
"try",
"unset",
"use",
"var",
"while",
"xor",
"yield",
"yield_from",
"__halt_compiler"
]);
2 changes: 2 additions & 0 deletions generators/php/codegen/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export { GLOBAL_NAMESPACE, SELF, STATIC } from "./ast/core/Constant";
export { PHP_RESERVED_KEYWORDS } from "./constants";
export { BasePhpCustomConfigSchema } from "./custom-config/BasePhpCustomConfigSchema";
export * as php from "./php";
export { getSafeClassName } from "./utils";
15 changes: 15 additions & 0 deletions generators/php/codegen/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { PHP_RESERVED_KEYWORDS } from "./constants";

/**
* Returns a safe class name, adding a trailing underscore if the name
* conflicts with a PHP reserved keyword.
*/
export function getSafeClassName(className: string): string {
// Check if the class name is a reserved keyword (case-insensitive)
if (PHP_RESERVED_KEYWORDS.has(className.toLowerCase())) {
// Add trailing underscore to avoid collision
return `${className}_`;
}

return className;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
FernGeneratorExec
} from "@fern-api/browser-compatible-base-generator";
import { FernIr } from "@fern-api/dynamic-ir-sdk";
import { BasePhpCustomConfigSchema, php } from "@fern-api/php-codegen";
import { BasePhpCustomConfigSchema, getSafeClassName, php } from "@fern-api/php-codegen";
import { camelCase, upperFirst } from "lodash-es";

import { DynamicTypeLiteralMapper } from "./DynamicTypeLiteralMapper";
Expand Down Expand Up @@ -63,7 +63,7 @@ export class DynamicSnippetsGeneratorContext extends AbstractDynamicSnippetsGene
}

public getClassName(name: FernIr.Name): string {
return name.pascalCase.safeName;
return getSafeClassName(name.pascalCase.safeName);
}

public getRootClientClassName(): string {
Expand Down
10 changes: 10 additions & 0 deletions generators/php/sdk/versions.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
# yaml-language-server: $schema=../../../fern-versions-yml.schema.json
- version: 1.17.2
changelogEntry:
- summary: |
Fix enum string escaping and PHP reserved keyword handling.
- Enum values containing quotes are now properly escaped to prevent syntax errors.
- Class names that conflict with PHP reserved keywords (e.g., "eval", "list") automatically receive a trailing underscore (e.g., "Eval_").
type: fix
createdAt: "2025-10-17"
irVersion: 59

- version: 1.17.1
changelogEntry:
- summary: |
Expand Down
Loading