Журнал LinuxFormat - перейти на главную

LXF74-75:PHP2

Материал из Linuxformat
Перейти к: навигация, поиск
PHP (Пол Хадсон)

Многобайтовые строки

Правильно ли выполняются ваши PHP-скрипты для всех иностранных языков? Пол Хадсон (Paul Hudson) открывает свой разговорник…

Несмотря на несколько грубое звучание, ASCII (American Standard Code for Information Interchange, произносится как «ass-key», «ключ от задницы») — возможно один из самых популярных стандартов из когда-либо изобретенных. ASCII был разработан как семибитная кодировка для латинских символов, используемых в английском языке. На каждый знак отводится 7 бит: таким образом можно записать 127 различных символов, включая все цифры и буквы (строчные и прописные), плюс символы пунктуации и управляющие символы — этого вполне достаточно для хранения текста.

Настоящий американец, ASCII совершенно игнорирует нужды остального мира — элементарные вещи как, например, дополнительные символы в скандинавских языках или около 90 000 символов Ханьшуй, необходимых китайским пользователям для свободного чтения и письма, или… Конечно же, это несправедливая критика, ведь 40 лет назад для компьютера это было слишком сложно, но верно и то, что явное преобладание ASCII уверило многих программистов в том, что они могут работать только с латиницей. В действительности люди хотят общаться на своем собственном языке, что требует дополнительных усилий.

Если для ASCII достаточно 1 байта (дополнительный бит обычно установлен в ноль), то другие языки требуют многобайтовых строк - более 1 байта на каждый знак. Стандартные операторы PHP не работают с многобайтовыми строками, что подразумевает создание нового набора функций, поддерживающих многобайтовость. К счастью, PHP позволяет сделать это очень легко (естественно), так что приступим!

ПОЧЕМУ ОДНОБАЙТОВОСТЬ — ЭТО ПЛОХО

Норвегия: земля фиордов, полуночного солнца и распивания украденного эля из пупков юных дев. Действительно превосходный край! Но что случается, когда вы возвращаетесь из поездки домой и обнаруживаете, что не можете разместить рассказ о посещенных вами местах на своем сайте, разработанном без поддержки многобайтовых символов? Рассмотрим, к примеру, сонный норвежский город Mj ndalen. Это красивое место с холмами, тихой рекой и лесами, где может разместиться армия белок. Однако, название города содержит нелатинский символ " ", что эквивалентно звуку «ё» в слове Гётеборг. Он не может быть набран в ASCII, следовательно при обработке названия стандартными строковыми функциями PHP возникнут проблемы. Например:

<?php
$string = "Mj ndalen";
echo strtoupper($string), "\n";

выдаст результат: «Mj ndalen», то есть все символы отобразились в верхнем регистре кроме . Это неправильно, хотя на самом деле не так уж и критично — смысл все еще сохраняется, даже если название выглядит слегка неприятно для глаз.

Однако могут быть проблемы и похуже:

echo strlen($string), "\n";

Эта строка выводит число 10 для строки, содержащей 9 символов, потому что PHP неправильно посчитал буквы благодаря многобайтовому символу " ". В этом случае мы имеем ошибку завышения на единицу, которая будет нарастать с увеличением числа многобайтовых символов.

МНОГОБАЙТОВЫЕ ФУНКЦИИ

Решение проблемы заключается в использовании многобайтовых функций из библиотеки PHP. Вместо strlen() следует использовать mb_strlen(); вместо substr() — mb_substr(), и так далее. Многие строковые функции имеют прямой многобайтовый аналог, который вы можете использовать, просто добавив приставку mb к вызову функции. Список параметров остается почти тем же и, таким образом, имеется достаточно функций для работы: mb_strtoupper(), mb_parse_str(), mb_ereg_match(), mb_split()…

Некоторые функции имеют не столь очевидные имена, но вы всегда можете найти их в справочном руководстве PHP. Например, многобайтовым эквивалентом для mail() будет mb_send_mail(), а функции strtoupper() /strtolower() заменены одной унифицированной mb_convert_case(). Ключевой особенностью является то, что они работают и с обычными ASCII-символами, то есть вы можете использовать их на своем сайте и никто не увидит разницы.

Для того, чтобы начать работать с многобайтовыми строкам, и вы должны перекомпилировать PHP и указать параметр --enablembstring=all в качестве ключа сценария ./configure; это задействует многобайтовые функции. Затем следует произвести небольшие изменения в вашем файле php.ini. Найдите параметр mbstring.internal_encoding’ (или добавьте если его нет), и присвойте ему значение UTF-8. Это общий формат Unicode, использующий различную длину в байтах для различных символов, что сокращает занимаемый ими размер. Например, для символов ASCII используется один байт, большинство европейских и ближневосточных языков используют два байта, а восточноазиатские используют три.

Теперь относительно кодирования. Помните, ранее я сказал, что «список параметров остается почти таким же», вместо «список параметров остается точно таким же». Разница заключается в том, что каждая функция имеет дополнительный параметр, добавляемый в конце списка и позволяющий вам указать тип кодировки для группы символов. По умолчанию мы установили кодировку UTF-8, которая подходит для большинства случаев. Но если у вас во входящих данных попали символы особого типа и вы хотите сохранить их правильно, то вам следует указать кодировку с помощью этого дополнительного параметра.

