Skip to main content

NoSpecialCharsMapper

Maps an input value to a string containing only the characters [0-9A-Za-z._-] (or null). It is designed for identifiers, slugs, path-ish fragments and similar machine-facing strings where accented letters, whitespace and punctuation are not allowed.

Accepts the following input types:

  • string.
  • Any Stringable object.
  • null — left as null, subject to validation.

Non-null values are normalized before validation by IoUtils::stripSpecialChars():

  1. the value is trimmed;
  2. accented characters are transliterated to ASCII (ä→ae, ö→oe, ß→ss, é→e, …);
  3. any remaining whitespace is replaced with -;
  4. every character outside [0-9A-Za-z._-] is removed;
  5. repeated -/./_ are collapsed and leading/trailing separators stripped ("pretty" mode).

If $lowercaseOnly = true (the default) the result is then lower-cased with mb_strtolower(). An empty result is turned back into null.

Validation possibilities​

ParameterTypeDefaultDescription
$mandatoryboolfalseRejects null input.
$minlengthint?1Rejects strings shorter than this.
$maxlengthint?255Rejects strings longer than this.
$lowercaseOnlybooltrueLower-cases the stripped value with mb_strtolower().

A noSpecialChars validator also runs as a safety net — since the mapper already strips every special character, it should never report a violation on a normalized value. The rules that surface in practice are, in order: mandatory > minlength > maxlength (only the FIRST violated rule is reported per bindable).

Example: basic normalization​

$result = Bind::values(' Jklö ', null)->map(Mappers::noSpecialChars(minlength: null))
->toArray()->exec();
var_dump($result->get());

Output:

array(2) {
[0]=>
string(5) "jkloe"
[1]=>
NULL
}

' Jklö ' is trimmed to 'Jklö', the ö is transliterated to oe, and the value is lower-cased to 'jkloe'. null is left as null. (minlength: null disables the default minimum-length check so the short result passes; with the default minlength = 1 it would pass too.)

Example: mandatory and length validation​

$result = Bind::attrs(['a' => null, 'b' => 'min', 'c' => 'holeradio'])
->props(['a', 'b', 'c'], Mappers::noSpecialChars(true, 4, 8, lowercaseOnly: true))
->toArray()->exec();
var_dump($result->getErrorMap()->getChild('a')->isEmpty()); // false becuase mandatory
var_dump($result->getErrorMap()->getChild('b')->isEmpty()); // false because to short
var_dump($result->getErrorMap()->getChild('c')->isEmpty()); // false because to long

Output:

bool(false)
bool(false)
bool(false)

All three props fail: a is null so it violates mandatory; b ('min', 3 chars) is shorter than minlength = 4; c ('holeradio', 9 chars) is longer than maxlength = 8. The corresponding messages are 'Mandatory', 'Minlength [minlength = 4]' and 'Maxlength [maxlength = 8]'.