LoginSlug
Corex\Config\Security\LoginProtection\LoginSlug · class · Config layer
The rules a custom login slug must satisfy, in one place. These rules previously lived in three places that disagreed: the settings store sanitised on write but not on read, the settings value object threw on a slug the store happily produced, and the REST controller validated nothing at all. Each disagreement was a reachable way to lock an owner out of their own site — both were reproduced on a real install (DECISIONS #140). Anything deciding whether a slug is usable MUST come through here.
Public API
Section titled “Public API”isValid(string $slug): bool
Section titled “isValid(string $slug): bool”rejectionReason(string $slug): ?string
Section titled “rejectionReason(string $slug): ?string”Why this slug is unusable, or null when it is fine. Returns a machine-readable code, never a translated string. The settings store resolves slugs on plugins_loaded (the container rebuilds the settings on every make()), which is long before init — calling __() here makes WordPress emit a “textdomain triggered too early” notice on every request. Translation belongs to whatever shows the message to a person; see SecuritySettingsController.
orDefault(string $candidate): string
Section titled “orDefault(string $candidate): string”The slug to actually use — never an unusable one. Read and write both route through this, so a stored value always resolves to the same working slug it was saved as (FR-010). Returning the default rather than an empty string is deliberate: an empty slug means “no login URL exists”, which is the lockout.