PHP 8.4: Новая функция grapheme_str_split
grapheme_str_split
, разбивающую заданную строку на массив графем.Графема — наименьшая содержательная и функциональная единица языковой системы. Для сравнения, функция mb_str_split
из расширения Mbstring имеет схожую семантику, но с существенным отличием в том, что функция mb_str_split
разбивает строку на многобайтовые символы Unicode, а функция grapheme_str_split
разбивает на функциональные единицы системы письма.
Разница между символами Юникода и графемами важна при представлении символов в некоторых сложных языках и Эмодзи с модификаторами. mb_str_split
разбивает строку на кодовые точки Юникода, а grapheme_str_split
разбивает строку на функциональные единицы. Отдельные кодовые точки Юникода являются допустимыми символами, но в сложных шрифтах и Эмодзи разбиение строки с помощью mb_str_split
может привести к потере модификаторов у некоторых символов, например у гласных.
Например, слово на сингальском языке අයේෂ්
(по-английски произносится Ayesh
) состоит из трёх единиц в сингальской письменности: අ
+ යේ
+ ෂ්
. අ
— это отдельный символ, а යේ
и ෂ්
используют дополнительные кодовые точки Юникода в качестве модификаторов гласных. grapheme_str_split
правильно разбивает слово на отдельные символы, соответствующие сингальской системе письма, а mb_str_split
разбивает его на отдельные кодовые точки Юникода: අ
+ ය
+ේ
+ ෂ
+්
.
Ещё несколько примеров на разных языках и Эмодзи:
Строка Представление в Юникод | Вывод grapheme_str_split Представление в Юникод | Вывод mb_str_split Представление в Юникод |
---|---|---|
PHP 0050 0048 0050 | P + H + P 0050 + 0048 + 0050 | P + H + P 0050 + 0048 + 0050 |
你好 4F60 597D | 你 + 好 4F60 + 597D | 你 + 好 4F60 + 597D |
අයේෂ් 0D85 0DBA 0DDA 0DC2 0DCA | අ + යේ + ෂ් 0D85U + 0DBA 0DDA + 0DC2 0DCA | අ + ය +ේ + ෂ +් 0D85 + 0DBAU + 0DDAU + 0DC2U + 0DCA |
สวัสดี 0E2A 0E27 0E31 0E2A 0E14 0E35 | ส + วั + ส + ดี 0E2A + 0E27 0E31 + 0E2A 0DCA + 0E2A + 0E14 0E35 | ส + ว +ั + ส + ด + ีี 0E2A + 0E27 + 0E31 + 0E2A + 0E14 + 0E35 |
👭🏻👰🏿♂️ 1F46D 1F3FB 1F470 1F3FF 200D 2642 FE0F | 👭🏻 + 👰🏿♂️ 1F46D 1F3FB + 1F470 1F3FF 200D 2642 FE0F | 👭 + 🏻 + 👰 + 🏿 + + ♂ + ️ 1F46D + 1F3FB + 1F470 + 1F3FF + 200D + 2642 + FE0F |
Синопсис grapheme_str_split
Функция grapheme_str_split
аналогична функции mb_str_split
и поддерживает указание параметра int $length
для определения длины каждого чанка. Если длина больше, чем длина всей строки или фрагмента графем, то будет возвращена строка/чанк.
Передача пустой строки возвращает пустой массив.
/**
* Splits a string into an array of individual or chunks of graphemes.
*
* @param string $string The string to split into individual graphemes
* or chunks of graphemes.
* @param int $length If specified, each element of the returned array
* will be composed of multiple graphemes instead of a single
* graphemes.
*
* @return array|false
*/
function grapheme_str_split(string $string, int $length = 1): array|false {}
Примеры использования grapheme_str_split
grapheme_str_split("PHP");
// ["P", "H", "P"]
grapheme_str_split("你好");
// ["你", "好"]
grapheme_str_split("你好", length: 4);
// ["你好"]
grapheme_str_split("สวัสดี");
// ["ส", "วั", "ส", "ดี"]
grapheme_str_split("අයේෂ්");
// ["අ", "යේ", "ෂ්"]
grapheme_str_split("👭🏻👰🏿♂️");
// ["👭🏻", "👰🏿♂️"]
Влияние на обратную совместимость
Новая функция grapheme_str_split
является новой в расширении Intl и объявлена в глобальное пространство имён. Если нет существующей функции с таким же именем, это изменение не должно вызвать проблем с обратной совместимостью.
Полифилл grapheme_str_split
Существует возможность создать полифилл функции grapheme_str_split
с помощью регулярных выражений Unicode. Селектор /\X/
соответствует полной графеме и может быть использован в качестве основы полифилла.
Обратите внимание, что в приведённом ниже полифилле используется регулярное выражение \X
, соответствующее полной графеме. Однако оно не позволяет правильно разделять сложные Эмодзи, такие, как Эмодзи с модификаторами кожи.
/**
* Splits a string into an array of individual or chunks of graphemes.
*
* @param string $string The string to split into individual graphemes
* or chunks of graphemes.
* @param int $length If specified, each element of the returned array
* will be composed of multiple graphemes instead of a single
* graphemes.
*
* @return array|false
*/
function grapheme_str_split(string $string, int $length = 1): array|false {
if ($length < 0 || $length > 1073741823) {
throw new \ValueError('grapheme_str_split(): Argument #2 ($length) must be greater than 0 and less than or equal to 1073741823.');
}
if ($string === '') {
return [];
}
preg_match_all('/\X/u', $string, $matches);
if (empty($matches[0])) {
return false;
}
if ($length === 1) {
return $matches[0];
}
$chunks = array_chunk($matches[0], $length);
array_walk($chunks, static function(&$value) {
$value = implode('', $value);
});
return $chunks;
}