respect-validation/library/Rules/Emoji.php
Henrique Moody 951c16e8e6
Refactor the NotEmoji rule
Since we have the ability to use `not` as a prefix, having rules that
validate negative behaviour makes them a bit inflexible, verbose, and
harder to understand.

This commit will refactor the `NotEmoji` and rename it to `Emoji`. It
will no longer check if the string contains emojis, but rather if the
string is an emoji or not. I’m also adding support to more emojis, since
the rule was a bit outdated.

This change will make the validator more strict, but will make it useful
in other scenarios. However, later on, I would like to create a rule
called `has` which, could use a validator like `Emoji` to check if the
input _has_ emojis.

Assisted-by: Cursor (claude-4.5-opus-high)
2025-12-29 11:16:25 +01:00

62 lines
2 KiB
PHP

<?php
/*
* Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
* SPDX-License-Identifier: MIT
*/
declare(strict_types=1);
namespace Respect\Validation\Rules;
use Attribute;
use Respect\Validation\Message\Template;
use Respect\Validation\Rules\Core\Simple;
use function is_string;
use function preg_match;
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
#[Template(
'{{subject}} must be an emoji',
'{{subject}} must not be an emoji',
)]
final class Emoji extends Simple
{
private const string REGEX = <<<'REGEX'
(?:
# Tag sequence for country flags
[\x{1F1E6}-\x{1F1FF}]{2}
|
# Tag sequence for subdivision flags
\x{1F3F4}[\x{E0020}-\x{E007E}]+\x{E007F}
|
# Keycap sequences
[0-9#*](?:\x{FE0F})?\x{20E3}
|
# Standard emoji cluster:
\p{Extended_Pictographic} # base emoji
(?:\x{FE0F})? # optional emoji variant selector
(?:[\x{1F3FB}-\x{1F3FF}])? # optional skin tone modifier
(?: # optionally repeat ZWJ sequences:
\x{200D} # ZWJ
(?: # joined element:
\p{Extended_Pictographic} # another emoji
|
[\x{2640}\x{2642}\x{26A7}] # or gender symbol
)
(?:\x{FE0F})? # optional variant selector
(?:[\x{1F3FB}-\x{1F3FF}])? # optional skin tone modifier
)* # ...zero or more times
)
REGEX;
public function isValid(mixed $input): bool
{
if (!is_string($input)) {
return false;
}
return preg_match('/^' . self::REGEX . '+$/ux', $input) === 1;
}
}