Skip to content

feat(php): support server URL variables (region/edge routing)#16982

Open
devin-ai-integration[bot] wants to merge 5 commits into
mainfrom
devin/1783552224-php-server-url-variables
Open

feat(php): support server URL variables (region/edge routing)#16982
devin-ai-integration[bot] wants to merge 5 commits into
mainfrom
devin/1783552224-php-server-url-variables

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Description

Linear ticket: Refs

Adds OpenAPI server URL variable support to the PHP SDK generator for region/edge routing. Server variables become optional constructor parameters and are interpolated into single- or multi-base URLs, using the IR defaults when omitted. Allowed values remain documentation-only, matching the Java and Python generators.

Changes Made

  • Extract and deduplicate variables from EnvironmentsConfig, preserving static URLs where a multi-base URL has no template.
  • Rebuild $this->options['baseUrl'] or Environments::custom(...) when any server-variable option is supplied.
  • De-conflict generated option names against built-in options, auth/header constructor parameters, and other server variables using serverUrl plus numeric suffixes.
  • Add single- and multi-base URL PHP seed fixtures and a PHP feat changelog entry.
  • Updated README.md generator (not applicable)

Testing

  • Unit tests added/updated — pnpm turbo run test --filter @fern-api/php-sdk (2 files, 11 tests)
  • Full TypeScript compile — pnpm compile (160/160 tasks)
  • Lint/format — pnpm lint:biome and pnpm format:check
  • Local seed generation — both server-url-templating fixtures passed with --local --skip-scripts; regeneration produced no source snapshot changes
  • Generated SDK validation in fernapi/php-seed (PHP 8.4) — syntax, PHPUnit (75 tests / 284 assertions per fixture), and PHPStan passed for both fixtures

Link to Devin session: https://app.devin.ai/sessions/2670cdc59f3f40a786bf824bcf5f3916
Requested by: @cadesark

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@nitpickybot nitpickybot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

Adds PHP server URL variable support. Core logic is sound, but the reserved-name de-collision has a gap and the docs generation could leak into a broken URL for edge cases. Main functional concern is that the interpolation condition and default-filling logic can produce incorrect URLs when only some variables are provided.

  • 🟡 2 warning(s)
  • 🔵 1 suggestion(s)

Comment on lines +37 to +39
const optionName = RESERVED_OPTION_NAMES.has(camel)
? caseConverter.camelSafe(`server url ${getOriginalName(variable.name)}`)
: camel;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 warning

De-collision only checks against RESERVED_OPTION_NAMES, not against other server-variable option names. If two variables camelCase to the same name (e.g. server url and serverUrl), or if a variable is literally named server url environment, you can produce duplicate constructor parameter names and generate invalid PHP. Consider tracking already-emitted optionNames and suffixing on collision.

Comment on lines +651 to +664
switch (environments.type) {
case "singleBaseUrl": {
const templatedEnvironment = environments.environments.find((env) => env.urlTemplate != null);
if (templatedEnvironment?.urlTemplate == null) {
return;
}
const phpString = urlTemplateToPhpString(templatedEnvironment.urlTemplate, serverVariableOptions);
writer.controlFlow("if", php.codeblock(condition));
writeDefaults();
writer.writeTextStatement(`$this->${this.context.getClientOptionsName()}['baseUrl'] = ${phpString}`);
writer.endControlFlow();
writer.writeLine();
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 warning

The singleBaseUrl branch reads urlTemplate from the first environment that has one, but the client may be constructed with a different $environment. If multiple environments have distinct templates, interpolation always uses the first template's host regardless of which environment the user selected. The multipleBaseUrls branch has the same issue (picks first templated env). Worth confirming this matches TS/Python behavior — otherwise selecting a non-default environment silently gets the wrong base URL.

for (const option of serverVariableOptions) {
const fallback =
option.variable.default != null ? this.escapeSingleQuoted(option.variable.default) : "";
writer.writeTextStatement(`$${option.optionName} ??= '${fallback}'`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 suggestion

When a variable has no default, $region ??= '' substitutes an empty string into the URL, producing a malformed URL like https://api..example.com. Since these are exposed as optional params, a user setting only one of several variables will silently get a broken URL for the others. Consider requiring all-or-nothing, or documenting that variables without defaults are effectively required together.

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

SDK Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-07-09T05:18:32Z).

Full benchmark table (click to expand)
Generator Spec main (generator) main (E2E) PR (generator) Delta
csharp-sdk square 72s (n=5) 114s (n=5) 69s -3s (-4.2%)
go-sdk square 135s (n=5) 287s (n=5) 131s -4s (-3.0%)
java-sdk square 218s (n=5) 272s (n=5) 200s -18s (-8.3%)
php-sdk square 62s (n=5) 84s (n=5) 56s -6s (-9.7%)
python-sdk square 139s (n=5) 241s (n=5) 136s -3s (-2.2%)
ruby-sdk-v2 square 92s (n=5) 128s (n=5) 89s -3s (-3.3%)
rust-sdk square 175s (n=5) 165s (n=5) 160s -15s (-8.6%)
swift-sdk square 61s (n=5) 439s (n=5) 55s -6s (-9.8%)
ts-sdk square 129s (n=5) 128s (n=5) 115s -14s (-10.9%)

main (generator): generator-only time via --skip-scripts (includes Docker image build, container startup, IR parsing, and code generation — this is the same Docker-based flow customers use via fern generate). main (E2E): full customer-observable time including build/test scripts (nightly baseline, informational). Delta is computed against generator-only baseline.
⚠️ = generation exited with a non-zero exit code (timing may not reflect a successful run).
Baseline from nightly runs on main (latest: 2026-07-09T05:18:32Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-07-09 15:34 UTC

cade and others added 2 commits July 9, 2026 13:54
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Docs Generation Benchmark Results

Comparing PR branch against median of 5 nightly run(s) on main (latest: 2026-07-09T05:18:32Z).

Fixture main PR Delta
docs 243.0s (n=5) 905.4s (35 versions) +662.4s (+272.6%)

Docs generation runs fern generate --docs --preview end-to-end against the benchmark fixture with 35 API versions (each version: markdown processing + OpenAPI-to-IR + FDR upload).
Delta is computed against the nightly baseline on main.
Baseline from nightly run(s) on main (latest: 2026-07-09T05:18:32Z). Trigger benchmark-baseline to refresh.
Last updated: 2026-07-09 15:45 UTC

devin-ai-integration Bot and others added 2 commits July 14, 2026 14:12
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants