Решение задачи (UTF-8):
<?php
header('Content-Type: text/html; charset=utf-8');
function utf8_strlen($string) {
return strlen(utf8_decode($string));
}
function utf8_substr($string, $offset, $length = null) {
// generates E_NOTICE
// for PHP4 objects, but not PHP5 objects
$string = (string)$string;
$offset = (int)$offset;
if (!is_null($length)) {
$length = (int)$length;
}
// handle trivial cases
if ($length === 0) {
return '';
}
if ($offset < 0 && $length < 0 && $length < $offset) {
return '';
}
// normalise negative offsets (we could use a tail
// anchored pattern, but they are horribly slow!)
if ($offset < 0) {
$strlen = strlen(utf8_decode($string));
$offset = $strlen + $offset;
if ($offset < 0) {
$offset = 0;
}
}
$Op = '';
$Lp = '';
// establish a pattern for offset, a
// non-captured group equal in length to offset
if ($offset > 0) {
$Ox = (int)($offset / 65535);
$Oy = $offset%65535;
if ($Ox) {
$Op = '(?:.{65535}){' . $Ox . '}';
}
$Op = '^(?:' . $Op . '.{' . $Oy . '})';
} else {
$Op = '^';
}
// establish a pattern for length
if (is_null($length)) {
$Lp = '(.*)$';
} else {
if (!isset($strlen)) {
$strlen = strlen(utf8_decode($string));
}
// another trivial case
if ($offset > $strlen) {
return '';
}
if ($length > 0) {
$length = min($strlen - $offset, $length);
$Lx = (int)($length / 65535);
$Ly = $length % 65535;
// negative length requires a captured group
// of length characters
if ($Lx) {
$Lp = '(?:.{65535}){' . $Lx . '}';
}
$Lp = '(' . $Lp . '.{' . $Ly . '})';
} elseif ($length < 0) {
if ($length < ($offset - $strlen)) {
return '';
}
$Lx = (int)((-$length) / 65535);
$Ly = (-$length)%65535;
// negative length requires ... capture everything
// except a group of -length characters
// anchored at the tail-end of the string
if ($Lx) {
$Lp = '(?:.{65535}){' . $Lx . '}';
}
$Lp = '(.*)(?:' . $Lp . '.{' . $Ly . '})$';
}
}
if (!preg_match( '#' . $Op . $Lp . '#us', $string, $match)) {
return '';
}
return $match[1];
}
function utf8_substr_replace($str, $repl, $start , $length = NULL ) {
preg_match_all('/./us', $str, $ar);
preg_match_all('/./us', $repl, $rar);
if( $length === NULL ) {
$length = utf8_strlen($str);
}
array_splice( $ar[0], $start, $length, $rar[0] );
return join('',$ar[0]);
}
function utf8_preg_match_all(
$ps_pattern,
$ps_subject,
&$pa_matches,
$pn_flags = PREG_PATTERN_ORDER,
$pn_offset = 0,
$ps_encoding = 'UTF-8'
) {
// WARNING! - All this function does is to correct offsets, nothing else:
//(code is independent of PREG_PATTER_ORDER / PREG_SET_ORDER)
$pn_offset = strlen(utf8_substr($ps_subject, 0, $pn_offset, $ps_encoding));
$ret = preg_match_all($ps_pattern, $ps_subject, $pa_matches, $pn_flags, $pn_offset);
if ($ret && ($pn_flags & PREG_OFFSET_CAPTURE)){
foreach($pa_matches as &$ha_match)
foreach($ha_match as &$ha_match)
if (isset($ha_match[1]))
$ha_match[1] = utf8_strlen(substr($ps_subject, 0, $ha_match[1]), $ps_encoding);
}
return $ret;
}
$source = 'М23[img]http://site[/img]122[img=http://site]3456789[img=http://site]33 33';
$limit = 5;
$counter = 0;
$matches = array();
echo $source . "<br>";
utf8_preg_match_all('/(?:\[.*\].*\[\/.*\])|(.)/Usiu', $source, $matches, PREG_OFFSET_CAPTURE);
foreach($matches[1] as $num=>$val) {
if(is_array($val)) {
$counter++;
if($counter == $limit) {
$source = utf8_substr_replace($source, '', $val[1] + 1);
break;
}
}
}
echo $source . "<br>";
?>
Меняя в паттерне (.) на (\x20) - будет обрезаться по словам, на (\.) - по количеству предложений
P.S. двух функций нет в хелпере utf8.php opencart- а, это utf8_substr_replace() и utf8_preg_match_all().
Почему надо было использовать utf8_preg_match_all() а не стандартный я могу объяснить кому интересно (намекну, стандартная функция смещение считает в байтах, а в UTF-8 на символ приходиться пара байт, поэтому смещение второго символа кириллицы - это 4 :ugeek: , поэтому и пришлось использовать новую пользовательскую функцию, так что аккуратнее со смещениями в UTF-8 и флага PREG_OFFSET_CAPTURE, грабли еще те).