Строки
Строка (тип string) - это набор символов, где символ - это то же самое, что и байт. Это значит, что PHP поддерживает ровно 256 различных символов, а также то, что в PHP нет встроенной поддержки Unicode. Смотрите также подробности реализации строкового типа.
Замечание: В 32-битных системах и в более ранних версиях PHP, строки (string) не могут быть более 2 ГБ (2147483647 байт).
Синтаксис
Строка может быть определена четырьмя различными способами:
Одинарные кавычки
Простейший способ определить строку - это заключить ее в одинарные кавычки
(символ '
).
Чтобы использовать одинарную кавычку внутри строки, проэкранируйте ее обратным слешем
(\
). Если необходимо написать сам обратный слеш, продублируйте его
(\\
). Все остальные случаи применения обратного слеша будут
интерпретированы как обычные символы: это означает, что если вы попытаетесь
использовать другие управляющие последовательности, такие как \r
или
\n
, они будут выведены как есть вместо какого-либо особого поведения.
Замечание: В отличие от синтаксиса двойных кавычек и heredoc, переменные и управляющие последовательности для специальных символов, заключенных в одинарные кавычки, не обрабатываются.
<?php
echo 'это простая строка';
echo 'Также вы можете вставлять в строки
символ новой строки вот так,
это нормально';
// Выводит: Однажды Арнольд сказал: "I'll be back"
echo 'Однажды Арнольд сказал: "I\'ll be back"';
// Выводит: Вы удалили C:\*.*?
echo 'Вы удалили C:\\*.*?';
// Выводит: Вы удалили C:\*.*?
echo 'Вы удалили C:\*.*?';
// Выводит: Это не будет развернуто: \n новая строка
echo 'Это не будет развернуто: \n новая строка';
// Выводит: Переменные $expand также $either не разворачиваются
echo 'Переменные $expand также $either не разворачиваются';
?>
Двойные кавычки
Если строка заключена в двойные кавычки ("), PHP распознает следующие управляющие последовательности специальных символов:
Последовательность | Значение |
---|---|
\n |
новая строка (LF или 0x0A (10) в ASCII) |
\r |
возврат каретки (CR или 0x0D (13) в ASCII) |
\t |
горизонтальная табуляция (HT или 0x09 (9) в ASCII) |
\v |
вертикальная табуляция (VT или 0x0B (11) в ASCII) |
\e |
escape-знак (ESC или 0x1B (27) в ASCII) |
\f |
подача страницы (FF или 0x0C (12) в ASCII) |
\\ |
обратная косая черта |
\$ |
знак доллара |
\" |
двойная кавычка |
\[0-7]{1,3} |
последовательность символов, соответствующая регулярному выражению символа в восьмеричной системе счисления, который молча переполняется, чтобы поместиться в байт (т.е. "\400" === "\000") |
\x[0-9A-Fa-f]{1,2} |
последовательность символов, соответствующая регулярному выражению символа в шестнадцатеричной системе счисления |
\u{[0-9A-Fa-f]+} |
последовательность символов, соответствующая регулярному выражению символа Unicode, которая отображается в строка в представлении UTF-8 |
Как и в строке, заключенной в одинарные кавычки, экранирование любого символа выведет также и сам символ экранирования.
Но самым важным свойством строк в двойных кавычках является обработка переменных. Смотрите более подробно обработку строк.
Heredoc
Третий способ определения строк - это использование heredoc-синтаксиса:
<<<
. После этого оператора необходимо указать идентификатор,
затем перевод строки. После этого идет сама строка, а потом этот же идентификатор,
закрывающий вставку.
Строка должна начинаться с закрывающего идентификатора, т.е. он должен стоять в первом столбце строки. Кроме того, идентификатор должен соответствовать тем же правилам именования, что и все остальные метки в PHP: содержать только буквенно-цифровые символы и знак подчеркивания, и не должен начинаться с цифры (знак подчеркивания разрешается).
Очень важно отметить, что строка с закрывающим идентификатором не должна содержать
других символов, за исключением точки с запятой (;
). Это означает,
что идентификатор не должен вводиться с отступом и что не
может быть никаких пробелов или знаков табуляции до или после точки с запятой. Важно
также понимать, что первым символом перед закрывающим идентификатором должен быть
символ новой строки, определенный в вашей операционной системе. Например, в UNIX
системах, включая macOS, это \n
. После закрывающего
идентификатора также сразу должна начинаться новая строка.
Если это правило нарушено и закрывающий идентификатор не является "чистым", считается, что закрывающий идентификатор отсутствует и PHP продолжит его поиск дальше. Если в этом случае верный закрывающий идентификатор так и не будет найден, то это вызовет ошибку парсинга с номером строки в конце скрипта.
Пример #1 Пример неправильного синтаксиса
<?php
class foo {
public $bar = <<<EOT
bar
EOT;
// отступ перед закрывающим идентификатором недопустим
}
?>
Пример #2 Пример правильного синтаксиса
<?php
class foo {
public $bar = <<<EOT
bar
EOT;
}
?>
Heredoc, содержащий переменные, не может использоваться для инициализации свойств класса.
Heredoc-текст ведет себя так же, как и строка в двойных кавычках, при этом их не имея. Это означает, что вам нет необходимости экранировать кавычки в heredoc, но вы по-прежнему можете использовать вышеперечисленные управляющие последовательности. Переменные обрабатываются, но с применением сложных переменных внутри heredoc нужно быть также внимательным, как и при работе со строками.
Пример #3 Пример определения heredoc-строки
<?php
$str = <<<EOD
Пример строки,
охватывающей несколько строк,
с использованием heredoc-синтаксиса.
EOD;
/* Более сложный пример с переменными. */
class foo
{
var $foo;
var $bar;
function __construct()
{
$this->foo = 'Foo';
$this->bar = array('Bar1', 'Bar2', 'Bar3');
}
}
$foo = new foo();
$name = 'Имярек';
echo <<<EOT
Меня зовут "$name". Я печатаю $foo->foo.
Теперь я вывожу {$foo->bar[1]}.
Это должно вывести заглавную букву 'A': \x41
EOT;
?>
Результат выполнения данного примера:
Меня зовут "Имярек". Я печатаю Foo. Теперь, я вывожу Bar2. Это должно вывести заглавную букву 'A': A
Также возможно использовать heredoc-синтаксис для передачи данных через аргументы функции:
Пример #4 Пример применения heredoc в аргументах
<?php
var_dump(array(<<<EOD
foobar!
EOD
));
?>
Можно инициализировать статические переменные и свойства/константы класса с помощью синтаксиса heredoc:
Пример #5 Использование heredoc для инциализации статических переменных
<?php
// Статические переменные
function foo()
{
static $bar = <<<LABEL
Здесь ничего нет...
LABEL;
}
// Константы/свойства класса
class foo
{
const BAR = <<<FOOBAR
Пример использования константы
FOOBAR;
public $baz = <<<FOOBAR
Пример использования поля
FOOBAR;
}
?>
Можно также окружать идентификатор Heredoc двойными кавычками:
Пример #6 Использование двойных кавычек в heredoc
<?php
echo <<<"FOOBAR"
Привет, мир!
FOOBAR;
?>
Nowdoc
Nowdoc - это то же самое для строк в одинарных кавычках, что и heredoc для строк в
двойных кавычках. Nowdoc похож на heredoc, но внутри него
не осуществляется никаких подстановок. Эта конструкция
идеальна для внедрения PHP-кода или других больших блоков текста без необходимости
его экранирования. В этом он немного похож на SGML-конструкцию
<![CDATA[ ]]>
тем, что объявляет блок текста,
не предназначенный для обработки.
Nowdoc указывается той же последовательностью <<<
,
что используется в heredoc, но последующий за ней идентификатор заключается
в одинарные кавычки, например, <<<'EOT'
.
Все условия, действующие для идентификаторов heredoc также действительны
и для nowdoc, особенно те, что относятся к закрывающему идентификатору.
Пример #7 Пример использования nowdoc
<?php
echo <<<'EOD'
Пример текста,
занимающего несколько строк,
с помощью синтаксиса nowdoc. Обратные слеши всегда обрабатываются буквально,
например, \\ и \'.
EOD;
Результат выполнения данного примера:
Пример текста, занимающего несколько строк, с помощью синтаксиса nowdoc. Обратные слеши всегда обрабатываются буквально, например, \\ и \'.
Пример #8 Nowdoc пример цитирования строки с переменными
<?php
/* Более сложный пример с переменными. */
class foo
{
public $foo;
public $bar;
function __construct()
{
$this->foo = 'Foo';
$this->bar = array('Bar1', 'Bar2', 'Bar3');
}
}
$foo = new foo();
$name = 'Имярек';
echo <<<'EOT'
Меня зовут "$name". Я печатаю $foo->foo.
Теперь я печатаю {$foo->bar[1]}.
Это не должно вывести заглавную 'A': \x41
EOT;
?>
Результат выполнения данного примера:
Меня зовут "$name". Я печатаю $foo->foo. Теперь я печатаю {$foo->bar[1]}. Это не должно вывести заглавную 'A': \x41
Пример #9 Пример использования статичных данных
<?php
class foo {
public $bar = <<<'EOT'
bar
EOT;
}
?>
Обработка переменных
Если строка указывается в двойных кавычках, либо при помощи heredoc, переменные внутри нее обрабатываются.
Существует два типа синтаксиса: простой и сложный. Простой синтаксис более легок и удобен. Он дает возможность обработки переменной, значения массива (array) или свойства объекта (object) с минимумом усилий.
Сложный синтаксис может быть определен по фигурным скобкам, окружающим выражение.
Простой синтаксис
Если интерпретатор встречает знак доллара ($
), он захватывает так
много символов, сколько возможно, чтобы сформировать правильное имя переменной. Если
вы хотите точно определить конец имени, заключайте имя переменной в фигурные скобки.
<?php
$juice = "apple";
echo "He drank some $juice juice.".PHP_EOL;
// Некорректно. 's' - верный символ для имени переменной, но переменная имеет имя $juice.
echo "He drank some juice made of $juices.";
// Корректно. Строго указан конец имени переменной с помощью скобок:
echo "He drank some juice made of ${juice}s.";
?>
Результат выполнения данного примера:
He drank some apple juice. He drank some juice made of . He drank some juice made of apples.
Аналогично могут быть обработаны элемент массива (array) или свойство
объекта (object). В индексах массива закрывающая квадратная скобка
(]
) обозначает конец определения индекса. Для свойств объекта
применяются те же правила, что и для простых переменных.
Пример #10 Пример простого синтаксиса
<?php
define('KOOLAID', 'koolaid1');
$juices = array("apple", "orange", "koolaid1" => "purple");
echo "He drank some $juices[0] juice.".PHP_EOL;
echo "He drank some $juices[1] juice.".PHP_EOL;
echo "He drank some $juices[koolaid1] juice.".PHP_EOL;
class people {
public $john = "John Smith";
public $jane = "Jane Smith";
public $robert = "Robert Paulsen";
public $smith = "Smith";
}
$people = new people();
echo "$people->john drank some $juices[0] juice.".PHP_EOL;
echo "$people->john then said hello to $people->jane.".PHP_EOL;
echo "$people->john's wife greeted $people->robert.".PHP_EOL;
echo "$people->robert greeted the two $people->smiths."; // Не сработает
?>
Результат выполнения данного примера:
He drank some apple juice. He drank some orange juice. He drank some purple juice. John Smith drank some apple juice. John Smith then said hello to Jane Smith. John Smith's wife greeted Robert Paulsen. Robert Paulsen greeted the two .
В PHP 7.1.0 добавлена поддержка отрицательных числовых индексов.
Пример #11 Отрицательные числовые индексы
<?php
$string = 'string';
echo "Символ с индексом -2 равен $string[-2].", PHP_EOL;
$string[-3] = 'o';
echo "Изменение символа на позиции -3 на 'o' дает следующую строку: $string.", PHP_EOL;
?>
Результат выполнения данного примера:
Символ с индексом -2 равен n. Изменение символа на позиции -3 на 'o' дает следующую строку: strong
Для чего-либо более сложного, используйте сложный синтаксис.
Сложный (фигурный) синтаксис
Он называется сложным не потому, что труден в понимании, а потому что позволяет использовать сложные выражения.
Любая скалярная переменная, элемент массива или свойство объекта, отображаемое в
строку, может быть представлена в строке этим синтаксисом. Просто запишите выражение
так же, как и вне строки, а затем заключите его в {
и }
. Поскольку {
не может быть экранирован, этот синтаксис будет
распознаваться только когда $
следует непосредственно за
{
. Используйте {\$
, чтобы напечатать {$
.
Несколько поясняющих примеров:
<?php
// Показываем все ошибки
error_reporting(E_ALL);
$great = 'здорово';
// Не работает, выводит: Это { здорово}
echo "Это { $great}";
// Работает, выводит: Это здорово
echo "Это {$great}";
// Работает
echo "Этот квадрат шириной {$square->width}00 сантиметров.";
// Работает, ключи, заключенные в кавычки, работают только с синтаксисом фигурных скобок
echo "Это работает: {$arr['key']}";
// Работает
echo "Это работает: {$arr[4][3]}";
// Это неверно по той же причине, что и $foo[bar] вне
// строки. Другими словами, это по-прежнему будет работать,
// но поскольку PHP сначала ищет константу foo, это вызовет
// ошибку уровня E_NOTICE (неопределенная константа).
echo "Это неправильно: {$arr[foo][3]}";
// Работает. При использовании многомерных массивов внутри
// строк всегда используйте фигурные скобки
echo "Это работает: {$arr['foo'][3]}";
// Работает.
echo "Это работает: " . $arr['foo'][3];
echo "Это тоже работает: {$obj->values[3]->name}";
echo "Это значение переменной по имени $name: {${$name}}";
echo "Это значение переменной по имени, которое возвращает функция getName(): {${getName()}}";
echo "Это значение переменной по имени, которое возвращает \$object->getName(): {${$object->getName()}}";
// Не работает, выводит: Это то, что возвращает getName(): {getName()}
echo "Это то, что возвращает getName(): {getName()}";
?>
С помощью этого синтаксиса также возможен доступ к свойствам объекта внутри строк.
<?php
class foo {
var $bar = 'I am bar.';
}
$foo = new foo();
$bar = 'bar';
$baz = array('foo', 'bar', 'baz', 'quux');
echo "{$foo->$bar}\n";
echo "{$foo->{$baz[1]}}\n";
?>
Результат выполнения данного примера:
I am bar. I am bar.
Замечание:
Значение, к которому осуществляется доступ из функций, вызовов методов, статических переменных класса и констант класса внутри
{$}
, будет интерпретироваться как имя переменной в области, в которой определена строка. Использование одинарных фигурных скобок ({}
) не будет работать для доступа к значениям функций, методов, констант классов или статических переменных класса.
<?php
// Показываем все ошибки
error_reporting(E_ALL);
class beers {
const softdrink = 'rootbeer';
public static $ale = 'ipa';
}
$rootbeer = 'A & W';
$ipa = 'Alexander Keith\'s';
// Это работает, выводит: Я бы хотел A & W
echo "Я бы хотел {${beers::softdrink}}\n";
// Это тоже работает, выводит: Я бы хотел Alexander Keith's
echo "Я бы хотел {${beers::$ale}}\n";
?>
Доступ к символу в строке и его изменение
Символы в строках можно использовать и модифицировать, определив их смещение относительно начала строки, начиная с нуля, в квадратных скобках после строки, например, $str[42]. Думайте о строке для этой цели, как о массиве символов. Если нужно получить или заменить более 1 символа, можно использовать функции substr() и substr_replace().
Замечание: Начиная с PHP 7.1.0, поддерживаются отрицательные значения смещения. Они задают смещение с конца строки. Ранее отрицательные смещение вызывали ошибку уровня
E_NOTICE
при чтении (возвращая пустую строку) либоE_WARNING
при записи (оставляя строку без изменений).
Замечание: К символу в строке также можно обращаться с помощью фигурных скобок, например, $str{42}.
Попытка записи в смещение за границами строки дополнит строку
пробелами до этого смещения. Нецелые типы будет преобразованы в целые.
Неверный тип смещения вызовет ошибку уровня E_WARNING
.
Используется только первый символ присваемой строки.
Начиная с PHP 7.1.0, присвоение пустой строки вызовет фатальную ошибку. Ранее
в таком случае присваивался нулевой байт (NULL).
Строки в PHP внутренне представляют из себя массивы байт. Как результат, доступ или изменение строки по смещению небезопасно с точки зрения многобайтной кодировки, и должно выполняться только со строками в однобайтных кодировках, таких как, например, ISO-8859-1.
Замечание: Начиная с PHP 7.1.0, использование пустого индекса вызывает фатальную ошибку, ранее в подобном случае строка преобразовывалась в массив без предупреждения.
Пример #12 Несколько примеров строк
<?php
// Получение первого символа строки
$str = 'This is a test.';
$first = $str[0];
// Получение третьего символа строки
$third = $str[2];
// Получение последнего символа строки
$str = 'This is still a test.';
$last = $str[strlen($str)-1];
// Изменение последнего символа строки
$str = 'Look at the sea';
$str[strlen($str)-1] = 'e';
?>
Смещение в строке должно задаваться либо целым числом, либо строкой, содержащей цифры, иначе будет выдаваться предупреждение.
Пример #13 Пример недопустимого смещения строки
<?php
$str = 'abc';
var_dump($str['1']);
var_dump(isset($str['1']));
var_dump($str['1.0']);
var_dump(isset($str['1.0']));
var_dump($str['x']);
var_dump(isset($str['x']));
var_dump($str['1x']);
var_dump(isset($str['1x']));
?>
Результат выполнения данного примера:
string(1) "b" bool(true) Warning: Illegal string offset '1.0' in /tmp/t.php on line 7 string(1) "b" bool(false) Warning: Illegal string offset 'x' in /tmp/t.php on line 9 string(1) "a" bool(false) string(1) "b" bool(false)
Замечание:
Попытка доступа к переменным других типов (исключая массивы или объекты, реализующие определенные интерфейсы) с помощью
[]
или{}
молча вернетnull
.
Замечание:
Доступ к символам в строковых литералах можно получить с помощью синтаксиса
[]
или{}
.
Замечание:
Доступ к символам в строковых литералах с использованием синтаксиса
{}
объявлен устаревшим в PHP 7.4. Функционал удален в PHP 8.0.
Полезные функции и операторы
Строки могут быть объединены при помощи оператора '.' (точка). Обратите внимание, оператор сложения '+' здесь не работает. Дополнительную информацию смотрите в разделе Строковые операторы.
Для модификации строк существует множество полезных функций.
Основные функции описаны в разделе строковых функций, а для расширенного поиска и замены - функции Perl-совместимых регулярных выражений.
Также существуют функции для работы с URL, и функции шифрования/дешифрования строк (Sodium и Hash).
Наконец, смотрите также функции символьных типов.
Преобразование в строку
Значение может быть преобразовано в строку с помощью приведения
(string)
, либо функции strval().
В выражениях, где необходима строка, преобразование происходит автоматически.
Это происходит, когда вы используете функции echo
или print, либо когда значение переменной сравнивается
со строкой. Прочтение разделов руководства Типы и Манипуляции с типами сделает
следующее более понятным. Смотрите также settype().
Значение bool true
преобразуется в строку
"1"
, а значение false
преобразуется в
""
(пустую строку). Это позволяет преобразовывать значения
в обе стороны - из булева типа в строковый и наоборот.
Целое (int) или число с плавающей точкой
(float) преобразуется в строку, представленную числом,
состоящим из его цифр (включая показатель степени для чисел с плавающей
точкой). Числа с плавающей точкой могут быть преобразованы с помощью
экспоненциального представления (4.1E+6
).
Замечание:
Начиная с PHP 8.0.0, символоом десятичной точки всегда является
.
. До PHP 8.0.0 символ десятичной точки определялся в локали скрипта (категория LC_NUMERIC). Смотрите функцию setlocale()
Массивы всегда преобразуются в строку "Array"
,
так что вы не можете отобразить содержимое массива (array),
используя echo или print,
чтобы узнать, что он содержит. Чтобы просмотреть отдельный элемент, используйте
что-нибудь вроде echo $arr['foo']
. Смотрите
ниже советы о том, как отобразить/просмотреть все содержимое.
Для преобразования переменной типа "Object"
в тип string
используется магический метод
__toString.
Тип ресурс (resource) всегда всегда преобразуется в строку (string) вида
"Resource id #1"
, где 1
является
номером ресурса привязанного к resource во время выполнения.
И хотя не стоит точно полагаться на эту строку, которая может быть изменена в будущем,
она всегда будет уникальной для текущего запуска скрипта (т.е. веб-запроса или
CLI-процесса) и не может использоваться повторно для другого ресурса.
Если вы хотите получить тип ресурса, используйте get_resource_type().
Значение null
всегда преобразуется в пустую строку.
Как вы могли видеть выше, прямое преобразование в строку массивов, объектов или ресурсов не дает никакой полезной информации о самих значениях, кроме их типов. Более подходящий способ вывода значений для отладки - использовать функции print_r() и var_dump().
Большинство значений в PHP может быть преобразовано в строку для постоянного хранения. Этот метод называется сериализацией и может быть выполнен при помощи функции serialize().
Подробности реализации строкового типа
Строковый тип (string) в PHP реализован в виде массива
байт и целого числа, содержащего длину буфера. Он не содержит никакой
информации о способе преобразования этих байт в символы, предоставляя
эту задачу программисту. Нет никаких ограничений на содержимое строки,
например, байт со значением 0
("NUL"-байт) может
располагаться где угодно (однако, стоит учитывать, что некоторые функции,
как сказано в этом руководстве, не являются "бинарно-безопасными",
т.е. они могут передавать строки библиотекам, которые игнорируют
данные после NUL-байта).
Данная природа строкового типа объясняет почему в PHP нет отдельного типа "byte" - строки играют эту роль. Функции, возвращающие нетекстовые данные - например, произвольный поток данных, считываемый из сетевого сокета - тем не менее возвращают строки.
Принимая во внимание тот факт, что PHP не диктует определенную кодировку
для строк, можно задать вопрос, как в таком случае кодируются строковые
литералы. Например, строка "á"
эквивалентна
"\xE1"
(ISO-8859-1), "\xC3\xA1"
(UTF-8, форма нормализации C), "\x61\xCC\x81"
(UTF-8, форма нормализации D) или какому-либо другому возможному
представлению? Ответом является следующее: строка будет закодирована тем
образом, которым она записана в файле скрипта. Таким образом, если
скрипт записан в кодировке ISO-8859-1, то и строка будет закодирована в
ISO-8859-1 и т.д. Однако, это правило не применяется при включенном
режиме Zend Multibyte: в этом случае скрипт может быть записан в любой
кодировке (которая указывается ясно или определяется автоматически),
а затем конвертируются в определенную внутреннюю кодировку, которая и будет
впоследствии использована для строковых литералов.
Учтите, что на кодировку скрипта (или на внутреннюю кодировку, если
включен режим Zend Multibyte) накладываются некоторые ограничения:
практически всегда данная кодировка должна быть надмножеством ASCII,
например, UTF-8 или ISO-8859-1. Учтите также, что кодировки, зависящие
от состояния, где одни и те же значения байт могут быть использованы
в начальном и не начальном состоянии сдвига, могут вызвать проблемы.
Разумеется, чтобы приносить пользу, строковые функции должны сделать некоторые предположения о кодировке строки. К несчастью, среди PHP-функций довольно большое разнообразие подходов к этому вопросу:
- Некоторые функции предполагают, что строка закодирована в какой-либо однобайтовой кодировке, однако, для корректной работы им не требуется интерпретировать байты как определенные символы. Под эту категорию попадают, например, substr(), strpos(), strlen() и strcmp(). Другой способ мышления об этих функциях представляет собой оперирование буферами памяти, т.е. они работают непосредственно с байтами и их смещениями.
- Другие функции ожидают передачу кодировку в виде параметра, возможно, предполагая некоторую кодировку по умолчанию, если параметр с кодировкой не был указан. Такой функцией является htmlentities() и большинство функций из расширения mbstring.
-
Другие функции используют текущие установки локали (см.
setlocale()), но оперируют побайтово).
В эту категорию попадают strcasecmp(),
strtoupper() и ucfirst().
Это означает, что они могут быть использованы только с однобайтовыми
кодировками, в том случае, когда кодировка совпадает с локалью.
Например,
strtoupper("á")
может вернуть"Á"
, если локаль установлена корректно и букваá
закодирована в виде одного байта. Если она закодирована в UTF-8, будет возвращен некорректный результат, и, в зависимости от текущей локали, результирующая строка может быть (или не быть) испорчена. -
Наконец, есть функции, подразумевающие, что строка использует
определенную кодировку, обычно UTF-8. Сюда попадают большинство
функций из расширений intl
и PCRE
(в последнем случае, только при указании модификатора
u
). Хотя это и сделано специально, функция utf8_decode() подразумевает кодировку UTF-8, а utf8_encode() - ISO-8859-1.
В конечном счете, написание корректных программ, работающих с Unicode, означает осторожное избегание функций, которые не работают с Unicode и, скорее всего, испортят данные, и использование вместо них корректных функций, обычно из расширений intl и mbstring. Однако, использование функций, способных работать с Unicode, является самым началом. Вне зависимости от тех функций, которые предоставляет язык, необходимо знать спецификацию самого Unicode. Например, если программа предполагает существование в языке только строчных и заглавных букв, то она делает большую ошибку.