Skip to content

[2.x] fix(nicknames): cast min/max settings to int before passing to Schema\Str#4599

Merged
imorland merged 2 commits into2.xfrom
im/fix-nicknames-settings-typeerror
Apr 21, 2026
Merged

[2.x] fix(nicknames): cast min/max settings to int before passing to Schema\Str#4599
imorland merged 2 commits into2.xfrom
im/fix-nicknames-settings-typeerror

Conversation

@imorland
Copy link
Copy Markdown
Member

@imorland imorland commented Apr 21, 2026

Fixes the TypeError reported against 2.x nightly:

TypeError: Flarum\Api\Schema\Str::maxLength(): Argument #1 ($length) must be of type int, string given,
  called in .../nicknames/src/Api/UserResourceFields.php on line 47

Root cause

UserResourceFields passed the raw result of $this->settings->get('flarum-nicknames.min' / '.max') to Schema\Str::minLength(int) / maxLength(int). Settings are always returned as strings from DatabaseSettingsRepository::get().

Two separate failure modes on top of that:

  1. Empty string → TypeError. PHP's default weak type coercion accepts numeric strings ('3'3), but an empty '' triggers a TypeError. That's the production symptom — the admin cleared the min or max field and saved the nicknames config form.
  2. Naive cast reveals a second bug. (int) '' is 0, so minLength(0) / maxLength(0) are applied unconditionally. maxLength(0) rejects every nickname with "must not be greater than 0 characters". A cast alone is worse than the TypeError — it turns a hard error into a confusing validation failure.

Fresh installs haven't been affected because the Extend\Settings()->default('…', 1 | 150) defaults only kick in when the key isn't in the DB. As soon as an admin saves the nicknames config, values are persisted as strings and the code path breaks.

Fix

Cast the settings to int and gate the constraint on a positive value:

$min = (int) $this->settings->get('flarum-nicknames.min');
$max = (int) $this->settings->get('flarum-nicknames.max');

// …
->minLength($min, $min > 0)
->maxLength($max, $max > 0)

An empty or zero setting now means "no length constraint" — which is the intuitive behaviour for a cleared admin field.

Verifying the repro

Local integration test request_succeeds_when_min_or_max_setting_is_empty_string:

  • Persists '' for both settings via $this->setting(...).
  • Sends PATCH /api/users/2 with a valid nickname.
  • Before the fix: 500, exact production stack trace.
  • After the fix: 200, nickname saved.

Tangential observation (not fixed here)

Schema\Str::minLength(int $length) gives no clean way to express "no constraint" without a guard at the call site. That's a framework-level ergonomic gap — arguably minLength should no-op when $length <= 0, or accept a nullable. Keeping the local guard for this RC; happy to open a follow-up if there's appetite.

Necessity

  • Has the problem that is being solved here been clearly explained?
  • If applicable, have various options for solving this problem been considered?
  • For core PRs, does this need to be in core, or could it be in an extension? (Extension-only change.)
  • Are we willing to maintain this for years / potentially forever?

Confirmed

  • Frontend changes: N/A.
  • Backend changes: tests are green — full nicknames integration suite (21 tests) passes locally, and CI green across all 10 PHP×DB matrix jobs + PHPStan + StyleCI.
  • Core developer confirmed locally this works as intended.
  • Tests have been added, or are not appropriate here.

Required changes

  • Related documentation PR: none — internal.

…strings

UserResourceFields passes the raw settings value to Schema\Str::minLength(int)
and maxLength(int). Numeric strings ('3', '150') coerce silently under PHP's
default weak typing, but an empty string — the result of an admin clearing
the min or max field in the nicknames config and saving — triggers:

    Str::maxLength(): Argument #1 ($length) must be of type int, string given

From then on every forum request 500s because UserResourceFields is
invoked during JSON:API resource resolution. Fresh installs are unaffected
because the Extend\Settings() int defaults bypass the DB path.

Test reproduces the exact production stack trace. Fix follows.
@imorland imorland force-pushed the im/fix-nicknames-settings-typeerror branch from dbff6a0 to 42362b6 Compare April 21, 2026 06:51
Cast the persisted setting to int before handing it to Schema\Str's
minLength/maxLength, and only apply the constraint when the admin has
configured a positive value. An empty field saved from the admin panel
was otherwise stored as '' — triggering a TypeError in PHP's default
weak coercion — and, after the naive cast, coerced to 0, which made
maxLength(0) reject every nickname.

Reveals a gap in Schema\Str: the int-only signature offers no way to
"disable" a length constraint via the condition argument without a
guard at the call site. Keeping the guard local for this RC fix.
@imorland imorland marked this pull request as ready for review April 21, 2026 07:14
@imorland imorland requested a review from a team as a code owner April 21, 2026 07:14
@imorland imorland added this to the 2.0.0-rc.2 milestone Apr 21, 2026
@imorland imorland merged commit 5b0aa9c into 2.x Apr 21, 2026
24 checks passed
@imorland imorland deleted the im/fix-nicknames-settings-typeerror branch April 21, 2026 07:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant