/** * Tests UTF8::str_split * * @test * @dataProvider provider_str_split */ public function test_str_split($input, $split_length, $expected) { $this->assertSame($expected, UTF8::str_split($input, $split_length)); }
/** * Generates a random string of a given type and length. * * @param string a type of pool, or a string of characters to use as the pool * @param integer length of string to return * @return string * * @tutorial alnum alpha-numeric characters * @tutorial alpha alphabetical characters * @tutorial hexdec hexadecimal characters, 0-9 plus a-f * @tutorial numeric digit characters, 0-9 * @tutorial nozero digit characters, 1-9 * @tutorial distinct clearly distinct alpha-numeric characters */ public static function random($type = 'alnum', $length = 8) { $utf8 = FALSE; switch ($type) { 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; default: $pool = (string) $type; $utf8 = !UTF8::is_ascii($pool); break; } // Split the pool into an array of characters $pool = $utf8 === TRUE ? UTF8::str_split($pool, 1) : 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; }
public static function string($length, $charlist = null) { if (!(ctype_digit(@(string) $length) && ($length = (int) $length) > 0)) { throw new InvalidArgumentException('Random::string(): A positive integer value as parameter is expected.'); } if (empty($charlist)) { return substr(rtrim(base64_encode(self::bytes(ceil($length * 0.75))), '='), 0, $length); } if (!is_array($charlist)) { if (IS_UTF8_CHARSET) { $charlist = UTF8::str_split(@(string) $charlist); } else { $charlist = str_split(@(string) $charlist); } } $charlist_length = count($charlist); if ($charlist_length == 1) { return str_repeat($charlist[0], $length); } $bytes = self::bytes($length); $pos = 0; $result = array(); for ($i = 0; $i < $length; $i++) { $pos = ($pos + ord($bytes[$i])) % $charlist_length; $result[$i] = $charlist[$pos]; } return implode('', $result); }
/** * Make regular expression for case insensitive match * Example (non ASCII): "123_слово_test" => "123_(с|С)(л|Л)(о|О)(в|В)(о|О)_[tT][eE][sS][tT]" * Example (only ASCII): "123_test" => "(?i:123_test)" * * @param string $s * @param string|null $delimiter If the optional delimiter is specified, it will also be escaped. * This is useful for escaping the delimiter that is required by the PCRE functions. * The / is the most commonly used delimiter. * @return string|bool|null Returns FALSE if error occurred */ public static function preg_quote_case_insensitive($s, $delimiter = null) { if (!ReflectionTypeHint::isValid()) { return false; } if (is_null($s)) { return $s; } if (self::is_ascii($s)) { return '(?i:' . preg_quote($s, $delimiter) . ')'; } #speed improve $s_re = ''; $s_lc = UTF8::lowercase($s); if ($s_lc === false) { return false; } $s_uc = UTF8::uppercase($s); if ($s_uc === false) { return false; } $chars_lc = UTF8::str_split($s_lc); if ($chars_lc === false) { return false; } $chars_uc = UTF8::str_split($s_uc); if ($chars_uc === false) { return false; } foreach ($chars_lc as $i => $char) { if ($chars_lc[$i] === $chars_uc[$i]) { $s_re .= preg_quote($chars_lc[$i], $delimiter); } elseif (self::is_ascii($chars_lc[$i])) { $s_re .= '[' . preg_quote($chars_lc[$i] . $chars_uc[$i], $delimiter) . ']'; } else { $s_re .= '(' . preg_quote($chars_lc[$i], $delimiter) . '|' . preg_quote($chars_uc[$i], $delimiter) . ')'; } } return $s_re; }
/** * "Подсветка" найденных слов для результатов поисковых систем. * Ищет все вхождения цифр или целых слов в html коде и обрамляет их заданными тэгами. * Текст должен быть в кодировке UTF-8. * * @param string|null $s Текст, в котором искать * @param array|null $words Массив поисковых слов * @param bool $is_case_sensitive Искать с учётом от регистра? * @param string $tpl HTML шаблон для замены * @return string|bool|null returns FALSE if error occured */ public static function words_highlight($s, array $words = null, $is_case_sensitive = false, $tpl = '<span class="highlight">%s</span>') { if (!ReflectionTypeHint::isValid()) { return false; } if (is_null($s)) { return $s; } #оптимизация для пустых значений if (!strlen($s) || !$words) { return $s; } #оптимизация #{{{ $s2 = UTF8::lowercase($s); foreach ($words as $k => $word) { $word = UTF8::lowercase(trim($word, ".. *")); if ($word == '' || strpos($s2, $word) === false) { unset($words[$k]); } } if (!$words) { return $s; } #}}} #d($words); #кэширование построения рег. выражения для "подсвечивания" слов в функции при повторных вызовах static $func_cache = array(); $cache_id = md5(serialize(array($words, $is_case_sensitive, $tpl))); if (!array_key_exists($cache_id, $func_cache)) { $re_words = array(); foreach ($words as $word) { $is_mask = substr($word, -1) === '*'; if ($is_mask) { $word = rtrim($word, '*'); } $is_digit = ctype_digit($word); #рег. выражение для поиска слова с учётом регистра или цифр: $re_word = preg_quote($word, '~'); #рег. выражение для поиска слова НЕЗАВИСИМО от регистра: if (!$is_case_sensitive && !$is_digit) { if (UTF8::is_ascii($word)) { $re_word = '(?i:' . $re_word . ')'; } else { $lc = UTF8::str_split(UTF8::lowercase($re_word)); $uc = UTF8::str_split(UTF8::uppercase($re_word)); $re_word = array(); foreach ($lc as $i => $tmp) { $re_word[] = '[' . $lc[$i] . $uc[$i] . ']'; } $re_word = implode('', $re_word); } } #d($re_word); if ($is_digit) { $append = $is_mask ? '\\d*+' : '(?!\\d)'; } else { $append = $is_mask ? '\\p{L}*+' : '(?!\\p{L})'; } $re_words[$is_digit ? 'digits' : 'words'][] = $re_word . $append; } if (array_key_exists('words', $re_words) && $re_words['words']) { #поиск вхождения слова: $re_words['words'] = '(?<!\\p{L}) #просмотр назад (\\b не подходит и работает медленнее) (?:' . implode(PHP_EOL . '| ', $re_words['words']) . ') '; } if (array_key_exists('digits', $re_words) && $re_words['digits']) { #поиск вхождения цифры: $re_words['digits'] = '(?<!\\d) #просмотр назад (\\b не подходит и работает медленнее) (?:' . implode(PHP_EOL . '| ', $re_words['digits']) . ') '; } #d(implode(PHP_EOL . '| ', $re_words)); $func_cache[$cache_id] = '~(?> #встроенный PHP, Perl, ASP код <([\\?\\%]) .*? \\1> \\K #блоки CDATA | <\\!\\[CDATA\\[ .*? \\]\\]> \\K #MS Word тэги типа "<![if! vml]>...<![endif]>", #условное выполнение кода для IE типа "<!--[if lt IE 7]>...<![endif]-->": | <\\! (?>--)? \\[ (?> [^\\]"\']+ | "[^"]*" | \'[^\']*\' )* \\] (?>--)? > \\K #комментарии | <\\!-- .*? --> \\K #парные тэги вместе с содержимым | <((?i:noindex|script|style|comment|button|map|iframe|frameset|object|applet))' . self::$re_attrs . '(?<!/)> .*? </(?i:\\2)> \\K #парные и непарные тэги | <[/\\!]?+[a-zA-Z][a-zA-Z\\d]*+' . self::$re_attrs . '> \\K #html сущности (< > &) (+ корректно обрабатываем код типа &amp;nbsp;) | &(?> [a-zA-Z][a-zA-Z\\d]++ | \\#(?> \\d{1,4}+ | x[\\da-fA-F]{2,4}+ ) ); \\K | ' . implode(PHP_EOL . '| ', $re_words) . ' ) ~suxSX'; #d($func_cache[$cache_id]); } $s = preg_replace_callback($func_cache[$cache_id], function (array $m) use($tpl) { return $m[0] !== '' ? sprintf($tpl, $m[0]) : $m[0]; }, $s); return $s; }