mirror of
https://github.com/avecms/AVE.cms.git
synced 2026-08-01 08:45:44 +00:00
412 lines
12 KiB
PHP
412 lines
12 KiB
PHP
<?php
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------------------
|
|
| AVE.cms
|
|
|--------------------------------------------------------------------------------------
|
|
| @package AVE.cms
|
|
| @file system/App/Helpers/Str.php
|
|
| @author AVE.cms <support@ave-cms.ru>
|
|
| @copyright 2007-2026 (c) AVE.cms
|
|
| @link https://ave-cms.ru
|
|
| @version 3.3
|
|
*/
|
|
|
|
namespace App\Helpers;
|
|
|
|
defined('BASEPATH') || die('Direct access to this location is not allowed.');
|
|
|
|
/**
|
|
* Хелпер для работы со строками (UTF-8).
|
|
* Все методы работают с многобайтовыми строками через mbstring.
|
|
*/
|
|
class Str
|
|
{
|
|
protected function __construct()
|
|
{
|
|
//
|
|
}
|
|
|
|
// ------------------------------------------------------------------ //
|
|
// Базовые операции
|
|
// ------------------------------------------------------------------ //
|
|
|
|
public static function length($str)
|
|
{
|
|
return mb_strlen((string)$str, 'UTF-8');
|
|
}
|
|
|
|
public static function upper($str)
|
|
{
|
|
return mb_strtoupper((string)$str, 'UTF-8');
|
|
}
|
|
|
|
public static function lower($str)
|
|
{
|
|
return mb_strtolower((string)$str, 'UTF-8');
|
|
}
|
|
|
|
/** Первый символ в верхний регистр (UTF-8) */
|
|
public static function ucfirst($str)
|
|
{
|
|
$str = (string)$str;
|
|
if ($str === '') return '';
|
|
return mb_strtoupper(mb_substr($str, 0, 1, 'UTF-8'), 'UTF-8') . mb_substr($str, 1, null, 'UTF-8');
|
|
}
|
|
|
|
/** Каждое слово с большой буквы */
|
|
public static function title($str)
|
|
{
|
|
return mb_convert_case((string)$str, MB_CASE_TITLE, 'UTF-8');
|
|
}
|
|
|
|
public static function trim($str, $characters = null)
|
|
{
|
|
return $characters !== null ? trim((string)$str, $characters) : trim((string)$str);
|
|
}
|
|
|
|
/** Вернуть trimmed string или NULL, если строка пустая */
|
|
public static function nullIfEmpty($str)
|
|
{
|
|
$str = trim((string)$str);
|
|
return $str === '' ? null : $str;
|
|
}
|
|
|
|
public static function ltrim($str, $characters = null)
|
|
{
|
|
return $characters !== null ? ltrim((string)$str, $characters) : ltrim((string)$str);
|
|
}
|
|
|
|
public static function rtrim($str, $characters = null)
|
|
{
|
|
return $characters !== null ? rtrim((string)$str, $characters) : rtrim((string)$str);
|
|
}
|
|
|
|
public static function substr($str, $start, $length = null)
|
|
{
|
|
return $length !== null
|
|
? mb_substr((string)$str, $start, $length, 'UTF-8')
|
|
: mb_substr((string)$str, $start, null, 'UTF-8');
|
|
}
|
|
|
|
public static function pos($haystack, $needle, $offset = 0)
|
|
{
|
|
return mb_strpos((string)$haystack, (string)$needle, $offset, 'UTF-8');
|
|
}
|
|
|
|
public static function replace($search, $replace, $subject)
|
|
{
|
|
return str_replace($search, $replace, $subject);
|
|
}
|
|
|
|
public static function pad($str, $length, $pad = ' ', $type = STR_PAD_RIGHT)
|
|
{
|
|
return str_pad((string)$str, $length, $pad, $type);
|
|
}
|
|
|
|
public static function repeat($str, $times)
|
|
{
|
|
return str_repeat((string)$str, max(0, (int)$times));
|
|
}
|
|
|
|
// ------------------------------------------------------------------ //
|
|
// Поиск и проверки
|
|
// ------------------------------------------------------------------ //
|
|
|
|
/** Содержит ли строка подстроку */
|
|
public static function contains($haystack, $needle)
|
|
{
|
|
return mb_strpos((string)$haystack, (string)$needle, 0, 'UTF-8') !== false;
|
|
}
|
|
|
|
/** Начинается ли строка с prefix */
|
|
public static function startsWith($str, $prefix)
|
|
{
|
|
$prefix = (string)$prefix;
|
|
return $prefix !== '' && mb_substr((string)$str, 0, mb_strlen($prefix, 'UTF-8'), 'UTF-8') === $prefix;
|
|
}
|
|
|
|
/** Заканчивается ли строка на suffix */
|
|
public static function endsWith($str, $suffix)
|
|
{
|
|
$suffix = (string)$suffix;
|
|
if ($suffix === '') return true;
|
|
$str = (string)$str;
|
|
return mb_substr($str, -mb_strlen($suffix, 'UTF-8'), null, 'UTF-8') === $suffix;
|
|
}
|
|
|
|
/** Соответствует ли строка паттерну (wildcards: * и ?) */
|
|
public static function is($pattern, $value)
|
|
{
|
|
$pattern = preg_quote((string)$pattern, '#');
|
|
$pattern = str_replace(['\*', '\?'], ['.*', '.'], $pattern);
|
|
return (bool)preg_match('#^' . $pattern . '$#u', (string)$value);
|
|
}
|
|
|
|
// ------------------------------------------------------------------ //
|
|
// Трансформации
|
|
// ------------------------------------------------------------------ //
|
|
|
|
/**
|
|
* Ограничить строку по количеству символов.
|
|
* Добавляет суффикс если строка обрезана.
|
|
*/
|
|
public static function limit($str, $limit = 100, $end = '...')
|
|
{
|
|
$str = (string)$str;
|
|
if (mb_strlen($str, 'UTF-8') <= $limit) {
|
|
return $str;
|
|
}
|
|
|
|
return mb_substr($str, 0, $limit, 'UTF-8') . $end;
|
|
}
|
|
|
|
/**
|
|
* Ограничить строку по количеству слов.
|
|
*/
|
|
public static function words($str, $words = 10, $end = '...')
|
|
{
|
|
$str = (string)$str;
|
|
$wArr = preg_split('/\s+/u', trim($str), -1, PREG_SPLIT_NO_EMPTY);
|
|
|
|
if (count($wArr) <= $words) {
|
|
return $str;
|
|
}
|
|
|
|
return implode(' ', array_slice($wArr, 0, $words)) . $end;
|
|
}
|
|
|
|
/** @deprecated Используй limit() */
|
|
public static function truncate($str, $length, $suffix = '...')
|
|
{
|
|
return self::limit($str, $length, $suffix);
|
|
}
|
|
|
|
/** Ограничить строку с сохранением целых слов. */
|
|
public static function truncateSmart($string, $length = 80, $suffix = '...', $breakWords = false, $middle = false)
|
|
{
|
|
$string = (string) $string;
|
|
$length = max(0, (int) $length);
|
|
if ($length === 0) { return ''; }
|
|
if (mb_strlen($string, 'UTF-8') <= $length) { return $string; }
|
|
|
|
$contentLength = $length - min($length, mb_strlen($suffix, 'UTF-8'));
|
|
if ($middle) {
|
|
$left = (int) floor($contentLength / 2);
|
|
$right = $contentLength - $left;
|
|
return mb_substr($string, 0, $left, 'UTF-8')
|
|
. $suffix
|
|
. mb_substr($string, -$right, null, 'UTF-8');
|
|
}
|
|
|
|
$value = mb_substr($string, 0, $contentLength + 1, 'UTF-8');
|
|
if (!$breakWords) {
|
|
$value = preg_replace('/\s+?(\S+)?$/u', '', $value);
|
|
}
|
|
|
|
return mb_substr($value, 0, $contentLength, 'UTF-8') . $suffix;
|
|
}
|
|
|
|
/** Ограничить текст на первом полном слове после заданной длины. */
|
|
public static function truncateText($string, $length = 100, $suffix = '…')
|
|
{
|
|
$string = (string) $string;
|
|
$length = max(0, (int) $length);
|
|
if (mb_strlen($string, 'UTF-8') <= $length) { return $string; }
|
|
|
|
$normalized = preg_replace('/\s+/u', ' ', str_replace(array("\r\n", "\r", "\n"), ' ', $string));
|
|
$out = '';
|
|
foreach (preg_split('/\s+/u', trim($normalized), -1, PREG_SPLIT_NO_EMPTY) as $word) {
|
|
$out .= ($out === '' ? '' : ' ') . $word;
|
|
if (mb_strlen($out, 'UTF-8') >= $length) {
|
|
return mb_strlen($out, 'UTF-8') === mb_strlen($normalized, 'UTF-8') ? $out : $out . $suffix;
|
|
}
|
|
}
|
|
|
|
return $normalized;
|
|
}
|
|
|
|
/**
|
|
* Маскировать часть строки.
|
|
*
|
|
* @param string $str
|
|
* @param int $start Позиция начала маски (отрицательные — от конца)
|
|
* @param int $length Длина маски (0 = до конца)
|
|
* @param string $char Символ маски
|
|
*/
|
|
public static function mask($str, $start, $length = 0, $char = '*')
|
|
{
|
|
$str = (string)$str;
|
|
$total = mb_strlen($str, 'UTF-8');
|
|
$start = $start < 0 ? max(0, $total + $start) : min($start, $total);
|
|
$length = $length > 0 ? min($length, $total - $start) : $total - $start;
|
|
|
|
return mb_substr($str, 0, $start, 'UTF-8')
|
|
. str_repeat($char, $length)
|
|
. mb_substr($str, $start + $length, null, 'UTF-8');
|
|
}
|
|
|
|
/**
|
|
* Маскировать email: jo***@example.com
|
|
*/
|
|
public static function maskEmail($email)
|
|
{
|
|
$parts = explode('@', (string)$email, 2);
|
|
if (count($parts) !== 2) return $email;
|
|
|
|
$name = $parts[0];
|
|
$len = mb_strlen($name, 'UTF-8');
|
|
|
|
if ($len <= 2) {
|
|
$masked = str_repeat('*', $len);
|
|
} else {
|
|
$visible = max(1, (int)floor($len / 3));
|
|
$masked = mb_substr($name, 0, $visible, 'UTF-8') . str_repeat('*', $len - $visible);
|
|
}
|
|
|
|
return $masked . '@' . $parts[1];
|
|
}
|
|
|
|
// ------------------------------------------------------------------ //
|
|
// Именование
|
|
// ------------------------------------------------------------------ //
|
|
|
|
/**
|
|
* snake_case → camelCase
|
|
*/
|
|
public static function camel($str)
|
|
{
|
|
return lcfirst(static::studly($str));
|
|
}
|
|
|
|
/**
|
|
* snake_case → StudlyCase (PascalCase)
|
|
*/
|
|
public static function studly($str)
|
|
{
|
|
$str = (string)$str;
|
|
return str_replace([' ', '_', '-'], '', mb_convert_case(
|
|
str_replace(['-', '_'], ' ', $str),
|
|
MB_CASE_TITLE,
|
|
'UTF-8'
|
|
));
|
|
}
|
|
|
|
/**
|
|
* camelCase / StudlyCase → snake_case
|
|
*/
|
|
public static function snake($str, $delimiter = '_')
|
|
{
|
|
$str = (string)$str;
|
|
if (!ctype_lower($str)) {
|
|
$str = preg_replace('/\s+/u', '', $str);
|
|
$str = preg_replace('/(.)(?=[A-Z])/u', '$1' . $delimiter, $str);
|
|
$str = mb_strtolower($str, 'UTF-8');
|
|
}
|
|
|
|
return $str;
|
|
}
|
|
|
|
/**
|
|
* Текст → URL-slug (кириллица транслитерируется).
|
|
* «Моя Строка» → «moya-stroka»
|
|
*/
|
|
public static function slug($str, $separator = '-')
|
|
{
|
|
$str = Locales::transliterateSlug((string) $str);
|
|
|
|
// Привести к нижнему регистру и оставить только допустимые символы
|
|
$str = mb_strtolower($str, 'UTF-8');
|
|
$str = preg_replace('/[^a-z0-9\s\-_]/u', '', $str);
|
|
$str = preg_replace('/[\s\-_]+/', $separator, $str);
|
|
return trim($str, $separator);
|
|
}
|
|
|
|
/**
|
|
* Машинный alias для admin-сущностей: латиница, цифры, _, . и дефисы.
|
|
* Не транслитерирует смысловые URL, а только чистит технические ключи.
|
|
*/
|
|
public static function machineAlias($str, $separator = '-')
|
|
{
|
|
$str = mb_strtolower(trim((string)$str), 'UTF-8');
|
|
$str = preg_replace('/[^a-z0-9_.-]+/', (string)$separator, $str);
|
|
$str = preg_replace('/' . preg_quote((string)$separator, '/') . '+/', (string)$separator, $str);
|
|
return trim($str, (string)$separator);
|
|
}
|
|
|
|
// ------------------------------------------------------------------ //
|
|
// Генерация
|
|
// ------------------------------------------------------------------ //
|
|
|
|
/**
|
|
* Сгенерировать криптографически стойкую случайную строку.
|
|
*
|
|
* @param int $length Длина строки
|
|
* @param string $chars Алфавит (по умолчанию a-z0-9)
|
|
*/
|
|
public static function random($length = 16, $chars = 'abcdefghijklmnopqrstuvwxyz0123456789')
|
|
{
|
|
$result = '';
|
|
$max = strlen($chars) - 1;
|
|
$bytesLen = (int)ceil($length * 2);
|
|
|
|
$bytes = random_bytes($bytesLen);
|
|
|
|
for ($i = 0; $i < $bytesLen && strlen($result) < $length; $i++) {
|
|
$index = ord($bytes[$i]) % ($max + 1);
|
|
$result .= $chars[$index];
|
|
}
|
|
|
|
return substr($result, 0, $length);
|
|
}
|
|
|
|
/**
|
|
* Сгенерировать UUID v4.
|
|
*/
|
|
public static function uuid()
|
|
{
|
|
$bytes = random_bytes(16);
|
|
$bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x40);
|
|
$bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80);
|
|
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($bytes), 4));
|
|
}
|
|
|
|
// ------------------------------------------------------------------ //
|
|
// Прочее
|
|
// ------------------------------------------------------------------ //
|
|
|
|
/** Безопасное сравнение строк (защита от timing-атак) */
|
|
public static function equals($a, $b)
|
|
{
|
|
return hash_equals((string)$a, (string)$b);
|
|
}
|
|
|
|
/** SHA-256 hash строки */
|
|
public static function sha256($str)
|
|
{
|
|
return hash('sha256', (string)$str);
|
|
}
|
|
|
|
/** Удалить теги и декодировать HTML-сущности */
|
|
public static function stripTags($str, $allowedTags = '')
|
|
{
|
|
return html_entity_decode(strip_tags((string)$str, $allowedTags), ENT_QUOTES, 'UTF-8');
|
|
}
|
|
|
|
/** Экранировать для вывода в HTML */
|
|
public static function escape($str)
|
|
{
|
|
return htmlspecialchars((string)$str, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
|
}
|
|
|
|
protected function __clone()
|
|
{
|
|
//
|
|
}
|
|
|
|
protected function __wakeup()
|
|
{
|
|
//
|
|
}
|
|
}
|