PHP_EOL is Broken (Sometimes)

Introduced in version 5.0.2 of php, PHP_EOL is a string constant that represents the correct end of line symbol for the platform that you are running php on. So on Windows this should be set to "\r\n" (carriage return and line feed) and on a *nix system it should be set to "\n" (only line feed).
Where is it Used?
You SHOULD use it when programmatically creating a text file via php.
This will create a platform correct end of line separator, and ensure portability in a project.
Okay, So How is it Broken?
Usually when one is working with strings that contain multiple lines and they want to create an array that consists of each individual line they will use:
However, in the last couple of weeks I’ve been bitten by two different bugs caused by the very simple line of code above.
Bug #1
The first was when I was working on my Web Log Viewer Bundle project which is a Symfony bundle for viewing log files created by a Symfony project online, so logging in to the server to view them in not necessary. This project parses log files, formats the lines, and displays them in an HTML table. I was using $array = explode(PHP_EOL, $string); , but when running this on a Windows box, my string was not exploding correctly and I was getting $array = [$string] instead of $array = [line1, line2, line3, . ] .
Symfony uses monolog, and I didn’t investigate how monolog writes its end of line symbols in an attempt to determine the root cause of this bug, but I did need to find a fix. Help me stackoverflow, you’re my only hope.
Bug #2
I’ve recently started using Magallanes to deploy my projects, instead of my old hacky way of using a git post commit hook to push my changes to a git repo on the production server and then logging in to the production server and manually performing 5 or 6 steps to update dependencies, build assets, and clear caches. I did have to make a handful of really small changes to make it work in a Windows environment, but oh, man, was it worth it. Lots more on how I improved my deployment process with Magallanes in later posts.
I mentioned that I had to make some changes to make Magallanes work in a Windows environment, and I thought that I had everything worked out until I noticed this next bug. I use the "releases" option of Magallanes that uploads the current release to a timestamp-based-name directory inside of the releases directory and then creates a symlink named current that points to the newest directory in the releases folder. In the Magallanes config file, there is a parameter named releases that indicates the number of previous releases that should be kept on the server, in case there is a need to rollback to an earlier release. I noticed that in one of my releases directory, there were about 10 previous releases, when I had this configuration parameter set to 5. Something was broken.
I went looking through the cleanup task code and discovered that it was using ls -1 via ssh to get a string of all the directories inside of the releases directory and then using my recent archnemesis $releases = explode(PHP_EOL, trim($releases)); to turn that string into an array and doing all the work on the array. I was pretty confident that the bug was in the explode statment, due to my recent experience with Bug #1. After stepping through the code using xdebug, my hunch was confirmed, and I knew just where to find the fix.
The Fix
Some people, when confronted with a problem, think "I know, I’ll use regular expressions." Now they have two problems. — Jamie Zawinski
After discovering Bug #1, I fired up my trusty google machine and went in search to a solution for this problem. Sure enough, the first search result was a stackoverflow answer that fixed the bug. It was the following simple, one-line, cross-platform fix:
So, of course, I knew the perfect fix when I came across Bug #2, just a few short weeks later.
Conclusion
Although I was more concerned with fixing the bugs than I was with finding the root cause of the bugs, which would have required I solve the riddle "when is the end of line not the end of the line?", I was able to implement a nice and easy cross-platform solution and come to the conclusion that PHP_EOL is Broken (Sometimes).
If you liked this post, you can subscribe to the rss feed or follow me @ToddEidson on Twitter to be notified of future blog posts.
Php eol что такое
Строка (тип string ) — это набор символов, где символ — это то же самое, что и байт. Это значит, что PHP поддерживает ровно 256 различных символов, а также то, что в PHP нет встроенной поддержки Unicode. Смотрите также подробности реализации строкового типа.
Замечание: В 32-битных системах и в более ранних версиях PHP, строки ( string ) не могут быть более 2 ГБ (2147483647 байт).
Синтаксис
Строка может быть определена четырьмя различными способами:
- одинарными кавычками
- двойными кавычками
- heredoc-синтаксисом
- nowdoc-синтаксисом
Одинарные кавычки
Простейший способ определить строку — это заключить её в одинарные кавычки (символ ' ).
Чтобы использовать одинарную кавычку внутри строки, проэкранируйте её обратным слешем ( \ ). Если необходимо написать сам обратный слеш, продублируйте его ( \\ ). Все остальные случаи применения обратного слеша будут интерпретированы как обычные символы: это означает, что если вы попытаетесь использовать другие управляющие последовательности, такие как \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] | последовательность символов, соответствующая регулярному выражению символа в восьмеричной системе счисления, который молча переполняется, чтобы поместиться в байт (т.е. "\400" === "\000") |
| \x[0-9A-Fa-f] | последовательность символов, соответствующая регулярному выражению символа в шестнадцатеричной системе счисления |
| \u | последовательность символов, соответствующая регулярному выражению символа Unicode, которая отображается в строка в представлении UTF-8 |
Как и в строке, заключённой в одинарные кавычки, экранирование любого другого символа выведет также и сам символ экранирования.
Но самым важным свойством строк в двойных кавычках является обработка переменных. Смотрите более подробно обработку строк.
Heredoc
Третий способ определения строк — это использование heredoc-синтаксиса: <<< . После этого оператора необходимо указать идентификатор, затем перевод строки. После этого идёт сама строка, а потом этот же идентификатор, закрывающий вставку.
Закрывающий идентификатор может иметь отступ с помощью пробела или табуляции, и в этом случае отступ будет удалён из всех строк в строке документа. До PHP 7.3.0 закрывающий идентификатор должен был находиться в самом начале новой строки.
Кроме того, закрывающий идентификатор должен соответствовать тем же правилам именования, что и любая другая метка в PHP: он должен содержать только буквенно-цифровые символы и подчёркивания и должен начинаться с нецифрового символа или символа подчёркивания.
Пример #1 Базовый пример использования Heredoc в PHP 7.3.0
Результат выполнения данного примера в PHP 7.3:
Если закрывающий идентификатор смещён дальше, чем любая строка тела, будет выброшено ParseError :
Пример #2 Закрывающий идентификатор не должен иметь отступ больше, чем любая строка тела
Результат выполнения данного примера в PHP 7.3:
Если у закрывающего идентификатора есть отступ, табуляции также могут использоваться, однако табуляции и пробелы не должны смешиваться в отношении отступа закрывающего идентификатора и отступа тела (вплоть до закрывающего идентификатора). В любом из этих случаев будет выброшено ParseError . Эти ограничения на пробелы были включены, потому что смешивание табуляции и пробелов для отступов вредно для разбора.
Пример #3 Другой отступ для закрывающего идентификатора тела (пробелов)
<?php
// Весь следующий код не работает.
// Другой отступ для закрывающего идентификатора (табов) тела (пробелов)
<
echo <<<END
a
END;
>
// смешивание пробелов и табуляции в теле
<
echo <<<END
a
END;
>
// смешивание пробелов и табуляции в закрывающем идентификаторе
<
echo <<<END
a
END;
>
Результат выполнения данного примера в PHP 7.3:
За закрывающим идентификатором основной строки не обязательно ставить точку с запятой или новую строку. Например, начиная с PHP 7.3.0 разрешён следующий код:
Пример #4 Продолжение выражения после закрывающего идентификатора
Результат выполнения данного примера в PHP 7.3:
Если закрывающий идентификатор был найден в начале строки, то независимо от того, был ли он частью другого слова, его можно рассматривать как закрывающий идентификатор и выбросить ParseError .
Пример #5 Закрывающий идентификатор в теле текста имеет тенденцию вызывать ParseError
Результат выполнения данного примера в PHP 7.3:
Чтобы избежать этой проблемы, вы можете безопасно следовать простому правилу: не выбирайте закрывающий идентификатор, который появляется в теле текста.
До PHP 7.3.0 очень важно отметить, что строка с закрывающим идентификатором не должна содержать других символов, за исключением точки с запятой ( ; ). Это означает, что идентификатор не должен вводиться с отступом и что не может быть никаких пробелов или знаков табуляции до или после точки с запятой. Важно также понимать, что первым символом перед закрывающим идентификатором должен быть символ новой строки, определённый в вашей операционной системе. Например, в UNIX системах, включая macOS, это \n . После закрывающего идентификатора также сразу должна начинаться новая строка.
Если это правило нарушено и закрывающий идентификатор не является "чистым", считается, что закрывающий идентификатор отсутствует и PHP продолжит его поиск дальше. Если в этом случае верный закрывающий идентификатор так и не будет найден, то это вызовет ошибку парсинга с номером строки в конце скрипта.
Пример #6 Пример неправильного синтаксиса, до PHP 7.3.0
Пример #7 Пример правильного синтаксиса, даже до PHP 7.3.0
Heredoc, содержащий переменные, не может использоваться для инициализации свойств класса.
Heredoc-текст ведёт себя так же, как и строка в двойных кавычках, при этом их не имея. Это означает, что вам нет необходимости экранировать кавычки в heredoc, но вы по-прежнему можете использовать вышеперечисленные управляющие последовательности. Переменные обрабатываются, но с применением сложных переменных внутри heredoc нужно быть также внимательным, как и при работе со строками.
Пример #8 Пример определения 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;
?>
Результат выполнения данного примера:
Также возможно использовать heredoc-синтаксис для передачи данных через аргументы функции:
Пример #9 Пример применения heredoc в аргументах
Можно инициализировать статические переменные и свойства/константы класса с помощью синтаксиса heredoc:
Пример #10 Использование heredoc для инциализации статических переменных
<?php
// Статические переменные
function foo ()
<
static $bar = <<<LABEL
Здесь ничего нет.
LABEL;
>
// Константы/свойства класса
class foo
<
const BAR = <<<FOOBAR
Пример использования константы
FOOBAR;
public $baz = <<<FOOBAR
Пример использования поля
FOOBAR;
>
?>
Можно также окружать идентификатор Heredoc двойными кавычками:
Пример #11 Использование двойных кавычек в heredoc
Nowdoc
Nowdoc — это то же самое для строк в одинарных кавычках, что и heredoc для строк в двойных кавычках. Nowdoc похож на heredoc, но внутри него не осуществляется никаких подстановок. Эта конструкция идеальна для внедрения PHP-кода или других больших блоков текста без необходимости его экранирования. В этом он немного похож на SGML-конструкцию <![CDATA[ ]]> тем, что объявляет блок текста, не предназначенный для обработки.
Nowdoc указывается той же последовательностью <<< , что используется в heredoc, но последующий за ней идентификатор заключается в одинарные кавычки, например, <<<'EOT' . Все условия, действующие для идентификаторов heredoc также действительны и для nowdoc, особенно те, что относятся к закрывающему идентификатору.
Пример #12 Пример использования nowdoc
Результат выполнения данного примера:
Пример #13 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;
?>
Результат выполнения данного примера:
Пример #14 Пример использования статичных данных
Обработка переменных
Если строка указывается в двойных кавычках, либо при помощи heredoc, переменные внутри неё обрабатываются.
Существует два типа синтаксиса: простой и сложный. Простой синтаксис более лёгок и удобен. Он даёт возможность обработки переменной, значения массива ( array ) или свойства объекта ( object ) с минимумом усилий.
Сложный синтаксис может быть определён по фигурным скобкам, окружающим выражение.
Простой синтаксис
Если интерпретатор встречает знак доллара ( $ ), он захватывает так много символов, сколько возможно, чтобы сформировать правильное имя переменной. Если вы хотите точно определить конец имени, заключайте имя переменной в фигурные скобки.
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.» ;
?>
Результат выполнения данного примера:
Аналогично могут быть обработаны элемент массива ( array ) или свойство объекта ( object ). В индексах массива закрывающая квадратная скобка ( ] ) обозначает конец определения индекса. Для свойств объекта применяются те же правила, что и для простых переменных.
Пример #15 Пример простого синтаксиса
<?php
$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 .» ; // Не сработает
?>
Результат выполнения данного примера:
В PHP 7.1.0 добавлена поддержка отрицательных числовых индексов.
Пример #16 Отрицательные числовые индексы
Результат выполнения данного примера:
Для чего-либо более сложного, используйте сложный синтаксис.
Сложный (фигурный) синтаксис
Он называется сложным не потому, что труден в понимании, а потому что позволяет использовать сложные выражения.
Любая скалярная переменная, элемент массива или свойство объекта, отображаемое в строку, может быть представлена в строке этим синтаксисом. Выражение записывается так же, как и вне строки, а затем заключается в < и >. Поскольку < не может быть экранирован, этот синтаксис будет распознаваться только когда $ следует непосредственно за < . Используйте <\$ , чтобы напечатать <$ . Несколько поясняющих примеров:
<?php
// Показываем все ошибки
error_reporting ( E_ALL );
// Не работает, выводит: Это < здорово>
echo «Это < $great >» ;
// Работает, выводит: Это здорово
echo «Это < $great >» ;
// Работает
echo «Этот квадрат шириной < $square -> width >00 сантиметров.» ;
// Работает, ключи, заключённые в кавычки, работают только с синтаксисом фигурных скобок
echo «Это работает: < $arr [ 'key' ]>» ;
// Это неверно по той же причине, что и $foo[bar] вне
// строки. Другими словами, это по-прежнему будет работать,
// но поскольку PHP сначала ищет константу foo, это вызовет
// ошибку уровня E_NOTICE (неопределённая константа).
echo «Это неправильно: < $arr [ foo ][ 3 ]>» ;
// Работает. При использовании многомерных массивов внутри
// строк всегда используйте фигурные скобки
echo «Это работает: < $arr [ 'foo' ][ 3 ]>» ;
// Работает.
echo «Это работает: » . $arr [ ‘foo’ ][ 3 ];
echo «Это значение переменной по имени $name : <$< $name >> » ;
echo «Это значение переменной по имени, которое возвращает функция getName(): <$< getName ()>> » ;
echo «Это значение переменной по имени, которое возвращает \$object->getName(): <$< $object -> getName ()>> » ;
// Не работает, выводит: Это то, что возвращает getName():
echo «Это то, что возвращает getName():
// Не работает, выводит: C:\folder\
echo «C:\folder\< $great >.txt»
// Работает, выводит: C:\folder\fantastic.txt
echo «C:\\folder\\ < $great >.txt»
?>
С помощью этого синтаксиса также возможен доступ к свойствам объекта внутри строк.
<?php
class foo <
var $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 при записи (оставляя строку без изменений).
Замечание: До PHP 8.0.0 для доступа к символу в строке ( string ) также можно было использовать фигурные скобки, например, $str <42>, для той же цели. Синтаксис фигурных скобок устарел в PHP 7.4.0 и больше не поддерживается в PHP 8.0.0.
Попытка записи в смещение за границами строки дополнит строку пробелами до этого смещения. Нецелые типы будет преобразованы в целые. Неверный тип смещения вызовет ошибку уровня E_WARNING . Используется только первый символ присваемой строки. Начиная с PHP 7.1.0, присвоение пустой строки вызовет фатальную ошибку. Ранее в таком случае присваивался нулевой байт (NULL).
Строки в PHP внутренне представляют из себя массивы байт. Как результат, доступ или изменение строки по смещению небезопасно с точки зрения многобайтной кодировки, и должно выполняться только со строками в однобайтных кодировках, таких как, например, ISO-8859-1.
Замечание: Начиная с PHP 7.1.0, использование пустого индекса вызывает фатальную ошибку, ранее в подобном случае строка преобразовывалась в массив без предупреждения.
Пример #17 Несколько примеров строк
<?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’ ;
Смещение в строке должно задаваться либо целым числом, либо строкой, содержащей цифры, иначе будет выдаваться предупреждение.
Пример #18 Пример недопустимого смещения строки
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’ ]));
?>
Результат выполнения данного примера:
Замечание:
Попытка доступа к переменным других типов (исключая массивы или объекты, реализующие определённые интерфейсы) с помощью [] или <> молча вернёт 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() ), но оперируют побайтово.
- Наконец, есть функции, подразумевающие, что строка использует определённую кодировку, обычно UTF-8. Сюда попадают большинство функций из модулей intl и PCRE (в последнем случае, только при указании модификатора u ).
В конечном счёте, написание корректных программ, работающих с Unicode, означает осторожное избегание функций, которые не работают с Unicode и, скорее всего, испортят данные, и использование вместо них корректных функций, обычно из модулей intl и mbstring. Однако, использование функций, способных работать с Unicode, является самым началом. Вне зависимости от тех функций, которые предоставляет язык, необходимо знать спецификацию самого Unicode. Например, если программа предполагает существование в языке только строчных и заглавных букв, то она делает большую ошибку.
When do I use the PHP constant "PHP_EOL"?
I sometimes see this in code samples of PHP. Does this handle DOS/Mac/Unix endline issues?
20 Answers 20
Yes, PHP_EOL is ostensibly used to find the newline character in a cross-platform-compatible way, so it handles DOS/Unix issues.
Note that PHP_EOL represents the endline character for the current system. For instance, it will not find a Windows endline when executed on a unix-like system.
![]()
From main/php.h of PHP version 7.1.1 and version 5.6.30:
As you can see PHP_EOL can be «\r\n» (on Windows servers) or «\n» (on anything else). On PHP versions prior 5.4.0RC8, there were a third value possible for PHP_EOL : «\r» (on MacOSX servers). It was wrong and has been fixed on 2012-03-01 with bug 61193.
As others already told you, you can use PHP_EOL in any kind of output (where any of these values are valid — like: HTML, XML, logs. ) where you want unified newlines. Keep in mind that it’s the server that it’s determining the value, not the client. Your Windows visitors will get the value from your Unix server which is inconvenient for them sometimes.
I just wanted to show the possibles values of PHP_EOL backed by the PHP sources since it hasn’t been shown here yet.
![]()
You use PHP_EOL when you want a new line, and you want to be cross-platform.
This could be when you are writing files to the filesystem (logs, exports, other).
You could use it if you want your generated HTML to be readable. So you might follow your <br /> with a PHP_EOL .
You would use it if you are running php as a script from cron and you needed to output something and have it be formatted for a screen.
You might use it if you are building up an email to send that needed some formatting.
PHP_EOL (string) The correct ‘End Of Line’ symbol for this platform. Available since PHP 4.3.10 and PHP 5.0.2
You can use this constant when you read or write text files on the server’s filesystem.
Line endings do not matter in most cases as most software are capable of handling text files regardless of their origin. You ought to be consistent with your code.
If line endings matter, explicitly specify the line endings instead of using the constant. For example:
- HTTP headers must be separated by \r\n
- CSV files should use \r\n as row separator
![]()
I’d like to throw in an answer that addresses «When not to use it» as it hasn’t been covered yet and can imagine it being used blindly and no one noticing the there is a problem till later down the line. Some of this contradicts some of the existing answers somewhat.
If outputting to a webpage in HTML, particularly text in <textarea> , <pre> or <code> you probably always want to use \n and not PHP_EOL .
The reason for this is that while code may work perform well on one sever — which happens to be a Unix-like platform — if deployed on a Windows host (such the Windows Azure platform) then it may alter how pages are displayed in some browsers (specifically Internet Explorer — some versions of which will see both the \n and \r).
I’m not sure if this is still an issue since IE6 or not, so it might be fairly moot but seems worth mentioning if it helps people prompt to think about the context. There might be other cases (such as strict XHTML) where suddently outputting \r ‘s on some platforms could cause problems with the output, and I’m sure there are other edge cases like that.
As noted by someone already, you wouldn’t want to use it when returning HTTP headers — as they should always follow the RFC on any platform.
I wouldn’t use it for something like delimiters on CSV files (as someone has suggested). The platform the sever is running on shouldn’t determine the line endings in generated or consumed files.
Escape-последовательности и числовые нотации в PHP

