Исходный код PHP в файлах UTF-8; как правильно интерпретировать?

Я создаю инструменты для анализа исходного кода. Такие инструменты должны правильно читать файлы исходного кода, особенно в отношении кодировок символов. Например, «Какая точная строка байтов в строковом литерале?» (как PHP-литералы, так и HTML-текст).

Мое, возможно, ошибочное понимание заключается в том, что исходные файлы PHP имеют только 8-битный символ (т. Е. Движок PHP читает их таким образом [right] ?, поскольку они должны содержать только 8-битные символы). Но, восемь бит символов, в которых кодировка ? (Я предполагаю, что должен соответствовать ISO-8859-1 (-x?) [Может кто-то цитирует главу и стих?]. То есть умлаут предназначен для умлаута, правильно? После этого можно писать PHP-скрипты с HTML и струны для большинства европейских наций / наборов персонажей прямо.

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

В приведенных выше ситуациях можно прочитать файл PHP как 8-битный текст символа, и мне кажется, что он соответствует языку.

Меня озадачивают исходные файлы PHP, закодированные как UTF-8 (у пакета Joomla есть ~ 1800 исходных файлов, из которых около 10 – UTF-8, а остальные нет). Любые (не ASCII) европейские символы, которые корректно отображаются в рендеринге UTF-8, фактически кодируются как многобайтовые последовательности. Я полагаю, что на таких страницах, как UTF-8, будет отображаться HTML-код правильно. Но любые строковые сравнения для европейских символов или других символов Юникода, которые явно отображаются правильно в текстовом редакторе, просто не будут работать. И строковые литералы не будут содержать то, что они содержат. Используют ли программисты файлы UTF-8, потому что это то, что предлагают редакторы? Они делают это нарочно? Или это просто несчастный случай, который не имеет большого значения для большинства работ?

Итак, как следует читать исходный PHP-файл? (в частности, в какой кодировке символов?) Один из возможных ответов – всегда как 8-битные коды ISO-8859-1, независимо от фактического содержимого или спецификаций (я вижу много файлов PHP с индексом UTF-8 BOM). Другой ответ – UTF-8, если он отмечен.

[Наши инструменты читают и записывают произвольные кодировки. «Тривиальным» инструментом является кодировка «чтение-файл в одном», запись идентичных кодовых точек в другой кодировке. Чтение файлов PHP UTF-8 таким образом затрудняет запись эквивалентных файлов ISO8859-1, потому что многие кодовые точки UTF-8 (например, символ евро) не могут быть закодированы в ISO8859-x.]

EDIT 30 августа. Теперь мы проверяем файлы PHP, чтобы увидеть, есть ли спецификации UTF-8 или, как представляется, все последовательности UTF-8. В любом из этих случаев мы читаем файл как UTF-8; в противном случае мы читаем его как ISO8859-1 по умолчанию. Теперь мы сохраним кодировку файла, если мы ее модифицируем. (Получение всего этого права – довольно много работы). Это, по-видимому, безопасная стратегия, но это может отличаться от ожидаемых PHP-программистов.

TL; DR

ASCII


До PHP 5.4 интерпретатор PHP совсем не заботился о кодировке файлов PHP, о чем свидетельствует тот факт, что директива zend.script_encoding ini появилась только в этой версии. Он всегда рассматривал его как ASCII в основном.

Когда PHP нужно идентифицировать, например, имя функции, которое содержит символы за пределами ASCII-7bit (ну, любой помеченный объект с любой меткой действительно, но вы получаете мою точку …), он просто ищет функцию в таблица символов с той же последовательностью байтов – умлаут (или что-то еще …), написанная одним способом, будет обрабатываться иначе, чем умлаут, написанный по-другому. Попробуй. Для обратной совместимости, если zend.script_encoding не установлен, это по-прежнему является поведением по умолчанию. Также обратите внимание на регулярное выражение, показывающее, что является действительным идентификатором , который вы видите, является нейтральным символом (ну … кроме латинских букв, которые находятся в диапазоне ASCII-7 бит), но вместо этого показывает вам байты.

Это приводит нас также к конструкции declare (encoding) . Если вы видите THAT в файле, это окончательный набор символов для этого конкретного файла (ТОЛЬКО). Используйте что-то еще, пока вы не столкнетесь с ним, и если вы увидите более одного – почитайте второй после его объявления.

Если нет …

В статическом контексте (т. Е. Когда вы не знаете эффективных ini-настроек) вам нужно отступить к чему-то другому (что-то, что определено пользователем, в идеале), когда кодировка важна, или иначе просто обрабатывать символы за пределами ASCII-7bit как чистый двоичный код, и отображать их в виде единой кодовой точки.

В динамическом контексте (например, если вы могли бы, например, переименовать файл на мгновение, создать временный файл в этом месте с таким именем, попросить его вернуть значение zend.script_encoding; восстановить исходный файл), вы должны использовать значение zend.script_encoding, если доступно, и отключение к чему-то другому (как в статическом контексте) в противном случае.

То же самое относится к строкам, фрагментам HTML и любому другому содержимому файла PHP – оно просто считывается как двоичная строка, за исключением некоторых символов ASCII (т.е. байтов), которые важны для лексика PHP, например последовательности «<? Php "(обратите внимание, что все символы ASCII …); апостроф в одной кавычки; и т. д. – Сам интерпретатор не заботится о кодировке строки, и если вы должны отображать содержимое строки на экране, вы должны использовать вышеуказанные средства, чтобы выяснить, как это сделать.


Крайние случаи (запрошены в комментариях):

  1. Существует ли ограничение на то, какая кодировка разрешена?

    Кажется, нет ни одного списка разрешенных кодировок в любом месте, или, по крайней мере, я не могу его найти. Учитывая, что это преемник параметра -enable-zend-multibyte compile, кодировки UTF всех вкусов обязательно будут в этом списке. Даже если другие (ANSI) кодировки не влияют на сам PHP, это не должно мешать вам использовать это значение в качестве подсказки.

  2. Как «declare (encoding)» работает, если исходным файлом является UTF-16 (нулевые 8-битные байты между 8-битными символами ascii для объявления)?

    zend.script_encoding используется до появления объявления (кодирования). Если он не установлен, предполагается ASCII. Это не должно быть проблемой даже в файле UTF-16 … правильно? (Я не использую UTF-16)

  3. Если параметр .ini или файл UTF-8 или иначе, то идентификаторы предположительно принимаются только из кодовых точек в диапазоне x41-xFF, но не из кодовых точек x100 вверх?

    Я не пробовал подавать недопустимые байты UTF-8, чтобы сообщить вам ответ на этот вопрос, и в руководстве никогда ничего не говорится о вопросе. Я бы предположил, что выполнение PHP завершится с ошибкой синтаксического анализа. Или, по крайней мере, это должно быть. Что касается вашего инструмента, он должен сообщать о недействительной последовательности UTF-8 в любом случае, так как даже если это позволяет PHP, это проблема с QA.

  4. Для кодировок UTF символы в строках представлены как их кодовая точка UTF (что не имеет смысла, поскольку строки PHP кажутся только 8-битными символами)?

    Нет. Символы в строках и не-PHP-контент по-прежнему рассматриваются как просто последовательность байтов, которую вы можете подтвердить, просмотрев вывод strlen () и видя, как он отличается от mb_strlen (), который является тем, который уважает кодирование (ну … он точно соответствует настройке mbstring.internal_encoding, но все же).

  5. Если нет, что значит установить кодировку в UTF?

    AFAIK, это влияет на поиск в таблице символов. С набором UTF, умляутами, написанными по-разному или в разных вариантах UTF, которые заканчиваются теми же кодовыми точками UTF … все они будут сходиться по одному и тому же символу, а не объявлять (кодировать), где байты, вместо этого выполняется сравнение байтов. И я говорю «AFAIK» здесь, потому что, честно говоря, я никогда не использовал такие эксперименты сам … Я «делаю добро» все, что действительно – UTF-8'-er ».

Как уже неоднократно повторялось, файлы PHP не имеют никакой кодировки для байтов выше x7f. Все, что вы можете сказать, это то, что байты от x00 до x7f являются ascii.

Файл с маркером спецификации в начале недействителен PHP. Таким образом, нет ничего похожего на файл PHP в iso-8859-1 или utf-8. Он простой 8-бит.

Файл PHP не iso-8859-x, потому что эти кодировки не содержат всех возможных значений байта. Как вы знаете, x7f к x9f недействительны в iso-8859-1, но любой файл PHP может содержать их.

PHP-файл также не является utf-8, поскольку он может содержать недопустимые последовательности utf-8, не будучи недействительными.

Большая картина

Charset по соглашению при написании

Файл PHP может иметь кодировку по соглашению, но это зависит от усмотрения программиста. Он скажет своему редактору, что такой проект находится в utf-8 или iso-8859-1 или что еще.

Но опять же, это только конвенция программиста. Его редактор обрабатывает файл PHP так, как если бы он был в такой-то кодировке. Кодирование служит только для отображения файла в редакторе и позволяет программисту редактировать его.

Нет кодировки во время компиляции

Как объяснялось выше, компилятор не должен знать кодировку, которую предполагал программист. Единственное, что имеет значение, это то, что представляет собой байтовые последовательности в файле.

Неявная или явная кодировка, определенная для потребления

PHP генерирует некоторые данные, которые отправляются через Интернет в браузер. В то время, когда браузер отображает данные, кодировка определенно определена, но как?

  • Кодировка может быть определена в HTTP-заголовке, например, Content-Type: text/html; charset=utf-8 Content-Type: text/html; charset=utf-8
  • Он может быть определен в самом выходе HTML: <meta charset="utf-8">
  • Или, если кодировка не определена явно, браузер делает обоснованное предположение в зависимости от последовательностей байтов, присутствующих в документе (например, действительные последовательности utf-8 или спецификация).

Конечно, хорошая практика заключается в том, что приложение PHP никогда не позволяет браузеру выбирать, но нет требования, чтобы кодировка определялась где угодно.

Подробнее

Обычно кодировка, которую выбирает программист, будет такой же, которая будет использоваться в конце цепочки в браузере, и все строки в PHP-файлах будут использовать эту же кодировку.

Но этого не должно быть. Имеются веские причины, почему это не так. Давайте рассмотрим примеры:

Различные языки, разные кодировки

Я использую Joomla, так как это версия 1.0. В этой версии языковые файлы имели свою собственную кодировку. Французский язык был iso-8859-1, а в арабских файлах были окна-1256 и русские файлы koi8-r. Для этих кодировок важно, но не для всех других файлов, которые можно рассматривать одинаково как utf-8 или iso-5598-1. (Между тем, Joomla переключился на utf-8.)

Гетерогенные базы данных

Одно из наших веб-приложений подключается к двум различным базам данных: один из них находится в utf-8, другой – в windows-1252. Это означает, что все строки в этом проекте не находятся в одной кодировке. Я использую utf-8 как можно больше, но мне нужно транслировать кодировки туда и обратно, используя группу функций mb_* в PHP.

Функции преобразования PHP

Просто наличие функций преобразования кодирования mb_convert_encoding , iconv , utf8_encode и т. Д. utf8_encode , что в той же строке проекта могут присутствовать различные кодировки.

Хорошая практика

Определите свою кодировку и придерживайтесь ее! Лучшим выбором будет использование utf-8. Если нужны другие строки других кодировок, вы всегда можете написать что-то вроде $s=mb_convert_encoding('Уровень','ucs-2','utf8');

И здесь: вы не можете использовать маркеры спецификации в PHP . Причина проста: маркер спецификации – два байта, которые идут перед открывающим тегом <?php . Поэтому они отправляются в браузер. Если кто-то пытается отправить header() после этого, генерируется ошибка, и заголовок не отправляется.

Вывод

  • В общем, нет необходимости определять кодировку файла PHP. Важное значение имеет только кодировка окончательно обработанного HTML-файла.
  • Хорошей практикой является редактирование всех файлов в той же кодировке, которая используется для отображения окончательных результатов. Но это действительно важно только для языковых файлов (если вы вообще используете какую-либо систему i18n).
  • Хотя на практике все строки в одном файле имеют одинаковую кодировку, ничто не помешает программисту-программисту написать строки в разных кодировках в одном файле и все равно получить рабочую программу.

Наконец, кодирование в PHP – это всего лишь соглашение, используемое во время записи, и кодировка, используемая в браузере для отображения страницы. Между ними, PHP-файл не имеет специальной кодировки, это просто 8-битный.

На самом деле нет способа достоверно рассказать о кодировке исходного кода PHP. Это может быть что угодно. Как вы знаете, единственным общим идентификатором является спецификация, но большинство людей удалит их из своих исходных файлов, так как они могут вызвать проблемы во время вывода.

Как бороться с этим, зависит от того, что вы хотите сделать. Обычно это не имеет значения, потому что PHP-файл позаботится об объявлении его самой кодировки, например, отправив заголовок Content-type (или он будет определен неявно, например, потому что он является частью проекта, в соответствии с которым он должен использовать определенный кодирование). Проблема кодирования на самом деле не возникает, потому что файл сортирует ее во время выполнения.

Если вы создаете инструмент, который манипулирует или анализирует исходные файлы PHP в той или иной форме, скорее всего, кодирование не имеет большого значения, но нам нужно было бы узнать больше о вашей ситуации, чтобы оценить это.

Способ, которым большинство IDE справляется с этой неопределенностью, заключается в том, что они просят разработчика вручную указать, какая кодировка содержит проект, папка и / или файл. Возможно, это и вариант для вас.