/
v.bolshakov
/
AIEcosystem-Testing
Обзор
Документация
Войти
/
v.bolshakov
/
AIEcosystem-Testing
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
dev
common/helpers/TextHelper.php
523 строки
17 KB
Developer
Initial commit
03 авг 2026, 17:43
03 авг 2026, 17:43
7433917
Код
Авторство
О чём код?
<?php namespace common\helpers; use Yii; use yii\base\InvalidCallException; /** * Вспомогательные функции для работы с текстом * * @author Dmitry E. Semenov <sde.tomsk@gmail.com> * @copyright Self (c) 2019 */ class TextHelper { /** * Нормализация перевода строк и убирание дублирующих пробелов * * @param string $str Строка, которую надо нормализовать * @return string Нормализованная строка */ public static function normalizeWhitespace($str) { $str = trim($str); $str = str_replace(["\r", "\n"], '', $str); $str = preg_replace(array('/\n+/', '/[ \t]+/'), array("\n", ' '), $str); return $str; } /** * Generates a random string of a given type and length. * * $str = TextHelper::random(); // 8 character random string * * @param string $type a type of pool, or a string of characters to use as the pool * @param integer $length length of string to return * @return string */ public static function random($type = null, $length = 8) { if ($type === null) { // Default is to generate an alphanumeric string $type = 'alnum'; } switch ($type) { default: case 'alnum': $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 'alpha': $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; break; case 'hexdec': $pool = '0123456789abcdef'; break; case 'numeric': $pool = '0123456789'; break; case 'nozero': $pool = '123456789'; break; case 'distinct': $pool = '2345679ACDEFHJKLMNPRSTUVWXYZ'; break; } // Split the pool into an array of characters $pool = str_split($pool, 1); // Largest pool key $max = count($pool) - 1; $str = ''; for ($i = 0; $i < $length; $i++) { // Select a random character from the pool and add it to the string $str .= $pool[mt_rand(0, $max)]; } // Make sure alnum strings contain at least one letter and one digit if ($type === 'alnum' and $length > 1) { if (ctype_alpha($str)) { // Add a random digit $str[mt_rand(0, $length - 1)] = chr(mt_rand(48, 57)); } elseif (ctype_digit($str)) { // Add a random letter $str[mt_rand(0, $length - 1)] = chr(mt_rand(65, 90)); } } return $str; } /** * Проверить - что строка пустая * @param $string * @return bool */ public static function isEmpty($string) { return strlen(trim($string)) == 0; } /** * Appends a trailing slash. */ public static function trailingslashit($string) { return self::untrailingslashit($string) . '/'; } /** * Removes trailing slash if it exists. */ public static function untrailingslashit($string, $right = true) { if ($right) { return rtrim($string, '\\/'); } else { return ltrim($string, '\\/'); } } /** * Replace repeated white spaces to single space * * @param string $string * * @return string */ public static function clearWhitespaces($string, $replacement = ' ') { return trim(preg_replace('/\s+/s', $replacement, $string)); } /** * Разбиваем заголовок на две части для "красивой сортировки" * * @param $title * @return array */ public static function explodeTitle($title) { $array = str_split($title); $number = ''; $pos = 0; $zero_cnt = 0; $from_start = true; foreach ($array as $item) { if (in_array($item, ['-', '_', '.', ':'])) { $pos++; continue; } if (is_numeric($item)) { $number .= $item; $pos++; } else { break; } if ($item == '0' and $from_start) { $zero_cnt++; } else { $from_start = false; } } $digits = explode(' ', preg_replace('~\D+~', ' ', $title)); if (is_numeric($number)) { $direction = 1; $string = TextHelper::clearWhitespaces(substr($title, $pos)); } else { $direction = 0; $number = ArrayHelper::getValue($digits, count($digits) - 1, 0); $string = TextHelper::clearWhitespaces(preg_replace('/\d/', '', $title), ''); } $float = '1' . str_pad($number, 10 - $zero_cnt, '0', STR_PAD_LEFT); return [ 'direction' => $direction, 'string' => $string, 'number' => $float / 1000000, ]; } /** * Убираем из utf-8 BOM * @param $text * @return string|string[]|null */ public static function removeUtf8Bom($text) { $bom = pack('H*', 'EFBBBF'); $text = preg_replace("/^$bom/", '', $text); return $text; } /** * Убираем из utf-8 Префикс * @param $text * @return string */ public static function removeUtf8Prefix($text) { return str_replace("77u/", '', $text); } /** * Вернуть слова учитывая нумирацию * @param int $number - номер для учета окончания * @param array $array - массив окончаний * 1 стол * 2 стола * 10 столов * * @return string */ public static function rusEnd($number, $array = array(), $append = true) { if (count($array) < 3) { throw new InvalidCallException('Массив "$array" инициализирован не верно'); } if (ceil($number) != $number) { $d_number = substr($number, -1); } else { $d_number = $number; } $amount = abs($d_number); $mod10 = $amount % 10; $mod100 = $amount % 100; if ($mod10 == 1 && $mod100 != 11) { $variant = 0; } elseif ($mod10 >= 2 && $mod10 <= 4 && !($mod100 > 10 && $mod100 < 20)) { $variant = 1; } else { $variant = 2; } if ($append) { return NumHelper::toString($number, 2) . ' ' . Yii::t('app', $array[$variant]); } else { return Yii::t('app', $array[$variant]); } } /** * Перевод из Unicode последовательности * * @param string $str Исходная строка * @param mixed $encoding Кодировка * @return mixed */ public static function fromUnistr($str, $encoding = 'UTF-8') { if (null == $encoding) { $encoding = ini_get('mbstring.internal_encoding'); } return preg_replace_callback('/\\\([0-9a-fA-F]{4})/u', function ($match) use ($encoding) { return mb_convert_encoding(pack('H*', $match[1]), $encoding, 'UTF-16BE'); }, $str); } /** * Перевод строки в Unicode последовательность * * @param $str * @return mixed */ public static function toUnistr($str) { $result = ''; if ($str = Utf8Helper::trim($str)) { $array = Utf8Helper::to_unicode($str); foreach ($array as $letter) { // 32-127 - диапазон ASCII // 1040-1105 диапазон русских букв if (!(($letter >= 32 and $letter <= 127) or ($letter >= 1040 and $letter <= 1105))) { $result .= '\\' . sprintf("%04s", dechex($letter)); } else { $result .= Utf8Helper::from_unicode([$letter]); } } } return $result; } /** * Преобразовать цвет к классу цвета * * @param $color * @return null|string */ public static function toColor($color) { switch ($color) { case 1: return 'danger'; break; case 2: return 'success'; break; case 3: return 'info'; break; case 4: return 'warning'; break; default: return null; } } /** * Перевод строки в транслит * @param string $string Исходная строка * * @return string возвращает строку в транслите */ public static function translit($string, $lower = true, $replace = true) { static $converter = array( 'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'е' => 'e', 'ё' => 'yo', 'ж' => 'zh', 'з' => 'z', 'и' => 'i', 'й' => 'y', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n', 'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't', 'у' => 'u', 'ф' => 'f', 'х' => 'kh', 'ц' => 'ts', 'ч' => 'ch', 'ш' => 'sh', 'щ' => 'shch', 'ь' => '', 'ы' => 'y', 'ъ' => '', 'э' => 'e', 'ю' => 'yu', 'я' => 'ya', 'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D', 'Е' => 'E', 'Ё' => 'Yo', 'Ж' => 'Zh', 'З' => 'Z', 'И' => 'I', 'Й' => 'I', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N', 'О' => 'O', 'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T', 'У' => 'U', 'Ф' => 'F', 'Х' => 'Kh', 'Ц' => 'Ts', 'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Shch', 'Ь' => '', 'Ы' => 'Y', 'Ъ' => '', 'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya', ); $str = strtr($string, $converter); if ($lower) { $str = strtolower($str); } if ($replace) { $str = preg_replace('~[^-a-zA-Z0-9_]+~u', '-', $str); } return trim($str, "-"); } /** * Очищаем текст от HTML * * @param $text * @return string */ public static function clear($text, $allowable_tags = null) { return self::normalizeWhitespace(strip_tags($text, $allowable_tags)); } /** * XSS filter * * This was built from numerous sources * (thanks all, sorry I didn't track to credit you) * * It was tested against *most* exploits here: http://ha.ckers.org/xss.html * WARNING: Some weren't tested!!! * Those include the Actionscript and SSI samples, or any newer than Jan 2011 */ public static function cleanXss($data) { // Fix &entity\n; $data = str_replace(array('&', '<', '>'), array('&amp;', '&lt;', '&gt;'), $data); $data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data); $data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data); $data = html_entity_decode($data, ENT_COMPAT, 'UTF-8'); // Remove any attribute starting with "on" or xmlns $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data); // Remove javascript: and vbscript: protocols $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2nojavascript...', $data); $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu', '$1=$2novbscript...', $data); $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u', '$1=$2nomozbinding...', $data); // Only works in IE: <span style="width: expression(alert('Ping!'));"></span> $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i', '$1>', $data); $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i', '$1>', $data); $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu', '$1>', $data); // Remove namespaced elements (we do not need them) $data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data); do { // Remove really unwanted tags $old_data = $data; $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i', '', $data); } while ($old_data !== $data); // we are done... return $data; } /** * Подсчёт количество слов * @param $text * @return int */ public static function countWordsWithFilter($text) { // 1. Удаляем всё, что заключено в <...>, {{...}}, {...}, [...] (включая вложенные) // Для простоты используем рекурсивное удаление регуляркой, но аккуратно — сначала теги, потом скобки $text = preg_replace('/<[^>]*>/', ' ', $text); // HTML теги $text = preg_replace('/\{\{[^}]*\}\}/', ' ', $text); // {{...}} $text = preg_replace('/\{[^{}]*\}/', ' ', $text); // {...} $text = preg_replace('/\[[^\[\]]*\]/', ' ', $text); // [...] // 2. Удаляем URL-ссылки (http://, https://, ftp://, www.) $text = preg_replace('/https?:\/\/\S+/i', ' ', $text); $text = preg_replace('/ftp:\/\/\S+/i', ' ', $text); $text = preg_replace('/www\.\S+/i', ' ', $text); // 3. Заменяем служебные символы и знаки препинания на пробелы $text = preg_replace('/[[:punct:]]/', ' ', $text); //$text = preg_replace('/[.,!?;:()\[\]{}\'\"\-]/', ' ', $text); // 4. Удаляем управляющие/невидимые символы: \n, \t, \r, \0 и пр. $text = preg_replace('/[\n\r\t\0\x0B]/', ' ', $text); // 5. Дополнительно убираем любые не-буквенно-цифровые разделители, // но оставляем дефис внутри слов (optional — по желанию) $text = preg_replace('/[^\p{L}\p{N}\'-]+/u', ' ', $text); // 6. Приводим к нижнему регистру (если нужен регистронезависимый подсчёт) //$text = Utf8Helper::strtolower($text); // 7. Разбиваем на слова и считаем только значимые (длина >= 1) $words = preg_split('/\s+/u', trim($text)); $words = array_filter($words, fn($w) => mb_strlen($w) > 0); return count($words); } /** * Убираем специальные символы перед обработкой текста * @param $ref * @return string */ public static function distanceClear($ref) { return Utf8Helper::strtolower(preg_replace('/<[^>]+>|\{[^}]*\}|\xa0/', '', (string)$ref)); } }