EmojiHelper.php 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC;
  8. use OCP\IDBConnection;
  9. use OCP\IEmojiHelper;
  10. class EmojiHelper implements IEmojiHelper {
  11. private IDBConnection $db;
  12. public function __construct(IDBConnection $db) {
  13. $this->db = $db;
  14. }
  15. public function doesPlatformSupportEmoji(): bool {
  16. return $this->db->supports4ByteText() &&
  17. \class_exists(\IntlBreakIterator::class);
  18. }
  19. public function isValidSingleEmoji(string $emoji): bool {
  20. $intlBreakIterator = \IntlBreakIterator::createCharacterInstance();
  21. $intlBreakIterator->setText($emoji);
  22. $characterCount = 0;
  23. while ($intlBreakIterator->next() !== \IntlBreakIterator::DONE) {
  24. $characterCount++;
  25. }
  26. if ($characterCount !== 1) {
  27. return false;
  28. }
  29. $codePointIterator = \IntlBreakIterator::createCodePointInstance();
  30. $codePointIterator->setText($emoji);
  31. foreach ($codePointIterator->getPartsIterator() as $codePoint) {
  32. $codePointType = \IntlChar::charType($codePoint);
  33. // Unicode chars need 2 or more chars
  34. // The characterCount before this loop already validate if is a single emoji
  35. // This condition is to don't continue if non emoji chars
  36. if (strlen($emoji) >= 2) {
  37. // If the current code-point is an emoji or a modifier (like a skin-tone)
  38. // just continue and check the next character
  39. if ($codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_SYMBOL ||
  40. $codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_LETTER ||
  41. $codePointType === \IntlChar::CHAR_CATEGORY_OTHER_SYMBOL ||
  42. $codePointType === \IntlChar::CHAR_CATEGORY_FORMAT_CHAR || // i.e. 🏴󠁧󠁢󠁥󠁮󠁧󠁿 🏴󠁧󠁢󠁳󠁣󠁴󠁿
  43. $codePointType === \IntlChar::CHAR_CATEGORY_OTHER_PUNCTUATION || // i.e. ‼️ ⁉️ #⃣
  44. $codePointType === \IntlChar::CHAR_CATEGORY_LOWERCASE_LETTER || // i.e. ℹ️
  45. $codePointType === \IntlChar::CHAR_CATEGORY_MATH_SYMBOL || // i.e. ↔️ ◻️ ⤴️ ⤵️
  46. $codePointType === \IntlChar::CHAR_CATEGORY_ENCLOSING_MARK || // i.e. 0⃣..9⃣
  47. $codePointType === \IntlChar::CHAR_CATEGORY_DECIMAL_DIGIT_NUMBER || // i.e. 0⃣..9⃣
  48. $codePointType === \IntlChar::CHAR_CATEGORY_DASH_PUNCTUATION || // i.e. 〰️
  49. $codePointType === \IntlChar::CHAR_CATEGORY_GENERAL_OTHER_TYPES
  50. ) {
  51. continue;
  52. }
  53. }
  54. // If it's neither a modifier nor an emoji, we only allow
  55. // a zero-width-joiner or a variation selector 16
  56. $codePointValue = \IntlChar::ord($codePoint);
  57. if ($codePointValue === 8205 || $codePointValue === 65039) {
  58. continue;
  59. }
  60. return false;
  61. }
  62. return true;
  63. }
  64. }