Skip to content

Commit

Permalink
Allow using Emoji ZWJ Sequences
Browse files Browse the repository at this point in the history
Signed-off-by: Georg Ehrke <developer@georgehrke.com>
  • Loading branch information
georgehrke committed Jun 3, 2020
1 parent fb878f5 commit 5d9711b
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 1 deletion.
46 changes: 45 additions & 1 deletion apps/user_status/lib/Service/StatusService.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public function setStatus(string $userId,
throw new InvalidStatusTypeException('Status-type "' . $statusType . '" is not supported');
}
// Check if statusIcon contains only one character
if (\mb_strlen($statusIcon) > 1) {
if ($statusIcon !== null && !$this->isValidEmoji($statusIcon)) {
throw new InvalidStatusIconException('Status-Icon is longer than one character');
}
// Check for maximum length of custom message
Expand Down Expand Up @@ -158,4 +158,48 @@ public function removeUserStatus(string $userId): bool {
$this->mapper->delete($userStatus);
return true;
}

/**
* @param string $emoji
* @return bool
*/
private function isValidEmoji(string $emoji): bool {
$intlBreakIterator = \IntlBreakIterator::createCharacterInstance();
$intlBreakIterator->setText($emoji);

$characterCount = 0;
while ($intlBreakIterator->next() !== \IntlBreakIterator::DONE) {
$characterCount++;
}

if ($characterCount !== 1) {
return false;
}

$codePointIterator = \IntlBreakIterator::createCodePointInstance();
$codePointIterator->setText($emoji);

foreach($codePointIterator->getPartsIterator() as $codePoint) {
$codePointType = \IntlChar::charType($codePoint);

// If the current code-point is an emoji or a modifier (like a skin-tone)
// just continue and check the next character
if ($codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_SYMBOL ||
$codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_LETTER ||
$codePointType === \IntlChar::CHAR_CATEGORY_OTHER_SYMBOL) {
continue;
}

// If it's neither a modifier nor an emoji, we only allow
// a zero-width-joiner or a variation selector 16
$codePointValue = \IntlChar::ord($codePoint);
if ($codePointValue === 8205 || $codePointValue === 65039) {
continue;
}

return false;
}

return true;
}
}
6 changes: 6 additions & 0 deletions apps/user_status/tests/Unit/Service/StatusServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ public function setStatusDataProvider(): array {
// Clear at is in the past
['john.doe', 'busy', '📱', 'In a phone call', 10, true, false, true, InvalidClearAtException::class, 'ClearAt is in the past'],
['john.doe', 'busy', '📱', 'In a phone call', 10, false, false, true, InvalidClearAtException::class, 'ClearAt is in the past'],
// Test some more complex emojis with modifiers and zero-width-joiner
['john.doe', 'busy', '👩🏿‍💻', 'In a phone call', 42, true, true, false, null, null],
['john.doe', 'busy', '🤷🏼‍♀️', 'In a phone call', 42, true, true, false, null, null],
['john.doe', 'busy', '🏳️‍🌈', 'In a phone call', 42, true, true, false, null, null],
['john.doe', 'busy', '👨‍👨‍👦‍👦', 'In a phone call', 42, true, true, false, null, null],
['john.doe', 'busy', '👩‍❤️‍👩', 'In a phone call', 42, true, true, false, null, null],
];
}

Expand Down

0 comments on commit 5d9711b

Please sign in to comment.