Многобайтовые строковые функции работают с большим количеством производных от UNICODE кодировок, например с UCS-4 и UTF-16 до специфических языковых кодировок, таких как SJIS (Японская), Big-5 (Китайская), KOI-8R (русская) и так далее. Для принудительного использования какой-либо кодировки, укажите ее название в виде строкового параметра в конце списка.

Если вы не планируете использовать какую-то другую кодировку, кроме внутренней, установленной по умолчанию, то существует специальный параметр в php.ini, позволяющий сберечь много часов работы и использовать ваши готовые сценарии (скрипты), не отвлекаясь на многобайтовые строки. Он называется mbstring.func_overload, и его действие заключается в простом конвертировании существующих функций, таких как strtoupper() и mail(), в их многобайтовые аналоги. Когда этот параметр задействован, вы можете использовать strtoupper() также как и обычно, а PHP динамически подставит и выполнит функцию mb_convert_case(), используя установленную по умолчанию кодировку. Параметр mbstring.func_overload представляет собой битовое поле, которое вы можете установить в одно из следующих положений:

  • 0 — отключить поддержку; использовать однобайтовые функции.
  • 1 — использовать подстановку для mail()
  • 2 — использовать подстановку для всех функций str*().
  • 4 — использовать подстановку для всех функций ereg*().

Вы можете также указать комбинацию режимов, просто сложив указанные числа. Например, значение 3 заставляет использовать подстановки для функции mail() и всех функции str*(), а значение 6 указывает на необходимость использования подстановок для всех str*() и всех ereg*() функций, но оставляет неизменной функцию mail(). Для конвертирования всех функций необходимо использовать значение 7. Многие параметры PHP могут быть установлены для определенной директории при помощи файлов .htaccess, но это не рекомендуется при использовании опции mbstring.func_overload. Как утверждают разработчики, это приводит к нестабильности. Указывайте этот параметр в файле php.ini и не трогайте его.

Если вы дочитали до этого места, а также посмотрели врезку Унифицируем UNICODE (ниже), то тогда вы способны написать свой собственный многобайтовый сценарий, используя опцию mbstring. func_overload, преобразовать чужие скрипты в целях обеспечения совместимости с многобайтовыми строками, а также сохранить новый многобайтовый текст в вашей базе данных с указанием подходящего набора символов. Мы все еще продолжаем работать преимущественно в ASCII, потому что решиться на переход не так просто, но дело того стоит. Мир уже плотно оплетен Глобальной Сетью, поэтому online-магазины и сайты новостей, не понимающие этого факта просто покинут рынок, открытый для тех, кто понял. Удачи!

УНИФИКАЦИЯ UNICODE
Сохранение многобайтовых элементов в вашей базе данных.

В отличие от PHP, MySQL обладает собственной продвинутой системой для работы с символами не-английских языков, возможно потому, что он разработан в Швеции. MySQL работает с UTF-8 просто прекрасно, а так как мы сделали UTF-8 международной кодировкой по умолчанию и для PHP, то налицо полное совпадение кодировок. Для указания типа набора символов, при создании таблицы используется оператор CHARACTER SET, например так:

CREATE TABLE Writers (ID INT PRIMARY KEY NOT
NULL AUTO_INCREMENT, Name VARCHAR(100),
HomeTown VARCHAR(30)) TYPE=InnoDB CHARACTER
SET utf8;

Эта конструкция создает таблицу из двух столбцов (Name и HomeTown), которые хранят строки с символами переменной длины в формате UTF-8. Если вы знаете, как работает UTF-8, то вспомните, что каждый символ в UTF-8 имеет переменную длину (variable-length) от 1 до 3 байт. MySQL не знает, какие символы вы будете использовать, а значит, для поля типа CHAR будет зарезервировано максимальное количество байт. Например, 10 латинских символов в кодировке UTF-8 занимают 10 байт, в то время как 10 японских – 30 байт. Если у вас есть поле CHAR(30), то будет использоваться именно 30 байт вне зависимости от типа символа. Если вы используете поле VARCHAR(30), то латинские символы будут занимать только 10 байт.

Так же как и PHP, MySQL поддерживает множество кодировок, не относящихся к UNICODE; вы можете увидеть те, которые доступны вам, выполнив запрос «SHOW CHARACTERS SET». Однако, использование этих кодировок может отличаться для разных приложений, разных версий приложений, и потенциально даже для разных платформ; как говорится: не все работает так, как задумывалось!

При использовании PostgreSQL вам доступен большой набор многобайтовых кодировок, и вы можете включить их в базу данных, используя параметр WITH ENCODING оператора CREATE DATABASE. Например, для создания БД с поддержкой русского языка вы должны дать команду:

CREATE DATABASE people WITH ENCODING 'KOI8';

Есть небольшая особенность использования UNICODE в PostgreSQL. Дело в том, что при указании значения UNICODE как типа кодировки в действительности автоматически устанавливается UTF-8. Во многих других приложениях синонимом UNICODE является кодировка UTF-16, которая использует символы фиксированной длины; попытка соединить эти два типа приводит к большим проблемам. Лучшим решением в этом случае будет избегать неопределенности. Если вам нужна кодировка UTF-8 – укажите это явно.

Персональные инструменты
купить
подписаться
Яндекс.Метрика