Skip to content

Commit

Permalink
Set check-specific error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
MatTheCat committed Feb 10, 2023
1 parent b48a2dd commit 84ed1f0
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@
#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class NoSuspiciousCharacters extends Constraint
{
public const RESTRICTION_LEVEL_ERROR = '1ece07dc-dca2-45f1-ba47-8d7dc3a12774';
public const INVISIBLE_ERROR = '6ed60e6c-179b-4e93-8a6c-667d85c6de5e';
public const CHAR_LIMIT_ERROR = 'ae6e496e-b315-4fdf-bea5-657accac631d';
public const MIXED_NUMBERS_ERROR = '9f01fc26-3bc4-44b1-a6b1-c08e2412053a';
public const HIDDEN_OVERLAY_ERROR = '56380dc5-0476-4f04-bbaa-b68cd1c2d974';

protected const ERROR_NAMES = [
self::RESTRICTION_LEVEL_ERROR => 'RESTRICTION_LEVEL_ERROR',
self::INVISIBLE_ERROR => 'INVISIBLE_ERROR',
self::CHAR_LIMIT_ERROR => 'CHAR_LIMIT_ERROR',
self::MIXED_NUMBERS_ERROR => 'MIXED_NUMBERS_ERROR',
self::HIDDEN_OVERLAY_ERROR => 'INVALID_CASE_ERROR',
];

/**
* Check that a string satisfies the requirements for the specified restriction level.
* It defaults to {@see self::RESTRICTION_LEVEL_HIGH} when using ICU >= 58,
Expand Down Expand Up @@ -58,7 +72,12 @@ class NoSuspiciousCharacters extends Constraint
public const RESTRICTION_LEVEL_MINIMAL = 1342177280;
public const RESTRICTION_LEVEL_NONE = 1610612736;

public $message = 'This value is suspicious.';
public string $restrictionLevelMessage = 'Restriction level check failed.';
public string $invisibleMessage = 'Invisible check failed.';
public string $charLimitMessage = 'Char limit check failed.';
public string $mixedNumbersMessage = 'Mixed numbers check failed.';
public string $hiddenOverlayMessage = 'Hidden overlay check failed.';

public int $checks = self::CHECK_RESTRICTION_LEVEL | self::CHECK_INVISIBLE | self::CHECK_CHAR_LIMIT | self::CHECK_MIXED_NUMBERS | self::CHECK_HIDDEN_OVERLAY;
public ?int $restrictionLevel = null;
public array $profileLocales = [];
Expand All @@ -70,7 +89,11 @@ class NoSuspiciousCharacters extends Constraint
*/
public function __construct(
array $options = null,
string $message = null,
string $restrictionLevelMessage = null,
string $invisibleMessage = null,
string $charLimitMessage = null,
string $mixedNumbersMessage = null,
string $hiddenOverlayMessage = null,
int $checks = null,
int $restrictionLevel = null,
array $profileLocales = null,
Expand All @@ -84,7 +107,11 @@ public function __construct(

parent::__construct($options, $groups, $payload);

$this->message ??= $message;
$this->restrictionLevelMessage ??= $restrictionLevelMessage;
$this->invisibleMessage ??= $invisibleMessage;
$this->charLimitMessage ??= $charLimitMessage;
$this->mixedNumbersMessage ??= $mixedNumbersMessage;
$this->hiddenOverlayMessage ??= $hiddenOverlayMessage;
$this->checks ??= $checks;
$this->restrictionLevel ??= $restrictionLevel;
$this->profileLocales ??= $profileLocales;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,29 @@
*/
class NoSuspiciousCharactersValidator extends ConstraintValidator implements LocaleAwareInterface
{
private const CHECK_ERROR = [
NoSuspiciousCharacters::CHECK_RESTRICTION_LEVEL => [
'code' => NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR,
'messageProperty' => 'restrictionLevelMessage',
],
NoSuspiciousCharacters::CHECK_INVISIBLE => [
'code' => NoSuspiciousCharacters::INVISIBLE_ERROR,
'messageProperty' => 'invisibleMessage',
],
NoSuspiciousCharacters::CHECK_CHAR_LIMIT => [
'code' => NoSuspiciousCharacters::CHAR_LIMIT_ERROR,
'messageProperty' => 'charLimitMessage',
],
NoSuspiciousCharacters::CHECK_MIXED_NUMBERS => [
'code' => NoSuspiciousCharacters::MIXED_NUMBERS_ERROR,
'messageProperty' => 'mixedNumbersMessage',
],
NoSuspiciousCharacters::CHECK_HIDDEN_OVERLAY => [
'code' => NoSuspiciousCharacters::HIDDEN_OVERLAY_ERROR,
'messageProperty' => 'hiddenOverlayMessage',
],
];

private string $locale;

public function validate(mixed $value, Constraint $constraint)
Expand Down Expand Up @@ -55,16 +78,23 @@ public function validate(mixed $value, Constraint $constraint)
}
$checker->setAllowedLocales(implode(',', $allowedLocales));

$checker->setChecks($constraint->checks);
foreach (self::CHECK_ERROR as $check => $error) {
if (!($constraint->checks & $check)) {
continue;
}

if (!$checker->isSuspicious($value)) {
return;
}
$checker->setChecks($check);

$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->addViolation()
;
if (!$checker->isSuspicious($value)) {
continue;
}

$this->context->buildViolation($constraint->{$error['messageProperty']})
->setParameter('{{ value }}', $this->formatValue($value))
->setCode($error['code'])
->addViolation()
;
}
}

public function setLocale(string $locale)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,49 +40,100 @@ public function testNonSuspiciousStrings()
/**
* @dataProvider provideSuspiciousStrings
*/
public function testSuspiciousStrings(string $string, array $options)
public function testSuspiciousStrings(string $string, array $options, string $errorCode, string $errorMessage)
{
$this->validator->validate($string, new NoSuspiciousCharacters(['message' => 'myMessage'] + $options));
$this->validator->validate($string, new NoSuspiciousCharacters($options));

$this->buildViolation('myMessage')
$this->buildViolation($errorMessage)
->setCode($errorCode)
->setParameter('{{ value }}', '"'.$string.'"')
->assertRaised();
}

public static function provideSuspiciousStrings(): iterable
{
yield 'Fails restriction level check because of character outside ASCII range' => ['à',
yield 'Fails restriction level check because of character outside ASCII range' => [
'à',
['restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_ASCII],
NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR,
'Restriction level check failed.',
];

yield 'Fails restriction level check because of mixed-script string' => [
'àㄚ',
[
'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_SINGLE_SCRIPT,
'profileLocales' => ['zh_Hant_TW'],
],
NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR,
'Restriction level check failed.',
];

yield 'Fails restriction level check because of disallowed Armenian script' => [
'àԱ',
[
'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_HIGH,
'profileLocales' => ['hy_AM'],
],
NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR,
'Restriction level check failed.',
];

yield 'Fails restriction level check because of disallowed Greek script' => [
'àπ',
[
'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_MODERATE,
'profileLocales' => ['el_GR'],
],
NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR,
'Restriction level check failed.',
];

yield 'Fails restriction level check because of Greek script absent from profile' => [
'àπ',
[
'checks' => NoSuspiciousCharacters::CHECK_RESTRICTION_LEVEL,
'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_MINIMAL,
],
NoSuspiciousCharacters::RESTRICTION_LEVEL_ERROR,
'Restriction level check failed.',
];

yield 'Fails INVISIBLE check because of duplicated non-spacing mark' => [
'à̀',
[
'checks' => NoSuspiciousCharacters::CHECK_INVISIBLE,
],
NoSuspiciousCharacters::INVISIBLE_ERROR,
'Invisible check failed.',
];

yield 'Fails CHAR_LIMIT check because of Greek script absent from profile' => [
'àπ',
[
'checks' => NoSuspiciousCharacters::CHECK_CHAR_LIMIT,
],
NoSuspiciousCharacters::CHAR_LIMIT_ERROR,
'Char limit check failed.',
];

yield 'Fails MIXED_NUMBERS check because of different numbering systems' => [
'8৪',
[
'checks' => NoSuspiciousCharacters::CHECK_MIXED_NUMBERS,
],
NoSuspiciousCharacters::MIXED_NUMBERS_ERROR,
'Mixed numbers check failed.',
];

yield 'Fails HIDDEN_OVERLAY check because of hidden combining character' => [
'i̇',
[
'checks' => NoSuspiciousCharacters::CHECK_HIDDEN_OVERLAY,
],
NoSuspiciousCharacters::HIDDEN_OVERLAY_ERROR,
'Hidden overlay check failed.',
];
yield 'Fails restriction level check because of mixed-script string' => ['àㄚ', [
'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_SINGLE_SCRIPT,
'profileLocales' => ['zh_Hant_TW'],
]];
yield 'Fails restriction level check because of disallowed Armenian script' => ['àԱ', [
'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_HIGH,
'profileLocales' => ['hy_AM'],
]];
yield 'Fails restriction level check because of disallowed Greek script' => ['àπ', [
'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_MODERATE,
'profileLocales' => ['el_GR'],
]];
yield 'Fails restriction level check because of Greek script absent from profile' => ['àπ', [
'checks' => NoSuspiciousCharacters::CHECK_RESTRICTION_LEVEL,
'restrictionLevel' => NoSuspiciousCharacters::RESTRICTION_LEVEL_MINIMAL,
]];

yield 'Fails INVISIBLE check because of duplicated non-spacing mark' => ['à̀', [
'checks' => NoSuspiciousCharacters::CHECK_INVISIBLE,
]];
yield 'Fails CHAR_LIMIT check because of Greek script absent from profile' => ['àπ', [
'checks' => NoSuspiciousCharacters::CHECK_CHAR_LIMIT,
]];
yield 'Fails MIXED_NUMBERS check because of different numbering systems' => ['8৪', [
'checks' => NoSuspiciousCharacters::CHECK_MIXED_NUMBERS,
]];
yield 'Fails HIDDEN_OVERLAY check because of hidden combining character' => ['i̇', [
'checks' => NoSuspiciousCharacters::CHECK_HIDDEN_OVERLAY,
]];
}

public function testConstants()
Expand Down

0 comments on commit 84ed1f0

Please sign in to comment.