корректировать ключи для хранения в битовом поле * @return void */ public static function sortByPriority(array &$data, string $key = 'priority', $preserveKeys = false) { if (empty($data)) { return; } if ($preserveKeys === 2) { if (sizeof($data) > 32) { $data = array_slice($data, 0, 32, true); } } $order = []; $i = 1; foreach ($data as $k => &$v) { $order[$k] = (!empty($v[$key]) ? $v[$key] : $i++); } unset($v); if ($preserveKeys) { if ($preserveKeys === 2) { $data2 = []; $last = 0; foreach ($data as $k => $v) { if (($k > 1 && ($k % 2)) || substr_count(decbin($k), '1') > 1) { for ($i = $last; $i <= 32; $i++) { $j = pow(2, $i); if (!isset($data[$j]) && !isset($data2[$j])) { $k = $j; $last = $i; break; } } } $data2[$k] = $v; } $data = $data2; } $keys = array_keys($data); array_multisort($order, SORT_ASC, SORT_NUMERIC, $data, $keys); $data = array_combine($keys, $data); } else { array_multisort($order, SORT_ASC, $data); } } /** * Парсинг даты/времени * @param string $datetime дата/время * @return array */ public static function parse_datetime(string $datetime = '2006-04-05 01:50:00'): array { $arr = explode(' ', $datetime, 2); $result = ['year' => '', 'month' => '', 'day' => '', 'hour' => '', 'min' => '', 'sec' => '']; if (isset($arr[0])) { $date = explode('-', $arr[0], 3); if (count($date) == 3) { $result['year'] = $date[0]; $result['month'] = $date[1]; $result['day'] = $date[2]; } } if (isset($arr[1])) { $time = explode(':', $arr[1], 3); if (count($time) == 3) { $result['hour'] = $time[0]; $result['min'] = $time[1]; $result['sec'] = $time[2]; } } return $result; } /** * Генератор случайной последовательности символов * @param int $length кол-во символов (1-32) * @param bool $numbersOnly только числа * @return string */ public static function generator(int $length = 10, bool $numbersOnly = false): string { if ($length > 32) { $length = 32; } if ($numbersOnly) { return mt_rand(($length > 1 ? pow(10, $length - 1) : 1), ($length > 1 ? pow(10, $length) - 1 : 9)); } return substr(md5(uniqid(mt_rand(), true)), 0, $length); } /** * Генератор случайной буквенно-числовой последовательности * @param int $length кол-во символов * @param string $letters строка разрешенных символов для генерации * @param callable|null $checkExist функция для проверки сгенерированного кода на совпадение с уже раннее созданным * function ($result - сгенерированный код) { * return true; - код существует, false - сгенерировать новый * } * @param int $attempts максимальное количество попыток для генерации кода * @return bool|string */ public static function generatorLetters( int $length = 10, string $letters = '', callable $checkExist = null, int $attempts = 5 ) { if (empty($letters)) { $letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; } $max = mb_strlen($letters) - 1; $i = 0; $exist = false; do { $result = ''; for ($j = 0; $j < $length; $j++) { $result .= mb_substr($letters, rand(0, $max), 1); } if ($checkExist) { $exist = call_user_func($checkExist, $result); } if ($i++ > $attempts) { return false; } } while ($exist); return $result; } /** * Транслитерация cyr->lat * @param string $text текст для транслитерации * @param bool $isURL адаптировать для URL * @param string|null $in кодировка входящий строки * @param string|null $out кодировка выходящий строки * @return string */ public static function translit(string $text, bool $isURL = true, ?string $in = null, ?string $out = null): string { if (empty($in)) { $in = 'utf-8'; } if (empty($out)) { $out = 'utf-8'; } if ($in === 'utf-8') { TextParser::cleanUtf8($text); } $text = iconv($in, 'utf-8', $text); $convert = bff::filter('utils.func.translit.convert', [ # Russian (RU), Ukrainian (UK) 'Щ' => 'Shh','Ш' => 'Sh','Ч' => 'Ch','Ц' => 'C','Ю' => 'Ju','Я' => 'Ja','Ж' => 'Zh','А' => 'A','Б' => 'B', 'В' => 'V','Г' => 'G','Д' => 'D','Е' => 'Je','Ё' => 'Jo','З' => 'Z','И' => 'I','І' => 'I','Й' => 'J', 'К' => 'K','Л' => 'L','М' => 'M','Н' => 'N','О' => 'O','П' => 'P','Р' => 'R','С' => 'S','Т' => 'T', 'У' => 'U','Ф' => 'F','Х' => 'Kh','Ь' => '\'','Ы' => 'Y','Ъ' => '`','Э' => 'E','Є' => 'Je','Ї' => 'Ji', 'щ' => 'shh','ш' => 'sh','ч' => 'ch','ц' => 'c','ю' => 'ju','я' => 'ja','ж' => 'zh','а' => 'a','б' => 'b', 'в' => 'v','г' => 'g','д' => 'd','е' => 'je','ё' => 'jo','з' => 'z','и' => 'i','і' => 'i','й' => 'j', 'к' => 'k','л' => 'l','м' => 'm','н' => 'n','о' => 'o','п' => 'p','р' => 'r','с' => 's','т' => 't', 'у' => 'u','ф' => 'f','х' => 'kh','ь' => '\'','ы' => 'y','ъ' => '`','э' => 'e','є' => 'je','ї' => 'ji', # Georgian (KA) 'ა' => 'a','ბ' => 'b','გ' => 'g','დ' => 'd','ე' => 'e','ვ' => 'v','ზ' => 'z','თ' => 't', 'ი' => 'i','კ' => 'k','ლ' => 'l','მ' => 'm','ნ' => 'n','ო' => 'o','პ' => 'p\'','ჟ' => 'zh', 'რ' => 'r','ს' => 's','ტ' => 't\'','უ' => 'u','ფ' => 'p','ქ' => 'q','ღ' => 'gh','ყ' => 'y\'', 'შ' => 'sh','ჩ' => 'ch','ც' => 'c','ძ' => 'dz','წ' => 'w\'','ჭ' => 'ch\'','ხ' => 'x','ჯ' => 'j', 'ჰ' => 'h', ]); $textBefore = $text; $text = str_replace( array_keys($convert), array_values($convert), $text ); if ($text !== $textBefore) { $text = preg_replace("/([qwrtpsdfghklzxcvbnmQWRTPSDFGHKLZXCVBNM]+)[jJ]e/", "\${1}e", $text); $text = preg_replace("/([qwrtpsdfghklzxcvbnmQWRTPSDFGHKLZXCVBNM]+)[jJ]/", "\${1}'", $text); $text = preg_replace("/([eyuioaEYUIOA]+)[Kk]h/", "\${1}h", $text); $text = preg_replace("/^kh/", "h", $text); $text = preg_replace("/^Kh/", "H", $text); } if ($isURL) { $text = TextParser::toASCII($text); $text = preg_replace('/[\?&\']+/', '', $text); $text = preg_replace('/[\s,\?&]+/', '-', $text); $text = preg_replace('/[\/\'\"\(\)\=\\\]+/', '', $text); $text = preg_replace('/[^a-zA-Z0-9_\-]/', '', $text); $text = preg_replace("/\-+/", "-", $text); //сжимаем двойные "-" } return bff::filter('utils.func.translit', iconv('utf-8', $out, $text)); } /** * Формирование JSON * @param mixed $data данные * @param array $opts [ * bool|string 'escape' - эскейпим строку: 'html' (true), 'js', false * bool 'numericCheck' - не обворачивать число в кавычки * ] * @return string */ public static function php2js($data, $opts = []): string { if (! is_array($opts)) { # for backward compability $opts = ['numericCheck' => !empty($opts)]; } $opts = array_merge([ 'escape' => false, 'numericCheck' => false, 'asObject' => false, ], $opts); $jsonOptions = JSON_UNESCAPED_UNICODE; if ($opts['numericCheck']) { $jsonOptions += JSON_NUMERIC_CHECK; } if ($opts['asObject']) { $jsonOptions += JSON_FORCE_OBJECT; } return HTML::escape(json_encode($data, $jsonOptions), $opts['escape']); } /** * Вставка необходимой строки в текст с учетом позиции найденной строки * @param string $text текст * @param string $search строка поиска * @param string $insert строка для вставки * @param bool $after после найденной строки (true), перед (false) * @return string */ public static function stringInsert(string $text, string $search, string $insert, bool $after = true): string { $index = mb_strpos($text, $search); if ($index === false) { return $text; } if ($after) { return substr_replace($text, $search . $insert, $index, mb_strlen($search)); } return mb_substr($text, 0, $index) . $insert . mb_substr($text, $index); } /** * Safe unserialize * @param mixed $data данные в сериализованном виде * @param mixed $default значение по-умолчанию * @param array $opts * @return mixed */ public static function unserialize($data, $default = [], array $opts = []) { if (is_array($default)) { if (empty($data)) { return $default; } if (is_array($data)) { return $data; } $data = strval($data); if (mb_strpos($data, 'a:') !== 0) { if (preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $data)) { $data = base64_decode($data, true); if (empty($data) || !static::isSerialized($data)) { return $default; } } else { return $default; } } $data = unserialize($data, $opts); if ($data === false) { bff::log('func::unserialize failed: ' . (new Exception())->getTraceAsString()); } return ( ! empty($data) ? $data : $default ); } return ( ! empty($data) ? unserialize(strval($data), $opts) : $default ); } /** * Проверка является ли строка сериализованной строкой * @param mixed $data * @return bool */ public static function isSerialized($data): bool { if (!is_string($data)) { return false; } $data = trim($data); if ('N;' == $data) { return true; } if ($data[1] !== ':' || mb_strlen($data) < 4) { return false; } if (!preg_match('/^([adObis]):/', $data, $matches)) { return false; } switch ($matches[1]) { case 'a': case 'O': case 's': if (preg_match("/^{$matches[1]}:[0-9]+:.*[;}]\$/s", $data)) { return true; } break; case 'b': case 'i': case 'd': if (preg_match("/^{$matches[1]}:[0-9.E-]+;\$/", $data)) { return true; } break; } return false; } }