Многие современные языки программирования поддерживают различные способы использования различных символов, таких как обычные английские латинские буквы, числа, символы, эмодзи и различные специальные символы, такие как символ новой строки или символ табуляции.
Большинство символов можно просто набрать с клавиатуры и использовать в коде PHP в их исходном виде. Например, $string = «php.watch» — это полностью допустимая строка в PHP, а $num = 42 — допустимое число. Также можно использовать многобайтные символы (для хранения которых требуется более одного байта), например, этот совершенно допустимый эмодзи: $emoji = «?» .
PHP, наряду со многими другими языками программирования, поддерживает определенное количество escape-последовательностей для использования различных символов, которые не могут быть набраны с обычной клавиатуры, не могут быть представлены в текстовой форме (например, невидимые символы или различные управляющие символы) или иным образом не считываются. Эти символы используют escape-последовательности, которые распознает PHP.
Что касается чисел, PHP поддерживает стандартные десятичные числа, но также может использовать и другие нотиции, такие как двоичное, восьмеричное, шестнадцатеричное и даже научное (scientific) представление. Они могут сделать код более читаемым и понятным в зависимости от контекста.
Двойные кавычки и Heredoc
В PHP строка с двойными кавычками ( «string» ) или Heredoc (смотрите ниже) поддерживает escape-последовательности и интерполяцию переменных.
PHP будет пытаться интерполировать переменные, если строковый литерал находится внутри строки с двойными кавычками или Heredoc.
Альтернативно (и желательно) интерполируемые переменные могут быть выделены фигурными скобками, так они будут выглядеть более удобочитаемыми:
Строки в одинарных кавычках ( ‘string’ ) и синтаксис Nowdoc не интерполируют переменные:
Только строки в двойных кавычках и Heredoc поддерживают escape-символы.
Экранирование символов
Поскольку PHP интерпретирует и интерполирует специальные символы внутри строковых литералов в двойных кавычках и heredoc, знак обратной косой черты ( \ ) используется как «escape-символ».
Например, использование \$name вместо $name не даст PHP интерполировать переменную $name .
Использование второго символа обратной косой черты предотвращает преобразование первого символа обратной косой черты в escape-символ.
PHP поддерживает несколько специальных escape-последовательностей для специальных символов. В приведенном выше примере \$ считается escape-последовательностью, потому что он отменяет интерполяцию PHP, заставляя PHP буквально использовать символ $ .
Символы табуляции: \t и \v
Возможно, самые простой из управляющих символов — это символ табуляции. Символ табуляции (по нажатию клавиши tab) можно использовать внутри строкового литерала, но использование \t вместо визуального пропуска делает его наглядным. Использование \t вместо буквального символа табуляции также позволяет избежать автоматической замены символов табуляции на пробелы в различных IDE.
\v — это вертикальная табуляция. На поддерживаемых терминалах символ вертикальной табуляции переходит к следующему символу в следующей строке:
Новые строки: \r и \n
\r («возврат каретки») и \n («перевод строки») являются символами новой строки.
Исторически так сложилось, что различные системы начали использовать либо \r , либо \n , и даже Windows использует \r\n . Например, Linux и MacOS по умолчанию используют символ «перевода строки» ( \n ) в качестве символа новой строки, тогда как Windows использует комбинацию \r\n (возврат каретки, за которым следует перевод строки). Старые системы MacOS использовали в качестве символа новой строки \r .
PHP имеет константу PHP_EOL , которая всегда ссылается на системный символ новой строки.
Escape-символ: \e
Escape-символы часто используется для отправки управляющих последовательностей ANSI в терминал. Например, \e , за которым следует [32m , указывает терминалу изменить цвет на зеленый, а [33m — на желтый.
Если приведенный выше фрагмент выполняется в терминале, который поддерживает управляющие последовательности ANSI, он интерпретирует его и изменяет текст:

Символ новой страницы: \f
Символ новой страницы — это управляющий символ ASCII для обозначения конца страницы. С его помощью принтеры могут вывести текущую страницу и начать с верхней части следующей. Когда \f передается на дисплейный терминал, он может результировать в очистке экрана, хотя это очень редкое поведение для большинства программ эмуляции терминала.
Восьмеричные escape-последовательности символов ASCII
PHP поддерживает экранирование восьмеричного числа в его соответствующий ASCII символ.
Например, ASCII символ P равен 80 в десятичной системе (смотрите диаграмму). 80 из десятичной системы счисления в переводе в восьмеричную — 120 .
Для символа P можно использовать восьмеричную escape-последовательность:
Любой базовый символ ASCII можно представить с помощью такой записи:
Любое значение в диапазоне от \0 до \377 будет интерпретироваться как восьмеричная escape-последовательность ASCII символа.
Обратите внимание, что числа для расширенных символов ASCII (от 128 до 255) несовместимы с UTF-8. PHP считает значение 128 (восьмеричное: 200 ; шестнадцатеричное: 80 ) недопустимым, поскольку оно не является допустимым значением UTF-8.
Хотя PHP принимает такие значения, они считаются недопустимыми символами в контексте UTF-8.
Шестнадцатеричные escape-последовательности символов ASCII
Подобно восьмеричным escape-последовательностям символов, PHP также допускает использование шестнадцатеричных чисел в escape-последовательности символов с префиксом \x .
Допускается только один байт, что подразумевает допустимый диапазон от x0 до xFF . Однако ограничение UTF-8 по-прежнему присутствует, и только значения до x80 будут считаться допустимыми символами.
Кроме того, шестнадцатеричные символы не чувствительны к регистру (т.е. AF равно af и aF ).
ASCII P — 80, что равно x50 :
Тот же «PHP.Watch» пример можно переписать с помощью шестнадцатеричных escape-последовательностей:
Escape-последовательности символов Unicode
PHP поддерживает использование любого Unicode символа с префиксом \u и шестнадцатеричным значением code point внутри фигурных скобок.
PHP выдаст ошибку парсера, если символ Unicode превышает значение 10FFFF :
10FFFF является верхним пределом, потому что спецификация UTF-8 объявляет диапазон от U+0000 до U+10FFFF .
В предыдущей версии этой статьи верхний предел неверно упоминался как FFFFF , что было исправлено на 10FFFF . Спасибо Саре Големон за то, что она указала на это.
Форму записи Unicode \u<> можно использовать качестве escape-последовательности для любого символа. Вот несколько примеров: