Некоторые функции PHP (strlen, substr, а также обращение к строке как к массиву: $str[0]) не работают с многобайтовыми кодировками (вроде utf-8). В utf-8 1 символ закодирован с помощью от 1 до 6 байтов, а эти функции думают, что 1 буква всегда кодируется одним байтом. По этой причине они ломают символы, в результате получаются битые символы и ничего не работает. Потому вместо них надо использовать mb_ функции например mb_strlen, mb_substr. Вместо доступа к строке как к массиву надо использовать mb_substr.
Если тебе интересно, почему эти функции поддерживают только однобайтные кодировки, а не многобайтные, то причина в том, что они очень старые и написаны в то время (лет 40 назад) когда utf-8 и многобайтных кодировок еще не было.
Давай разберем пример. Допустим, у нас есть строка из русской буквы «щ» в кодирове utf-8. Попытаемся взять первую букву с помощью неправильной функции:
// Внимание! это неправильный код, не пиши так!
$s = "щ";
$x = substr($s, 0, 1);
Буква «щ» кодируется в utf-8 как 2 байта: 209 137 (я взял информацию тут: http://w...content-available-to-author-only...e.de/unicode-utf8-table.pl?start=1024&utf8=dec ). substr отрезает от строки не первую букву, а первый байт. Это значит, что в $x он положит 1 байт с кодом 209. В utf-8 это неверная последовательность, она не соответвует никакому символу (так как после 209 обязательно должно идти второе число). Ideone может вообще отказаться что-то отображать, встретив такой код.
То же самое, когда ты обращаешься к строке как к массиву: $s[0]. Эта команда берет не первую букву, а только первый байт строки. Естественно, такая программа не будет работать.
Функция strlen считает число байт (не букв) в строке. То есть в данном случае strlen($s) вернет нам 2.
Латинница и цифры кодируются в utf-8 одним байтом, с ними это работает, но все равно, не надо использовать эти функции — это слишком ненадежно и легко сделать ошибку.
Вывод: используй mb_* функции. Не используй доступ к строке как к массиву. В регулярных выражениях используй флаг u (он говорит что используется utf-8 а не однобайтовая кодировка).
Некоторые строковые функции без префикса mb тем не менее корректно работают с utf-8 и их можно использовать. Вот они: strtr (если передавать массив), str_replace, str_repeat, explode, addslashes, trim.
Не работают с utf-8: strrev, strlen, substr, strpos, ucfirst, wordwrap, str_pad и большинство других строковых функций, для работы которых нужно считать число символов. Не работает задание ширины в функциях вроде sprintf и printf.
mbstring.func_overload
В неоторых (неграмотных) учебниках ты можешь увидеть совет включить опцию mbstring.func_overload (подробнее про нее: http://p...content-available-to-author-only...p.net/manual/ru/mbstring.overload.php ). Ни в коем случае так не делай, так как это изначально неправильно спроектированная опция. Она не решает проблему, для которой задумывалась (включить в старом приложении использующем функции вроде strlen поддержку utf-8), а лишь создает путаницу. Например, при ее включении strlen заменяется на поддерживающую utf-8 mb_strlen, но ucfirst ни на что не заменяется и не работает.