Глава 76 · С нуля → справочник
Строковые функции: полный справочник Strings
От первого текстового поля до всех 105 функций: байты и Unicode, поиск, форматирование, HTML, локали, старые форматы и точные ошибки.
С чего начать: текст товара → полезная подпись
Из строки поставщика в подпись товара
Одна задача связывает несколько небольших операций.
- A-42;Лампа
Исходная строка из известного простого формата.
- trim
Убирает внешние пробелы, оставляет внутренние данные.
- explode(";", ...)
Разделяет запись на массив двух полей.
- $fields[0] и $fields[1]
Код и название доступны по отдельности.
- A-42: Лампа
Склеивание создаёт подпись. Для HTML далее нужно корректное экранирование.
Если поля сами могут содержать разделитель, нужен согласованный CSV/JSON-формат и его парсер. Схема не заменяет проверку внешних данных.
В карточке товара нужно убрать случайные пробелы, найти разделитель, собрать подпись, а затем безопасно показать её на странице. Строковые функции решают отдельные части этой работы. Здесь полный раздел Strings официального справочника: от trim до редких старых форматов. Начните с нескольких строк ниже; затем выбирайте карточку по задаче. Исторические и платформенные функции сохранены с условиями применения и переходом на современные средства.
Сохраните пример как UTF-8-файл без BOM, например text-demo.php, и выполните его установленным PHP CLI. Начальный <?php переключает файл в PHP-код. $name — имя переменной, = присваивает значение, кавычки задают текст, ; завершает команду. Скобки после имени функции передают аргументы, а возвращённый результат можно сохранить в другую переменную. echo выводит данные; "\n" в двойных кавычках обозначает перевод строки. Полная установка и запуск объяснены в первых шагах.
- $raw получает " Болт M10 ": пробелы — часть данных.
- trim($raw) получает это значение и возвращает "Болт M10".
- $clean сохраняет ответ; $raw остаётся прежним.
- echo выводит "Товар: " и $clean → Товар: Болт M10.
<?php
$raw = ' Болт M10 ';
$clean = trim($raw);
echo 'Товар: ', $clean, "\n";
echo '[', $raw, "]\n";
Ожидаемый результат
Товар: Болт M10
[ Болт M10 ]
Первый результат полезен для подписи, второй показывает неизменность исходной переменной. Квадратные скобки только делают крайние пробелы видимыми; они не участвуют в очистке.
В следующем примере запись после запятой в вызове — второй аргумент. Строка-разделитель передаётся первой, текст — вторым. explode вернёт массив: несколько значений с номерами от 0; [0] читает первое поле. Знак . склеивает строки. Для настоящего внешнего ввода сначала проверяют наличие и число полей; здесь запись задана автором.
<?php
$record = 'BR41;Болт M10;12';
$fields = explode(';', $record);
$label = $fields[0] . ': ' . $fields[1];
echo $label, "\n";
echo 'Остаток: ', $fields[2], "\n";
Ожидаемый результат
BR41: Болт M10
Остаток: 12
Это собственный простой формат без кавычек. Если название может содержать ;, договор меняется: выбирайте CSV/JSON и соответствующий парсер, а не добавляйте случайные замены.
Дальше карточки постепенно используют типы и переменные, массивы, if, foreach, параметры и return и исключения. Сначала читайте минимальный пример карточки; обёртка в function и обработка отказа идут после него. Полный синтаксис строк, кавычек, интерполяции, heredoc/nowdoc находится в главе языка о строках.
Байты, кодовые точки и видимые знаки
PHP string хранит байты. Байт — число 0…255; кодировка объясняет, как из байтов получить текст. ASCII-латиница обычно занимает один байт на букву, русская буква UTF-8 — два. Кодовая точка — единица Unicode; видимый знак может состоять из нескольких точек, объединённых в графему. Поэтому «длина 20 символов» неполна как требование: лимит API, номер позиции и обрезание должны называть свои единицы.
| Единица | Чем считать и что получится |
|---|---|
| Байты | strlen и substr: размер пакета/позиция в бинарном формате. Для Я — 2 байта UTF-8. |
| Кодовые точки UTF-8 | mb_strlen/mb_substr с явной UTF-8; нужен mbstring. Для e + комбинируемого акцента — 2 точки. |
| Графемные кластеры | grapheme_strlen/grapheme_substr, нужен intl; e + акцент обычно один видимый знак. Правила связаны с данными Unicode/ICU. |
| Единицы UTF-16 другого API | Некоторые клиенты передают позиции именно в 16-битных единицах. Нельзя подставлять туда byte-offset PHP или число графем без согласованного преобразования. |
| Пиксели/ширина колонки | Ни strlen, ни mb_strlen не измеряют шрифт. Разметка/CSS и рендерер определяют визуальную ширину. |
- Я → UTF-8 D0 AF: 2 байта, 1 точка, 1 графема.
- e + U+0301 → 65 CC 81: 3 байта, 2 точки, 1 графема.
- Выберите единицу до вызова функции; не исправляйте ответ коэффициентом «делить на 2».
<?php
$letter = 'Я';
$accent = "e\u{0301}";
echo strlen($letter), '/', mb_strlen($letter, 'UTF-8'), '/', grapheme_strlen($letter), "\n";
echo strlen($accent), '/', mb_strlen($accent, 'UTF-8'), '/', grapheme_strlen($accent), "\n";
echo bin2hex($accent), "\n";
Ожидаемый результат
2/1/1
3/2/1
65cc81
Нужны mbstring и intl. \u{0301} в двойных кавычках вставляет Unicode-кодовую точку акцента в UTF-8; это не шесть букв её записи. bin2hex показывает реальные байты.
<?php
function preview(string $text, int $limit): string
{
if ($limit < 1 || !mb_check_encoding($text, 'UTF-8')) {
throw new InvalidArgumentException('Нужны UTF-8 и положительный лимит');
}
$length = grapheme_strlen($text);
if (!is_int($length)) { throw new RuntimeException('Текст не разобран'); }
if ($length <= $limit) { return $text; }
$start = grapheme_substr($text, 0, $limit);
if (!is_string($start)) { throw new RuntimeException('Не удалось получить начало'); }
return $start . '…';
}
echo preview('Болт', 4), "\n";
echo preview('Болт M10', 4), "\n";
echo preview("e\u{0301}x", 1), "\n";
Ожидаемый результат
Болт
Болт…
é…
Сначала проверяется UTF-8, затем длина в той же единице, что обрезание. is_int принимает только успешную длину, отвергая оба отказа grapheme_strlen: false и null. is_string проверяет результат обрезания. Первый пример не получает лишнее многоточие; третий сохраняет букву вместе с акцентом. Возвращается обычный текст — перед HTML его ещё экранируют.
Байтовые функции обычно binary-safe: допускают NUL внутри строки. Это не обещание Unicode-обработки. Явные исключения вроде strcoll и crypt рассмотрены в своих карточках. strrev/str_shuffle могут разрушить UTF-8, а char-mask trim/strspn проверяет байты независимо. Регистр ASCII с PHP 8.2 не зависит от локали у большинства стандартных операций; Unicode-регистр выполняют mb_* или специализированные инструменты. Внешнюю кодировку не угадывают по одному удачному преобразованию: её задаёт договор источника.
Как читать аргументы, результаты и ошибки
Справочная сигнатура описывает договор, а не строку для копирования вместо вызова. strpos(string $haystack, string $needle, int $offset = 0): int|false говорит: два обязательных текста, необязательное целое смещение, ответ — число либо false. В месте вызова пишут strpos($text, "="); слова string/int там не нужны. Знак ? перед типом разрешает null, а = задаёт значение по умолчанию; пропущенный аргумент не всегда равнозначен явно переданному null.
| Запись в сигнатуре | Как читать |
|---|---|
| string / int / bool / array / mixed | Строка / целое / логическое / массив / любой тип. Функция может иметь дополнительные смысловые ограничения. |
| string|false | Либо строка, либо логический отказ false; сравнивайте === false. "" и "0" — строки. |
| int &$count | Функция запишет значение в переданную переменную; & в месте вызова не добавляют. |
| mixed ...$values | Произвольное число отдельных аргументов; вариант vprintf вместо них принимает один массив. |
| void | Полезный эффект есть, но возвращаемого значения нет: parse_str пишет в аргумент, echo выводит. |
| resource | Открытый поток из fopen; это не имя файла и не содержимое файла. |
| ValueError / TypeError / предупреждение | Неверное допустимое значение / неподходящий тип / диагностическое сообщение; конкретный договор карточки объясняет, какой результат ещё проверять. |
<?php
$position = strpos('=12', '=');
if ($position === false) {
echo "Нет разделителя\n";
} else {
echo 'Разделитель на позиции ', $position, "\n";
}
Ожидаемый результат
Разделитель на позиции 0
if выбирает одну ветку, === сравнивает и тип, и значение. Нулевая позиция не становится отсутствием. Для нашего формата имя поля перед = пусто — это уже отдельная проверка правил данных.
Не передавайте null вместо обязательного string и не рассчитывайте на случайные слабые преобразования: явно проверьте тип внешнего поля. В strict_types=1 ошибочный скалярный тип обычно даёт TypeError; echo/print всё равно преобразуют значения в строку. Предупреждение не становится исключением само по себе. Учебные set_error_handler-примеры наблюдают ожидаемую диагностику и восстанавливают обработчик; они не советуют глобально скрывать ошибки.
Выбрать операцию по потребности
| Нужный результат | Начать с этих карточек |
|---|---|
| Убрать крайний отступ, взять часть | trim / ltrim / rtrim / substr / substr_replace |
| Ответ да/нет, позиция, хвост | str_contains / str_starts_with / strpos / strrpos / strstr / strrchr |
| Разделить/собрать фиксированный формат | explode / implode / strtok / str_getcsv / parse_str |
| Заменить целые метки или отдельные байты | str_replace / str_ireplace / strtr |
| Оформить отчёт/HTML | sprintf / number_format / htmlspecialchars / nl2br |
| Выбрать порядок/близкие кандидаты | strcmp / strnatcmp / strcoll / levenshtein / soundex |
| Перевести бинарные байты в представление | bin2hex / hex2bin / quoted_printable_encode / convert_uuencode |
Специализированные книги Regex, URL, mbstring, Intl и Hash имеют собственные функции и договоры: наличие связанных примеров здесь не заменяет их полный справочник. Для правил регулярного выражения сначала изучите его синтаксис; для целого CSV-файла используйте потоковый парсер. Нормализация, проверка допустимости и экранирование решают разные задачи: trim не обезвреживает HTML, htmlspecialchars не валидирует артикул, хеш не подтверждает отправителя.
Установка и различия окружений
Стандартные строковые функции входят в PHP: отдельная установка Strings не нужна. Это не означает, что каждое историческое имя существует на любой версии/ОС: nl_langinfo нет в Windows, convert_cyr_string/hebrevc/money_format удалены в PHP 8, utf8_encode/utf8_decode deprecated с PHP 8.2. str_increment/str_decrement появились в 8.3. Примеры этой авторской партии выполнены на PHP 8.5.11 с проектным php.ini; поддержка старых платформ и системных локалей отдельно не объявляется проверенной.
Альтернативные Unicode-примеры явно помечены mbstring/intl, это отдельные расширения. Поддержка crypt с PHP 8.1 может собираться через системную библиотеку опцией --with-external-libcrypt; этот выбор относится к сборке PHP, а не к настройке php.ini и не запускается примером. Установку/настройку PHP рассматривают соответствующие главы. Чтение файлов выполняется только из локальных fixtures примера; никакой PHP-код этой группы не обращается к сети.
<?php
echo PHP_VERSION, "\n";
echo PHP_SAPI, "\n";
foreach (['str_contains', 'str_increment', 'nl_langinfo'] as $function) {
echo $function, ': ', function_exists($function) ? 'есть' : 'нет', "\n";
}
echo 'mbstring: ', extension_loaded('mbstring') ? 'есть' : 'нет', "\n";
echo 'intl: ', extension_loaded('intl') ? 'есть' : 'нет', "\n";
Вывод зависит от версии, платформы и загруженных расширений. Это диагностический пример с проверкой синтаксиса, без универсального exact stdout. Он не устанавливает расширения и не меняет конфигурацию.
Как читать формат %s, %d, ширину и точность
Перед печатью складской строки нужно решить, где стоят артикул, количество и цена. Формат — маленький шаблон: обычный текст копируется, а % начинает место для значения. Сначала разберём "%s: %d". %s берёт строку, %d — целое число; значения передаются после шаблона в таком же порядке. printf печатает сразу, sprintf возвращает строку. Ни один из них не проверяет бизнес-данные вместо вас.
- format="%s: %d" → два места.
- BR41 попадает в %s; 12 попадает в %d.
- sprintf возвращает BR41: 12 в $line.
- echo печатает готовую $line; другие действия могут сохранить или отправить её позже.
<?php
$line = sprintf('%s: %d', 'BR41', 12);
echo $line, "\n";
echo sprintf('Готово: %d%%', 75), "\n";
Ожидаемый результат
BR41: 12
Готово: 75%
Один % начинает спецификатор; %% означает буквальный знак процента и не потребляет значение. Одинарные кавычки вокруг формата сохраняют его как буквальный текст.
Полная запись одного места: %[argnum$][flags][width][.precision]specifier. Квадратные скобки в описании означают необязательные части: в шаблон их не копируют. Целое argnum начинается с 1 и заканчивается знаком $; оно выбирает значение по номеру после формата. В одинарных кавычках PHP $ остаётся буквальным. Ширина — минимальная, она не обрезает длинное значение; точность для %s — максимальное число байтов и может разрезать UTF-8.
| Часть | Точное значение |
|---|---|
| argnum$ | %2$s берёт второе значение; %1$s позволяет повторить первое. Не смешивайте без необходимости позиционный и обычный порядок. |
| - | Выравнивание влево внутри ширины; по умолчанию вправо. |
| + | Печатать плюс у положительного числа; минус у отрицательного остаётся. |
| пробел / 0 / 'символ | Обычное заполнение пробелами; 0 заполняет нулями; апостроф и следующий байт задают своё заполнение. Нулевое заполнение действует слева у чисел; с %s возможно и правое заполнение. |
| width | Минимальная ширина числом либо *: тогда ширина берётся из дополнительного int перед форматируемым значением. Внешнюю ширину ограничивайте небольшим разумным пределом. |
| .precision | Для e/E/f/F — цифры после точки (по умолчанию 6); для g/G/h/H — значащие цифры; для s — максимум байтов. Точка без числа означает 0, обработка этого случая исправлена в PHP 8.5. * после точки берёт дополнительный int. |
| %c | Игнорирует ширину/заполнение: целое превращается в один байт, это не Unicode-кодовая точка. |
| Спецификатор | Преобразование значения |
|---|---|
| s | Строка. |
| d / u | Знаковое / беззнаковое десятичное целое. Для отрицательного u результат зависит от разрядности целого; не используйте его как проверку неотрицательности. |
| b / o / x / X | Двоичное / восьмеричное / шестнадцатеричное целое; X использует заглавные A…F. |
| c | Один байт из целого. |
| e / E | Экспоненциальная запись float с e / E. |
| f / F | Дробная запись float: f учитывает десятичный разделитель LC_NUMERIC, F всегда использует точку. |
| g / G | Общий формат float с компактным выбором дробной или экспоненциальной записи; G использует E. Десятичный разделитель зависит от локали. |
| h / H | С PHP 8.0: как g/G, но десятичная точка не зависит от локали. |
| % | Буквальный %, без аргумента. |
Точный выбор g/G: P равняется указанной ненулевой точности, 6 при её отсутствии, 1 при точности 0. Если показатель X в экспоненциальной записи удовлетворяет P > X ≥ −4, выбирается дробная форма с P−(X+1) знаками после точки; иначе экспоненциальная с P−1. h/H применяют соответствующее правило с независимой от локали дробной формой F. Целые спецификаторы преобразуют значение в int, вещественные — во float, s — в string; форматирование не доказывает правильность исходной записи числа.
<?php
echo sprintf('[%-6s] [%04d] [%+.2F]', 'BR41', 7, 12.5), "\n";
echo sprintf('%2$s / %1$s / %2$s', 'BR41', 'ready'), "\n";
echo sprintf('[%*.*F]', 8, 2, 12.5), "\n";
echo sprintf("[%'_6s]", 'BR41'), "\n";
echo sprintf('%.F', 12.5), "\n";
Ожидаемый результат
[BR41 ] [0007] [+12.50]
ready / BR41 / ready
[ 12.50]
[__BR41]
12
%-6s задаёт минимум 6 байтов с выравниванием влево; %04d — четыре позиции с нулями; %+.2F — знак и два десятичных знака. В %*.*F сначала расходуются 8 и 2, затем 12.5. Последний результат показывает PHP 8.5 и округление printf семейства, которое не следует подменять правилом number_format.
<?php
echo sprintf('%b %o %x %X %c', 10, 10, 26, 26, 65), "\n";
$old = setlocale(LC_NUMERIC, '0');
setlocale(LC_NUMERIC, 'C');
echo sprintf('%.1e %.1E %.3g %.3G %.3h %.3H', 1200, 1200, 1200, 1200, 1200, 1200), "\n";
if ($old !== false) { setlocale(LC_NUMERIC, $old); }
echo bin2hex(sprintf('%.1s', 'Я')), "\n";
Ожидаемый результат
1010 12 1a 1A A
1.2e+3 1.2E+3 1.2e+3 1.2E+3 1.2e+3 1.2E+3
d0
Точность %.1s оставила только первый байт UTF-8-буквы Я: d0 не является самостоятельным корректным UTF-8-символом. Для превью текста сначала используйте Unicode-функцию, затем %s без байтового обрезания.
Ошибки PHP 8: неизвестный спецификатор, недопустимый номер аргумента, ширина или точность дают ValueError. Недостаток значений в printf/sprintf/fprintf — ArgumentCountError; в vprintf/vsprintf/vfprintf недостаточно элементов массива — ValueError. Отсутствие самого обязательного аргумента format — ArgumentCountError. Указание 0 как номера в %0$s — другая ошибка, ValueError. Для переносимых шаблонов задавайте неотрицательные ограниченные width/precision: реализация имеет дополнительные детали, например -1 как специальную точность g/G/h/H; не используйте их вместо ясного формата без precision.
<?php
try { sprintf('%s: %d', 'BR41'); }
catch (ArgumentCountError) { echo "Не хватает отдельного значения\n"; }
try { vsprintf('%s: %d', ['BR41']); }
catch (ValueError) { echo "Не хватает элемента массива\n"; }
try { sprintf('%0$s', 'BR41'); }
catch (ValueError) { echo "Номер начинается с 1\n"; }
Ожидаемый результат
Не хватает отдельного значения
Не хватает элемента массива
Номер начинается с 1
Это учебный перехват ошибок программного шаблона. В приложении формат обычно фиксирован автором; текст пользователя передают как значение %s, чтобы его % не управляли форматированием.
Шаблон не экранирует HTML/SQL и не создаёт безопасный пароль. Вывод в браузер ещё может находиться в буфере; printf не гарантирует доставку клиенту. Для денег float имеет двоичную погрешность: храните точное значение в минимальных единицах или используйте подходящую десятичную арифметику, а оформление выполняйте последним шагом.
HTML: кодировка, сущности и выбор флагов
Название товара может содержать <, &, кавычки и любые буквы. В HTML часть этих знаков начинает разметку, поэтому текстовое значение надо превратить в безопасную текстовую запись на границе вывода. Это не удаление символов: браузер покажет прежний текст. Для обычного HTML-текста и значения атрибута, обязательно заключённого в кавычки, используйте htmlspecialchars с явными UTF-8 и ENT_QUOTES | ENT_SUBSTITUTE. Содержимое JavaScript/CSS, имя тега и произвольный URL имеют другие правила; одной этой функции там недостаточно.
- Имя товара: <bolt> & washer.
- htmlspecialchars заменяет < на <, & на & и другие значимые знаки.
- Автор добавляет доверенные <p> и </p> вокруг результата.
- Браузер показывает буквальный <bolt> & washer. Это описание модели; CLI ниже проверяет созданные байты.
| Флаг | Что именно меняет |
|---|---|
| ENT_COMPAT | Экранировать/раскрывать двойные кавычки, оставляя одинарные. |
| ENT_QUOTES | Обрабатывать оба вида кавычек. |
| ENT_NOQUOTES | Не обрабатывать ни один вид кавычек. |
| ENT_SUBSTITUTE | При кодировании заменить некорректную последовательность единиц кодировки на U+FFFD в UTF-8 или числовую сущность; не терять всю строку. |
| ENT_IGNORE | При кодировании молча выбросить повреждённые последовательности; PHP не рекомендует это из-за возможных последствий для безопасности. |
| ENT_DISALLOWED | При кодировании заменять кодовые точки, запрещённые выбранным типом документа; это отличается от неверного байтового UTF-8. |
| ENT_HTML401 / ENT_XML1 / ENT_XHTML / ENT_HTML5 | Выбрать таблицу сущностей и допустимые символы конкретного типа документа. HTML5 нужен для полного набора его сущностей. |
| HTML_SPECIALCHARS / HTML_ENTITIES | Выбор таблицы для get_html_translation_table; это отдельный первый аргумент, не часть flags. |
Вертикальная черта | объединяет биты независимых вариантов. Не объединяйте конкурирующие варианты «какие кавычки» и «какой документ» специально. Правила неоднозначного flags у htmlspecialchars: без флага кавычек — ENT_NOQUOTES; ENT_QUOTES имеет приоритет над ENT_COMPAT. Без типа документа — ENT_HTML401; при конфликте приоритет HTML5, XHTML, XML1, HTML401. Для повреждённых последовательностей ENT_IGNORE имеет приоритет над ENT_SUBSTITUTE; ENT_DISALLOWED дополнительно относится к допустимости кодовых точек документа. С PHP 8.1 значения по умолчанию у пяти функций этой группы стали ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 вместо ENT_COMPAT. Явные параметры делают поведение заметным при чтении кода.
htmlspecialchars/htmlentities/html_entity_decode принимают encoding=null: тогда используется default_charset. Это не определение фактической кодировки входного файла. Пустая строка включает попытку выбора через кодировку скрипта Zend multibyte, default_charset и текущую локаль в таком порядке; PHP не рекомендует полагаться на такой выбор. get_html_translation_table по умолчанию использует UTF-8. Неизвестное имя кодировки даёт предупреждение и fallback к кодировке по умолчанию, поэтому ошибка имени не становится надёжной проверкой входа.
| Кодировка | Поддерживаемые имена и назначение |
|---|---|
| UTF-8 | Многобайтовый Unicode; выбор новых учебных HTML-примеров. |
| ISO-8859-1 | ISO8859-1 — западноевропейский Latin-1. |
| ISO-8859-5 | ISO8859-5 — однобайтовая кириллица. |
| ISO-8859-15 | ISO8859-15 — Latin-9 с евро и дополнительными буквами. |
| cp866 | ibm866, 866 — кириллица DOS. |
| cp1251 | Windows-1251, win-1251, 1251 — кириллица Windows. |
| cp1252 | Windows-1252, 1252 — западноевропейская кодировка Windows. |
| KOI8-R | koi8-ru, koi8r — кириллица. |
| BIG5 / BIG5-HKSCS | BIG5 имеет псевдоним 950; традиционный китайский, HKSCS — расширение Гонконга. |
| GB2312 | 936 — упрощённый китайский. |
| Shift_JIS | SJIS, SJIS-win, cp932, 932 — японский. |
| EUC-JP | EUCJP, eucJP-win — японский. |
| MacRoman | Историческая кодировка Mac OS. |
В htmlspecialchars пять HTML-значимых ASCII-знаков занимают одинаковые позиции в ISO-8859-1/15, UTF-8, cp866/1251/1252 и KOI8-R, но вход всё равно должен быть корректен в объявленной кодировке. Ни htmlspecialchars, ни htmlentities не являются общим преобразователем кодировки. htmlentities превращает только символы с известной именованной сущностью; это не обещание ASCII-выхода для любой буквы. Для числовых сущностей существует mb_encode_numericentity, а реальное перекодирование выполняет mb_convert_encoding/iconv при известной исходной кодировке.
<?php
$broken = "A\xFFB";
echo bin2hex(htmlspecialchars($broken, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')), "\n";
var_dump(htmlspecialchars($broken, ENT_QUOTES, 'UTF-8'));
echo bin2hex(htmlspecialchars("A\x01B", ENT_QUOTES | ENT_SUBSTITUTE | ENT_DISALLOWED | ENT_XML1, 'UTF-8')), "\n";
Ожидаемый результат
41efbfbd42
string(0) ""
41efbfbd42
Первый вход содержит невозможный UTF-8-байт FF. Во втором случае байт 01 — корректная кодовая точка, но запрещённая XML 1; ENT_DISALLOWED заменяет именно её. Без substitute/ignore кодирование повреждённого UTF-8 может вернуть пустую строку целиком.
<?php
$literal = 'Напишите & в файле';
echo htmlspecialchars($literal, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), "\n";
echo htmlspecialchars(''', ENT_QUOTES | ENT_HTML401, 'UTF-8', false), "\n";
echo htmlspecialchars(''', ENT_QUOTES | ENT_HTML5, 'UTF-8', false), "\n";
Ожидаемый результат
Напишите &amp; в файле
&apos;
'
Двойное кодирование по умолчанию правильно, когда пользователь хочет видеть буквальное &. double_encode=false сохраняет только уже распознанные сущности выбранного типа документа: ' входит в HTML5, но не HTML401. Это не универсальное средство исправления двойного экранирования.
Локаль: категории, денежные поля и системные имена
Локаль — набор соглашений окружения, например десятичный разделитель или порядок сравнения. Она не является кодировкой каждой строки и не переводит язык. Меняйте только нужную категорию, проверяйте результат и восстанавливайте прежнее значение. Если библиотека позволяет передать локаль объекту NumberFormatter/Collator, это обычно удобнее глобального setlocale.
| Категория | На что влияет |
|---|---|
| LC_ALL | Все категории одновременно; не выбирайте ради одного разделителя. |
| LC_COLLATE | strcoll: порядок строк. |
| LC_CTYPE | Классификация байтов, например ctype; не возвращает прежнюю зависимость strtolower от локали в PHP 8.2+. |
| LC_MONETARY | Денежные сведения localeconv. |
| LC_NUMERIC | Десятичный разделитель ряда операций, например %f. |
| LC_TIME | Историческое strftime (deprecated с PHP 8.1); для нового локализованного вывода времени — IntlDateFormatter. |
| LC_MESSAGES | Сообщения системы; наличие зависит от сборки с libintl. |
localeconv возвращает следующие поля. В локали C денежные соглашения обычно не заданы: пустая строка или CHAR_MAX означают отсутствие подходящего значения, а не валюту с 127 знаками после запятой. CHAR_MAX — предоставляемая PHP константа системного предела типа char; сравнивайте с ней, не полагаясь на зашитое число 127 для всех платформ.
| Поля localeconv | Значение |
|---|---|
| decimal_point / thousands_sep / grouping | Дробный разделитель, разделитель групп, массив размеров групп для обычного числа. |
| int_curr_symbol / currency_symbol | Международный код/обозначение валюты; международная системная строка может содержать пробел. |
| mon_decimal_point / mon_thousands_sep / mon_grouping | Те же правила для денежных значений. |
| positive_sign / negative_sign | Строковые знаки положительной и отрицательной суммы. |
| int_frac_digits / frac_digits | Дробные знаки международного/местного денежного оформления, если определены. |
| p_cs_precedes / n_cs_precedes | 1 — символ валюты перед положительным/отрицательным числом, 0 — после. |
| p_sep_by_space / n_sep_by_space | Промежуток между валютой и положительным/отрицательным значением; проверяйте определённость системного поля. |
| p_sign_posn / n_sign_posn | 0 — скобки вокруг числа и валюты; 1 — знак перед ними; 2 — после; 3 — непосредственно перед валютой; 4 — непосредственно после валюты. |
grouping/mon_grouping читаются от правой группы к левым: 3,2 означает сначала три цифры справа, затем две левее. Элемент 0 повторяет предыдущий размер; CHAR_MAX прекращает дальнейшее разбиение. Не подставляйте эти массивы в number_format как готовое правило: эта функция группирует по три и не реализует все денежные соглашения.
nl_langinfo недоступна в Windows. В Unix наличие отдельных констант и значений зависит от системы/локали; проверяйте function_exists и defined до обращения. Возвращается строка даже для числовых/групповых сведений; некоторые элементы могут быть бинарным системным представлением, а не печатным десятичным числом. Полная группа имён из официальной страницы:
| Группа nl_langinfo | Константы и их смысл |
|---|---|
| Время: дни/месяцы | ABDAY_1…7 / DAY_1…7 — краткие/полные дни, DAY_1 означает воскресенье. ABMON_1…12 / MON_1…12 — краткие/полные месяцы. |
| Время: форматы | AM_STR, PM_STR; D_T_FMT, D_FMT, T_FMT, T_FMT_AMPM — подписи и исторические шаблоны strftime. |
| Время: эры | ERA, ERA_YEAR, ERA_D_T_FMT, ERA_D_FMT, ERA_T_FMT — альтернативная эра, её год и форматы; ALT_DIGITS — альтернативные символы цифр. |
| Валюта | INT_CURR_SYMBOL, CURRENCY_SYMBOL, CRNCYSTR; MON_DECIMAL_POINT, MON_THOUSANDS_SEP, MON_GROUPING; POSITIVE_SIGN, NEGATIVE_SIGN; INT_FRAC_DIGITS, FRAC_DIGITS. CRNCYSTR указан PHP как эквивалент CURRENCY_SYMBOL. |
| Положение валюты/знака | P_CS_PRECEDES, P_SEP_BY_SPACE, N_CS_PRECEDES, N_SEP_BY_SPACE, P_SIGN_POSN, N_SIGN_POSN — те же группы правил, что в localeconv. |
| Обычные числа | DECIMAL_POINT / RADIXCHAR; THOUSANDS_SEP / THOUSEP; GROUPING. |
| Сообщения | YESEXPR, NOEXPR — системные регулярные выражения ответа да/нет (не переносите как готовый PCRE-шаблон); YESSTR, NOSTR — слова ответа. |
| Кодировка | CODESET — название кодировки выбранной LC_CTYPE, а не определение кодировки произвольного файла. |
Предопределённые константы: полный указатель групп
Именованная константа делает выбор режима понятным. Пишите STR_PAD_LEFT, а не запомненное число; складывать несвязанные режимы нельзя. Только флаги, для которых это предусмотрено, объединяются побитовым |. Здесь перечислены все группы официальной страницы Strings constants; системные nl_langinfo-константы могут отсутствовать на Windows/в конкретной сборке.
| Группа | Состав и подробное объяснение |
|---|---|
| Дополнение строки | STR_PAD_LEFT, STR_PAD_RIGHT, STR_PAD_BOTH → str_pad |
| Таблицы сущностей | HTML_SPECIALCHARS, HTML_ENTITIES → get_html_translation_table |
| Флаги HTML | ENT_COMPAT, ENT_QUOTES, ENT_NOQUOTES, ENT_IGNORE, ENT_SUBSTITUTE, ENT_DISALLOWED, ENT_HTML401, ENT_XML1, ENT_XHTML, ENT_HTML5 → все различия |
| Поддержка crypt | CRYPT_SALT_LENGTH, CRYPT_STD_DES, CRYPT_EXT_DES, CRYPT_MD5, CRYPT_BLOWFISH, CRYPT_SHA256, CRYPT_SHA512. Последние шесть индикаторов по текущей официальной документации равны 1; это поддержка, не рекомендация алгоритма. Поля солей и ограничения → crypt |
| Системный маркер | CHAR_MAX — системный предел char, используется как маркер отсутствующих соглашений/остановки группировки; localeconv |
| Категории локали | LC_CTYPE, LC_NUMERIC, LC_TIME, LC_COLLATE, LC_MONETARY, LC_ALL, LC_MESSAGES → область каждой категории |
| Названия дней nl_langinfo | ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7; DAY_1, DAY_2, DAY_3, DAY_4, DAY_5, DAY_6, DAY_7 → сокращённые/полные дни, первый — воскресенье. |
| Названия месяцев nl_langinfo | ABMON_1, ABMON_2, ABMON_3, ABMON_4, ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9, ABMON_10, ABMON_11, ABMON_12; MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7, MON_8, MON_9, MON_10, MON_11, MON_12 → сокращённые/полные месяцы. |
| Остальные nl_langinfo | AM_STR, PM_STR, D_T_FMT, D_FMT, T_FMT, T_FMT_AMPM, ERA, ERA_YEAR, ERA_D_T_FMT, ERA_D_FMT, ERA_T_FMT, ALT_DIGITS; INT_CURR_SYMBOL, CURRENCY_SYMBOL, CRNCYSTR, MON_DECIMAL_POINT, MON_THOUSANDS_SEP, MON_GROUPING, POSITIVE_SIGN, NEGATIVE_SIGN, INT_FRAC_DIGITS, FRAC_DIGITS, P_CS_PRECEDES, P_SEP_BY_SPACE, N_CS_PRECEDES, N_SEP_BY_SPACE, P_SIGN_POSN, N_SIGN_POSN; DECIMAL_POINT, RADIXCHAR, THOUSANDS_SEP, THOUSEP, GROUPING; YESEXPR, NOEXPR, YESSTR, NOSTR, CODESET → значение каждой группы и правила наличия |
<?php
echo str_pad('41', 5, '0', STR_PAD_LEFT), "\n";
$flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5;
echo htmlspecialchars("BR'41", $flags, 'UTF-8'), "\n";
var_dump(defined('CHAR_MAX'));
Ожидаемый результат
00041
BR'41
bool(true)
STR_PAD_LEFT — один режим. ENT_* в этом сочетании независимы: кавычки, подстановка повреждений, тип документа. Значение определённой константы читается без кавычек; defined принимает её имя строкой.
Версии и миграция старых вызовов
Версии влияют на результат и отказ, а не только на наличие имени. Ниже переходы официального changelog сгруппированы по действию; конкретные параметры и проверенные случаи находятся в карточках. Исторический код с предупреждением/false нельзя автоматически перенести в PHP 8 без обработки ValueError/TypeError. Уточнения, где старое описание двусмысленно, дополнены наблюдением текущего PHP 8.5.11.
| Версия | Что проверить при переносе |
|---|---|
| 8.5 | chr: значения вне 0…255 deprecated; ord: строка не из одного байта deprecated; setlocale: используйте строку "0", а не специальный integer 0. Семейство printf исправило точность точки без числа (%.F → precision 0). |
| 8.4 | str_getcsv: явно задайте escape, иначе deprecated; неверная длина separator/enclosure/escape — ValueError. strcspn с пустым набором больше не останавливается на NUL. |
| 8.3 | number_format поддерживает отрицательные decimals; strrchr получил before_needle; strtok предупреждает о продолжении без начала; последний CSV-field с незакрытой кавычкой теперь "", не NUL. str_increment/str_decrement добавлены (их наличие отмечено в карточках). |
| 8.2 | ASCII-регистр стал независим от локали у case/search/replace функций. str_split("") возвращает []; сравнения strcmp/strcasecmp/strncmp/strncasecmp/strnatcmp/strnatcasecmp/substr_compare обещают знак, а не разность длин. utf8_encode/decode deprecated. |
| 8.1 | Пять HTML-функций изменили flags по умолчанию на ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401; crypt можно собирать с --with-external-libcrypt. |
| 8.0: удаление и обязательность | Удалены convert_cyr_string, hebrevc, money_format; crypt требует salt; parse_str требует result; implode больше не принимает (array, separator). |
| 8.0: пустые значения и границы | explode с пустым разделителем, str_split с длиной <1, wordwrap с пустым break → ValueError. convert_uuencode/quotemeta/soundex корректно обрабатывают пустую строку вместо старого false. substr возвращает "" вместо прежнего false; поиски допускают пустой needle и больше не трактуют int как код символа. |
| 8.0: nullable и число аргументов | nullable length: substr/substr_replace/substr_compare/substr_count/strspn/strcspn; nullable characters у str_word_count, allowed_tags у strip_tags, encoding у htmlentities/html_entity_decode. number_format допускает 3 аргумента; levenshtein допускает 3/4 и строки длиннее 255. |
| 8.0: диагностика | Семейство printf получает исключения вместо прежних предупреждений/false при неправильном формате/списке; count_chars/metaphone тоже больше не используют прежний false при ошибке. Конкретные классы указаны в карточках. |
| 7.4 / 7.3 | В 7.4 устарели три позднее удалённые функции и старый порядок implode; strip_tags допускает массив тегов; CSV escape="" отключает механизм; chr перестал молча принимать неподдерживаемые типы/коды и превращать их в 0. В 7.3 int needle deprecated. |
| 7.2.18 / 7.3.5 | substr_compare допускает offset на конце строки. |
| 7.2 | number_format больше не выдаёт -0; parse_str без result deprecated; utf8_encode/decode перенесены из XML в core. |
| 7.1 | str_shuffle использует общий Mersenne Twister; strpos/stripos поддерживают отрицательный offset; substr_count — отрицательные offset/length и длину 0. |
Собрать операции в понятный обработчик поля
Небольшая задача объединяет три уже изученных шага: принять текстовый артикул, нормализовать крайние пробелы, проверить ASCII-серию и сформировать сообщение. Пока входы заданы в коде, поэтому здесь нет HTTP-запроса и сохранения. Для реальной формы сначала проверьте, что поле является строкой, затем допустимую длину и весь формат; показ в HTML — отдельный последний шаг.
<?php
function itemMessage(mixed $input): string
{
if (!is_string($input)) { return 'Ошибка типа'; }
$sku = strtoupper(trim($input));
if (!str_starts_with($sku, 'BR-') || strlen($sku) !== 5) { return 'Нужен код BR- и две цифры'; }
if (strspn($sku, '0123456789', 3) !== 2) { return 'Нужны две ASCII-цифры'; }
return sprintf('Выбран %s', $sku);
}
foreach ([' br-41 ', 'BR-AA', ['BR-41']] as $input) {
echo htmlspecialchars(itemMessage($input), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), "\n";
}
Ожидаемый результат
Выбран BR-41
Нужны две ASCII-цифры
Ошибка типа
mixed допускает любой вход, is_string останавливает массив. strlen проверяет договор ASCII из пяти байтов; strspn проверяет две конечные цифры. return завершает текущий вызов. Эта функция проверяет только форму кода, не существование товара и не права на заказ.
Упражнение. Разрешите также серию NU- с теми же двумя цифрами, не разрешая пустой код и массив. Сначала предложите ответ для "nu-09", "NU-9", "BR-0A". Ожидаются успех, ошибка длины, ошибка цифр. Решение: сохранить общую нормализацию, заменить проверку префикса на два явных str_starts_with, объединённых ||, и оставить прежние проверки длины/цифр. Для разделителя в имени товара перейдите к CSV-карточке, а не расширяйте этот код случайными replace.
Все функции раздела Strings
Это полный набор 105 официальных карточек book.strings в зафиксированном оглавлении. Часть основ уже находилась в главе языка «Строки»: ссылки ведут к прежним ID с новым вводным объяснением. Псевдонимы и удалённые функции доступны отдельно для чтения старого кода.
Длины, границы и байты. trim, ltrim, rtrim, chop, strlen, substr, substr_replace, str_pad, str_repeat, str_split, strrev, chunk_split, wordwrap, str_increment, str_decrement, chr, ord, bin2hex, hex2bin.
Поиск и сравнение. str_contains, str_starts_with, str_ends_with, strpos, stripos, strrpos, strripos, strstr, strchr, stristr, strrchr, strpbrk, strcspn, strspn, substr_count, substr_compare, strcmp, strcasecmp, strncmp, strncasecmp, strnatcmp, strnatcasecmp.
Разделение, замены и регистр. explode, implode, join, str_replace, str_ireplace, strtr, strtolower, strtoupper, lcfirst, ucfirst, ucwords, str_word_count, strtok, count_chars, str_shuffle, str_rot13, stripcslashes, stripslashes, addslashes, addcslashes, quotemeta.
Вывод и форматы. echo, print, printf, sprintf, fprintf, vprintf, vsprintf, vfprintf, sscanf, number_format, parse_str, str_getcsv, nl2br.
HTML и сущности. htmlspecialchars, htmlspecialchars_decode, htmlentities, html_entity_decode, get_html_translation_table, strip_tags.
Локали и старые кодировки. setlocale, localeconv, strcoll, nl_langinfo, utf8_encode, utf8_decode, convert_cyr_string, hebrev, hebrevc, money_format.
Хеши, сходство и транспортные кодировки. crypt, md5, md5_file, sha1, sha1_file, crc32, soundex, metaphone, levenshtein, similar_text, quoted_printable_encode, quoted_printable_decode, convert_uuencode, convert_uudecode.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
ltrimСнять служебный отступ слева, сохранив значимые пробелы справа.
ltrim(string $string, string $characters = " \n\r\t\v\x00"): stringВ записи ltrim($text) одна строка передаётся в функцию. Второй аргумент необязателен; запятая отделяет его от текста.
| Часть договора | Что передать и что получить |
|---|---|
| string | Исходный текст; значение переменной само не меняется. |
| characters | Набор отдельных байтов для удаления. По умолчанию: пробел, TAB, LF, CR, вертикальная табуляция и NUL; form feed сюда не входит. Диапазон записывается как a..z. Это не подстрока и не регулярное выражение. |
| Результат | Новая строка без подходящих крайних байтов слева. Строка только из удаляемых байтов превращается в "". |
| Ошибки и границы | Неподходящий тип в строгом вызове даёт TypeError; нормальный результат, включая "", не является признаком ошибки. |
| Версия / расширение | PHP 4+; standard |
- Исходный текст находится между видимыми скобками: [ Отчёт ].
- ltrim удаляет подходящие крайние байты только слева.
- Ответ: [Отчёт ]; противоположная сторона сохранена.
<?php
$text = ' Отчёт ';
echo '[', ltrim($text), "]\n";
echo '[', $text, "]\n";
Ожидаемый результат
[Отчёт ]
[ Отчёт ]
Квадратные скобки — подсказка для глаз, не часть входных данных. Возвращается новая строка, исходная переменная остаётся прежней. Второй аргумент задаёт набор отдельных удаляемых байтов, а не слово или целый суффикс.
<?php
$line = "\t stock=8 \r\n";
$clean = ltrim($line, " \t");
echo 'После обработки: ', str_replace(["\r", "\n", "\t"], ['<CR>', '<LF>', '<TAB>'], $clean), "\n";
Ожидаемый результат
После обработки: stock=8 <CR><LF>
Маска содержит пробел и TAB: только они считаются служебным отступом. Переводы строк справа остаются. str_replace в последней строке лишь делает невидимые символы видимыми. Если нужно убрать строго один конечный CRLF, rtrim не подходит: он снимает всю последовательность подходящих крайних байтов.
Маска не означает слово: после удаления крайнего байта следующий становится крайним и тоже проверяется. Не задавайте UTF-8-букву как байтовую маску: можно оборвать другой символ. По умолчанию NBSP U+00A0 не удаляется; в PHP 8.4+ для Unicode-пробелов есть mb_trim/mb_ltrim/mb_rtrim. HTML-сущность сначала требует осознанного разбора HTML. Снятие пробелов не проверяет допустимость значения, не экранирует HTML и не делает путь безопасным.
Проверьте себя. Почему код "0" нельзя считать пустым через empty? В примере после обработки это непустая строка: сравнивайте с "" строго.
Сопоставьте: trim, rtrim. Байты, символы и выбор операции.
rtrimУбрать окончание прочитанной строки, сохранив отступы и пробелы самого текста.
rtrim(string $string, string $characters = " \n\r\t\v\x00"): stringВ записи rtrim($text) одна строка передаётся в функцию. Второй аргумент необязателен; запятая отделяет его от текста.
| Часть договора | Что передать и что получить |
|---|---|
| string | Исходный текст; значение переменной само не меняется. |
| characters | Набор отдельных байтов для удаления. По умолчанию: пробел, TAB, LF, CR, вертикальная табуляция и NUL; form feed сюда не входит. Диапазон записывается как a..z. Это не подстрока и не регулярное выражение. |
| Результат | Новая строка без подходящих крайних байтов справа. Строка только из удаляемых байтов превращается в "". |
| Ошибки и границы | Неподходящий тип в строгом вызове даёт TypeError; нормальный результат, включая "", не является признаком ошибки. |
| Версия / расширение | PHP 4+; standard |
- Исходный текст находится между видимыми скобками: [ Отчёт ].
- rtrim удаляет подходящие крайние байты только справа.
- Ответ: [ Отчёт]; противоположная сторона сохранена.
<?php
$text = ' Отчёт ';
echo '[', rtrim($text), "]\n";
echo '[', $text, "]\n";
Ожидаемый результат
[ Отчёт]
[ Отчёт ]
Квадратные скобки — подсказка для глаз, не часть входных данных. Возвращается новая строка, исходная переменная остаётся прежней. Второй аргумент задаёт набор отдельных удаляемых байтов, а не слово или целый суффикс.
<?php
$line = "\t stock=8 \r\n";
$clean = rtrim($line, "\r\n");
echo 'После обработки: ', str_replace(["\r", "\n", "\t"], ['<CR>', '<LF>', '<TAB>'], $clean), "\n";
Ожидаемый результат
После обработки: <TAB> stock=8
В маске только CR и LF. Поэтому справа убрано окончание строки, а пробел перед ним и отступ слева сохранены. str_replace в последней строке лишь делает невидимые символы видимыми. Если нужно убрать строго один конечный CRLF, rtrim не подходит: он снимает всю последовательность подходящих крайних байтов.
Маска не означает слово: после удаления крайнего байта следующий становится крайним и тоже проверяется. Не задавайте UTF-8-букву как байтовую маску: можно оборвать другой символ. По умолчанию NBSP U+00A0 не удаляется; в PHP 8.4+ для Unicode-пробелов есть mb_trim/mb_ltrim/mb_rtrim. HTML-сущность сначала требует осознанного разбора HTML. Снятие пробелов не проверяет допустимость значения, не экранирует HTML и не делает путь безопасным.
Проверьте себя. Почему код "0" нельзя считать пустым через empty? В примере после обработки это непустая строка: сравнивайте с "" строго.
Сопоставьте: trim, ltrim. Байты, символы и выбор операции.
chopУбрать окончание прочитанной строки, сохранив отступы и пробелы самого текста. chop — другое имя rtrim; в новом коде rtrim понятнее показывает направление.
chop(string $string, string $characters = " \n\r\t\v\x00"): stringВ записи chop($text) одна строка передаётся в функцию. Второй аргумент необязателен; запятая отделяет его от текста.
| Часть договора | Что передать и что получить |
|---|---|
| string | Исходный текст; значение переменной само не меняется. |
| characters | Набор отдельных байтов для удаления. По умолчанию: пробел, TAB, LF, CR, вертикальная табуляция и NUL; form feed сюда не входит. Диапазон записывается как a..z. Это не подстрока и не регулярное выражение. |
| Результат | Новая строка без подходящих крайних байтов справа. Строка только из удаляемых байтов превращается в "". |
| Ошибки и границы | Неподходящий тип в строгом вызове даёт TypeError; нормальный результат, включая "", не является признаком ошибки. |
| Версия / расширение | PHP 4+; standard |
- Исходный текст находится между видимыми скобками: [ Отчёт ].
- chop удаляет подходящие крайние байты только справа.
- Ответ: [ Отчёт]; противоположная сторона сохранена.
<?php
$text = ' Отчёт ';
echo '[', chop($text), "]\n";
echo '[', $text, "]\n";
Ожидаемый результат
[ Отчёт]
[ Отчёт ]
Квадратные скобки — подсказка для глаз, не часть входных данных. Возвращается новая строка, исходная переменная остаётся прежней. Второй аргумент задаёт набор отдельных удаляемых байтов, а не слово или целый суффикс.
<?php
$line = "\t stock=8 \r\n";
$clean = chop($line, "\r\n");
echo 'После обработки: ', str_replace(["\r", "\n", "\t"], ['<CR>', '<LF>', '<TAB>'], $clean), "\n";
Ожидаемый результат
После обработки: <TAB> stock=8
В маске только CR и LF. Поэтому справа убрано окончание строки, а пробел перед ним и отступ слева сохранены. str_replace в последней строке лишь делает невидимые символы видимыми. Если нужно убрать строго один конечный CRLF, rtrim не подходит: он снимает всю последовательность подходящих крайних байтов.
Маска не означает слово: после удаления крайнего байта следующий становится крайним и тоже проверяется. Не задавайте UTF-8-букву как байтовую маску: можно оборвать другой символ. По умолчанию NBSP U+00A0 не удаляется; в PHP 8.4+ для Unicode-пробелов есть mb_trim/mb_ltrim/mb_rtrim. HTML-сущность сначала требует осознанного разбора HTML. Снятие пробелов не проверяет допустимость значения, не экранирует HTML и не делает путь безопасным.
Проверьте себя. Почему код "0" нельзя считать пустым через empty? В примере после обработки это непустая строка: сравнивайте с "" строго.
Сопоставьте: trim, ltrim, rtrim. Байты, символы и выбор операции.
substr_replaceНужно изменить известный участок технической строки: заменить код склада в этикетке или вставить разделитель в номер. substr_replace задаёт место и длину заменяемого участка. Это отличается от поиска всех совпадений через str_replace.
substr_replace(array|string $string, array|string $replace, array|int $offset, array|int|null $length = null): string|array| Часть договора | Что передать и что получить |
|---|---|
| string | Одна строка либо массив строк. |
| replace | Текст замены; для массива входов возможен массив соответствующих замен. |
| offset | Байтовая позиция, отрицательная — от конца; для массива входов возможен массив позиций. |
| length | Сколько заменить; 0 вставляет; null — до конца; отрицательное оставляет указанное число байтов в конце. Для массива возможны отдельные длины. |
| Результат | Новая строка либо массив результатов по виду первого аргумента. |
| Ошибки и границы | Несовместимые типы дают TypeError. Для одной строки нельзя произвольно подставлять массивы offset/length; формат входов должен соответствовать перегрузке. |
| Версия / расширение | PHP 4+; nullable length PHP 8.0+; standard |
- В "OLD-0042" первые три байта OLD — код склада.
- substr_replace($label, "NEW", 0, 3) заменяет именно этот участок.
- Номер остаётся 0042; результат NEW-0042 — новая строка.
<?php
$label = 'OLD-0042';
echo substr_replace($label, 'NEW', 0, 3), "\n";
echo substr_replace('0042', '-', 2, 0), "\n";
echo substr_replace('ORDER-0042', '', 0, 6), "\n";
Ожидаемый результат
NEW-0042
00-42
0042
Аргументы: исходный текст, новый текст, начало и длина удаляемого участка. Длина 0 означает вставку без удаления, пустая замена — удаление. Все позиции байтовые. В первом примере формат фиксирован; если префикс неизвестен, сначала проверяют его.
<?php
$reference = 'REQUEST-781245';
$shown = substr_replace($reference, '****', -6, 4);
echo 'Заявка: ', $shown, "\n";
echo 'Исходный номер: ', $reference, "\n";
Ожидаемый результат
Заявка: REQUEST-****45
Исходный номер: REQUEST-781245
Начало -6 отсчитывается от конца, длина 4 заменяет первые четыре цифры номера. Так можно ограничить отображение внутреннего идентификатора, когда полный номер не нужен. Маскирование на странице не удаляет исходное значение из памяти и не заменяет проверку доступа.
<?php
$labels = ['OLD-0042', 'OLD-0043'];
$updated = substr_replace($labels, 'NEW', 0, 3);
echo implode(', ', $updated), "\n";
echo substr_replace('ABC-42-END', 'X', 4, -4), "\n";
Ожидаемый результат
NEW-0042, NEW-0043
ABC-X-END
Массив исходных строк даёт массив результатов. Отрицательная длина задаёт конец заменяемой области относительно конца строки: -4 оставляет суффикс -END. Списки смещений, длин и замен работают по договору, описанному в таблице параметров; не используйте байтовые позиции для середины русской буквы.
Если неизвестно положение текста, подходит str_replace или сначала поиск. Все позиции здесь байтовые. В массовом варианте лучше подавать согласованные массивы одинаковой длины и явно проверять структуру, а не зависеть от неявных подстановок отсутствующих элементов.
Сопоставьте: substr, str_replace, strlen. Байты, символы и выбор операции.
str_padНа наклейке нужен номер фиксированного вида, например 000042 вместо 42. str_pad дополняет строку до минимальной длины выбранным заполнителем. Длинный номер он не обрезает.
str_pad(string $string, int $length, string $pad_string = " ", int $pad_type = STR_PAD_RIGHT): string| Часть договора | Что передать и что получить |
|---|---|
| string | Начальная строка. |
| length | Желаемая итоговая длина в байтах; меньшая, равная или отрицательная не обрезает вход. |
| pad_string | Непустой шаблон заполнения; последняя копия может быть обрезана. |
| pad_type | STR_PAD_LEFT, STR_PAD_RIGHT (по умолчанию) или STR_PAD_BOTH; нечётный лишний байт при BOTH уходит вправо. |
| Результат | Строка с заполнением до минимума; исходный текст не обрезается. |
| Ошибки и границы | Пустой pad_string или неизвестный pad_type при требуемом заполнении — ValueError. Размер результата необходимо ограничить до вызова. |
| Версия / расширение | PHP 4.0.1+; standard |
- "42" — два ASCII-байта; требуемая минимальная длина — 6.
- STR_PAD_LEFT означает добавить недостающие четыре нуля слева.
- Получается "000042" — строка для отображения, не новое числовое значение.
<?php
$number = '42';
$label = str_pad($number, 6, '0', STR_PAD_LEFT);
echo 'Заказ № ', $label, "\n";
echo str_pad('1234567', 6, '0', STR_PAD_LEFT), "\n";
Ожидаемый результат
Заказ № 000042
1234567
Константа STR_PAD_LEFT выбирает левую сторону. Если аргумент не задан, дополнение идёт справа. Второй результат длиннее шести знаков: задана минимальная, а не максимальная длина. Уникальные номера создаёт хранилище, а str_pad лишь оформляет уже имеющийся номер.
<?php
$stock = ['BRUSH' => 8, 'TAPE' => 15];
foreach ($stock as $code => $quantity) {
echo str_pad($code, 8, '.'), $quantity, "\n";
}
echo str_pad('SALE', 9, '-', STR_PAD_BOTH), "\n";
Ожидаемый результат
BRUSH...8
TAPE....15
--SALE---
Заполнитель . добавляется справа до восьми байтов. STR_PAD_BOTH распределяет заполнение по сторонам; при нечётном остатке справа один знак больше. Для русского текста str_pad считает байты, поэтому столбцы могут разъехаться. mb_str_pad учитывает кодовые точки, но не фактическую ширину шрифта; для сайта используйте HTML-таблицу.
Ширина измеряется байтами, а не экранными колонками: русский текст и emoji так не выравнивают. PHP 8.3+ mb_str_pad работает с символами, но тоже не обещает ширину терминальной колонки. Для веб-таблицы используют HTML/CSS, а не пробелы.
Сопоставьте: sprintf, strlen, str_repeat. Байты, символы и выбор операции.
str_repeatНужна короткая линия в текстовом отчёте или наглядная шкала из повторяющихся знаков. str_repeat создаёт строку из указанного числа копий. Повторяется вся переданная строка, а не только первый символ.
str_repeat(string $string, int $times): string| Часть договора | Что передать и что получить |
|---|---|
| string | Повторяемая строка. |
| times | Количество копий, целое число от0. |
| Результат | Соединённые копии; times=0 или пустая исходная строка дают "". |
| Ошибки и границы | Отрицательное times — ValueError; слишком большой результат может исчерпать память. Лимит задают заранее. |
| Версия / расширение | PHP 4+; standard |
- Один знак # и количество 3 передаются в str_repeat.
- Функция склеивает три копии: ###.
- Количество можно получить из данных, но сначала ограничить размер.
<?php
echo "Кисть: 1 шт.\n";
echo str_repeat('-', 12), "\n";
echo "Итого: 250 руб.\n";
Ожидаемый результат
Кисть: 1 шт.
------------
Итого: 250 руб.
Первый аргумент — повторяемый текст, второй — число повторов. Ответ можно вывести, сохранить или добавить к другой строке. str_repeat("ab", 3) вернул бы ababab: три копии целой строки.
<?php
$requests = ['Пн' => 3, 'Вт' => 5, 'Ср' => 0];
foreach ($requests as $day => $count) {
if ($count < 0 || $count > 20) {
echo $day, ": число вне диапазона\n";
continue;
}
echo $day, ': ', str_repeat('#', $count), ' (', $count, ")\n";
}
Ожидаемый результат
Пн: ### (3)
Вт: ##### (5)
Ср: (0)
Длина шкалы имеет понятный смысл: один # — одна заявка. При нуле повторов получается пустая строка, но числовая подпись сохраняет ясность. Лимит проверяется до создания строки: число из запроса нельзя бесконтрольно использовать как размер. Отрицательное число повторов вызывает ValueError.
Не создавайте многомегабайтные заполнители по числу из запроса. Повторение UTF-8-строки сохраняет её байты, но размер результата растёт на длину всей копии.
Сопоставьте: str_pad, strlen. Байты, символы и выбор операции.
str_splitДлинный ASCII-код удобнее прочитать группами. str_split возвращает массив кусочков заданной длины в байтах, после чего их можно соединить дефисом или обработать по отдельности.
str_split(string $string, int $length = 1): array| Часть договора | Что передать и что получить |
|---|---|
| string | Строка для разбиения. |
| length | Максимум байтов в одном куске, не меньше1; последний кусок может быть короче. |
| Результат | Индексированный массив строк. Пустой вход с PHP 8.2 даёт [], ранее — [""]. |
| Ошибки и границы | length<1 — ValueError с PHP 8.0; большой массив отдельных байтов расходует больше памяти, чем исходная строка. |
| Версия / расширение | PHP 5+; пустая строка → [] PHP 8.2+; standard |
- Код ABCD1234EF56 имеет 12 ASCII-байтов.
- str_split($code, 4) получает ["ABCD", "1234", "EF56"].
- implode("-", $parts) даёт ABCD-1234-EF56.
<?php
$code = 'ABCD1234EF56';
$parts = str_split($code, 4);
echo implode('-', $parts), "\n";
echo 'Первая группа: ', $parts[0], "\n";
Ожидаемый результат
ABCD-1234-EF56
Первая группа: ABCD
str_split делит строку, а implode только оформляет уже полученные группы. Это форматирование заранее созданного демонстрационного кода, не генерация секретного ключа. Последний кусок может быть короче заданной длины.
<?php
$parts = str_split('Я');
echo 'Кусков по одному байту: ', count($parts), "\n";
foreach ($parts as $byte) {
echo 'Байт в HEX: ', bin2hex($byte), "\n";
}
echo 'Кусков в пустой строке: ', count(str_split('')), "\n";
Ожидаемый результат
Кусков по одному байту: 2
Байт в HEX: d0
Байт в HEX: af
Кусков в пустой строке: 0
Каждый байт по отдельности уже не является полной UTF-8-буквой, поэтому выводим шестнадцатеричную запись, а не повреждённый текст. Для букв используют mb_str_split, для графем — grapheme_str_split. С PHP 8.2 пустая строка даёт пустой массив. Длина куска меньше 1 вызывает ValueError.
Для кодовых точек есть mb_str_split, для графем — grapheme_str_split (PHP 8.4+). Для большого потока читайте блоки: массив из каждого байта создаёт лишние элементы.
Сопоставьте: chunk_split, explode, bin2hex. Байты, символы и выбор операции.
strrevВ учебном словесном приложении нужно проверить, одинаково ли ASCII-слово читается с двух сторон. strrev разворачивает порядок байтов. Для пользовательского Unicode-текста это опасное ограничение: байты буквы нельзя переставлять как отдельные буквы.
strrev(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Байтовая строка для разворота. |
| Результат | Новая строка с обратным порядком байтов; пустая строка остаётся пустой. |
| Ошибки и границы | Типовая ошибка строгого вызова — TypeError. Функция не сообщает об испорченной UTF-8 после перестановки байтов. |
| Версия / расширение | PHP 4+; standard |
- Строка LEVEL имеет пять ASCII-байтов.
- После strrev порядок байтов снова образует LEVEL.
- Сравнение исходного и обратного текста даёт ответ для простого ASCII-палиндрома.
<?php
echo strrev('ABCD'), "\n";
echo strrev('LEVEL'), "\n";
Ожидаемый результат
DCBA
LEVEL
Функция возвращает новую строку. Она не сортирует, не шифрует и не знает, где в тексте начинаются слова.
<?php
foreach (['LEVEL', 'TOOL'] as $word) {
echo $word, ': ', strrev($word) === $word ? 'палиндром' : 'не палиндром', "\n";
}
echo 'Байты Я: ', bin2hex('Я'), "\n";
echo 'После strrev: ', bin2hex(strrev('Я')), "\n";
Ожидаемый результат
LEVEL: палиндром
TOOL: не палиндром
Байты Я: d0af
После strrev: afd0
Первое применение рассчитано на согласованные ASCII-слова без пробелов. Русская Я занимает D0 AF, разворот создаёт AF D0 — уже неверную UTF-8-последовательность. Для пользовательских слов нужны графемы и отдельно определённые правила регистра, пробелов и нормализации. Поэтому strrev редко является правильным инструментом для переворота подписи на сайте.
Для текста пользователя сначала определяют единицу разворота: графемы, слова или строки. Разворот списка графем не решает корректную визуальную раскладку двунаправленного текста.
Сопоставьте: str_split, bin2hex. Байты, символы и выбор операции.
chunk_splitДлинную ASCII-запись нужно разбить на строки фиксированной длины — например транспортное представление файла. chunk_split вставляет разделитель после каждого блока, включая последний. Результат — одна строка, а не массив.
chunk_split(string $string, int $length = 76, string $separator = "\r\n"): string| Часть договора | Что передать и что получить |
|---|---|
| string | Строка для группировки. |
| length | Положительная длина блока в байтах; по умолчанию76. |
| separator | Добавляемая последовательность, по умолчанию CRLF; ставится и после последнего блока. |
| Результат | Одна строка с разделителями, а не массив. |
| Ошибки и границы | length≤0 — ValueError; строка результата увеличивается, поэтому ограничивайте вход. |
| Версия / расширение | PHP 4+; standard |
- ABCDEF разбивается на блоки по два байта: AB, CD, EF.
- После каждого добавляется /, в том числе после EF.
- Получается AB/CD/EF/. Если конечный разделитель не нужен, удобнее implode со str_split.
<?php
echo chunk_split('ABCDEF', 2, '/'), "\n";
echo implode('/', str_split('ABCDEF', 2)), "\n";
Ожидаемый результат
AB/CD/EF/
AB/CD/EF
Длина 2 относится к байтам исходной строки. В первом варианте разделитель есть в конце, во втором — только между элементами. Это разные договоры формата, выбирайте нужный заранее.
<?php
$encoded = base64_encode('REPORT: READY');
$wrapped = chunk_split($encoded, 8, "\r\n");
echo $wrapped;
echo 'После обратного декодирования: ', base64_decode($wrapped, true), "\n";
Ожидаемый результат
UkVQT1JU
OiBSRUFE
WQ==
После обратного декодирования: REPORT: READY
Base64 представляет байты ASCII-символами; она не шифрует содержимое. Для наглядности блоки здесь по восемь символов, а не стандартная длина конкретного MIME-формата. Настоящий формат задаёт документация протокола. Вывод в файле содержит CRLF; показанный результат нормализует их как переносы строк. Письмо или запрос этот код не отправляет.
chunk_split не понимает слова, UTF-8 или структуру документа. Для слов — wordwrap, для массива кусков — str_split. Не добавляйте разделители в подписанные данные без договорённости формата.
Сопоставьте: str_split, wordwrap, bin2hex. Байты, символы и выбор операции.
wordwrapВ простом текстовом отчёте для англоязычного терминала нужно переносить длинное предложение по словам. wordwrap ищет пробелы около заданной ширины. Эта ширина измеряется байтами, а не пикселями или буквами Unicode.
wordwrap(string $string, int $width = 75, string $break = "\n", bool $cut_long_words = false): string| Часть договора | Что передать и что получить |
|---|---|
| string | Текст для переноса. |
| width | Желаемая ширина в байтах; задавайте положительное число. |
| break | Непустая строка-разделитель, по умолчанию LF. |
| cut_long_words | false сохраняет длинное слово целиком; true разрешает разрезать его. |
| Результат | Строка с добавленными разделителями; по умолчанию перенос ищется после обычного пробела U+0020. |
| Ошибки и границы | Пустой break — ValueError с PHP 8.0. width=0 вместе с принудительным разрезанием также недопустим. Некорректный UTF-8 после байтового разреза отдельно не диагностируется. |
| Версия / расширение | PHP 4.0.2+; standard |
- Order ready for pickup — предложение из ASCII-слов.
- Ширина 11 позволяет поместить Order ready в первую строку.
- Следующие слова for pickup переходят на вторую.
<?php
$text = 'Order ready for pickup';
echo wordwrap($text, 11, "\n"), "\n";
Ожидаемый результат
Order ready
for pickup
Третий аргумент выбирает символы переноса. По умолчанию длинное слово не разрезается, даже если оно превышает width. Оригинальный текст не меняется.
<?php
$code = 'TRACKING123';
echo 'Сохранить код: ', wordwrap($code, 4, '|'), "\n";
echo 'Разрезать код: ', wordwrap($code, 4, '|', true), "\n";
Ожидаемый результат
Сохранить код: TRACKING123
Разрезать код: TRAC|KING|123
true разрешает разрезать длинное слово. Для кода отслеживания это может мешать копированию, поэтому требование нужно согласовать. Для русского UTF-8 принудительный байтовый разрез может повредить букву. На веб-странице перенос текста обычно решают средствами CSS, а не вставкой переносов через PHP.
Не используйте для русской строки с cut=true: размер байта не совпадает с буквой. Для браузера перенос задаётся CSS; ширина шрифта также не равна числу байтов.
Сопоставьте: chunk_split, str_split, nl2br. Байты, символы и выбор операции.
str_incrementВ приложении с учебными обозначениями полок нужен следующий буквенно-цифровой код. str_increment реализует ASCII-последовательность с переносом между разрядами; это не арифметика номера заказа.
str_increment(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Непустая строка из ASCII-букв и цифр; это специальная последовательность, не произвольное имя. |
| Результат | Соседняя метка с переносом/заёмом между позициями; исходная строка не изменяется. |
| Ошибки и границы | Пустой вход, не-ASCII или символы вне букв/цифр — ValueError. str_decrement также отклоняет значение, которое нельзя уменьшить, например A или0. |
| Версия / расширение | PHP 8.3+; standard |
- A9 — текущая метка.
- str_increment меняет последний разряд с переносом/заёмом.
- Возвращается B0; чтобы продолжить ряд, нужно сохранить ответ.
<?php
echo str_increment('A9'), "\n";
echo str_increment('Z'), "\n";
Ожидаемый результат
B0
AA
Нужен PHP 8.3+. Кавычки оставляют метку строкой. Алгоритм понимает ASCII-буквы и цифры; длина результата может измениться на границе Z/AA. Исходная переменная сама не обновляется.
<?php
$label = 'A9';
for ($i = 0; $i < 2; $i++) {
echo 'Полка ', $label, "\n";
$label = str_increment($label);
}
try {
str_increment('A-9');
} catch (ValueError) {
echo "Дефис не входит в эту последовательность\n";
}
Ожидаемый результат
Полка A9
Полка B0
Дефис не входит в эту последовательность
for выполняет ровно две итерации; на каждой сначала печатается текущая метка, затем вычисляется соседняя. Пустые и неподходящие строки недопустимы; str_decrement также не может уменьшить некоторые начальные значения вроде A или 0. Для уникальных номеров в параллельных запросах нужен отдельный механизм хранилища, эта функция не резервирует значения.
Функции появились в PHP 8.3. Не путайте с арифметикой int или с ++/-- старого PHP: используйте явное имя операции. Уникальность, резервирование и конкурентное изменение номера требуют хранилища и транзакции.
Сопоставьте: str_decrement, str_pad. Байты, символы и выбор операции.
str_decrementВ приложении с учебными обозначениями полок нужен предыдущий буквенно-цифровой код. str_decrement реализует ASCII-последовательность с переносом между разрядами; это не арифметика номера заказа.
str_decrement(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Непустая строка из ASCII-букв и цифр; это специальная последовательность, не произвольное имя. |
| Результат | Соседняя метка с переносом/заёмом между позициями; исходная строка не изменяется. |
| Ошибки и границы | Пустой вход, не-ASCII или символы вне букв/цифр — ValueError. str_decrement также отклоняет значение, которое нельзя уменьшить, например A или0. |
| Версия / расширение | PHP 8.3+; standard |
- B0 — текущая метка.
- str_decrement меняет последний разряд с переносом/заёмом.
- Возвращается A9; чтобы продолжить ряд, нужно сохранить ответ.
<?php
echo str_decrement('B0'), "\n";
echo str_decrement('AA'), "\n";
Ожидаемый результат
A9
Z
Нужен PHP 8.3+. Кавычки оставляют метку строкой. Алгоритм понимает ASCII-буквы и цифры; длина результата может измениться на границе Z/AA. Исходная переменная сама не обновляется.
<?php
$label = 'B0';
for ($i = 0; $i < 2; $i++) {
echo 'Полка ', $label, "\n";
$label = str_decrement($label);
}
try {
str_decrement('A-9');
} catch (ValueError) {
echo "Дефис не входит в эту последовательность\n";
}
Ожидаемый результат
Полка B0
Полка A9
Дефис не входит в эту последовательность
for выполняет ровно две итерации; на каждой сначала печатается текущая метка, затем вычисляется соседняя. Пустые и неподходящие строки недопустимы; str_decrement также не может уменьшить некоторые начальные значения вроде A или 0. Для уникальных номеров в параллельных запросах нужен отдельный механизм хранилища, эта функция не резервирует значения.
Функции появились в PHP 8.3. Не путайте с арифметикой int или с ++/-- старого PHP: используйте явное имя операции. Уникальность, резервирование и конкурентное изменение номера требуют хранилища и транзакции.
Сопоставьте: str_increment, str_pad. Байты, символы и выбор операции.
chrНекоторые устройства ожидают двоичный протокол: команду и значение передают отдельными байтами. chr создаёт ровно один байт из числа. Сначала увидим печатаемый ASCII-байт, затем соберём небольшой полностью описанный учебный пакет.
chr(int $codepoint): string| Часть договора | Что передать и что получить |
|---|---|
| codepoint | Целое0…255; название параметра не делает его кодовой точкой Unicode. |
| Результат | Строка длиной ровно один байт. |
| Ошибки и границы | С PHP 8.5 число вне0…255 deprecated; исторически берётся младший байт. Не опирайтесь на это усечение, проверяйте диапазон. |
| Версия / расширение | PHP 4+; вне0…255 deprecated PHP 8.5+; standard |
- Число 65 — код ASCII-буквы A.
- chr(65) возвращает строку из одного байта A.
- Для непечатаемых команд результат показывают через bin2hex; это те же байты в читаемой записи.
<?php
$letter = chr(65);
echo 'Буква: ', $letter, "\n";
echo 'Размер: ', strlen($letter), " байт\n";
Ожидаемый результат
Буква: A
Размер: 1 байт
65 передаётся как целое число, ответ является строкой. chr не превращает произвольный Unicode-код в UTF-8: для такого преобразования есть mb_chr. Рабочий диапазон одного байта — от 0 до 255.
<?php
$brightness = 80;
if ($brightness < 0 || $brightness > 100) {
throw new InvalidArgumentException('Яркость должна быть от 0 до 100');
}
$packet = chr(2) . chr(16) . chr($brightness) . chr(3);
echo 'Пакет: ', bin2hex($packet), "\n";
echo 'Размер пакета: ', strlen($packet), " байта\n";
Ожидаемый результат
Пакет: 02105003
Размер пакета: 4 байта
Учебный договор: 02 — начало, 10 HEX (16 десятичное) — команда яркости, 50 HEX (80 десятичное) — значение, 03 — конец. Точка . соединяет байты. Пример только собирает пакет, не отправляет его и не управляет оборудованием. Настоящий протокол берут из документации устройства; диапазон chr проверяют до вызова, вне 0–255 в PHP 8.5 выдаётся deprecation.
Для UTF-8 по кодовой точке существует mb_chr($value, "UTF-8"); chr(1040) не создаёт русскую А. Для бинарного пакета нескольких чисел удобен pack с явно выбранным форматом.
Сопоставьте: ord, bin2hex. Байты, символы и выбор операции.
ordОт устройства пришли байты, а PHP должен получить их числовые значения. ord читает один байт и возвращает число от 0 до 255. Это обратная сторона chr, но не декодер целого Unicode-символа.
ord(string $character): int| Часть договора | Что передать и что получить |
|---|---|
| character | Строка ровно из одного байта. |
| Результат | Целое0…255 — значение первого байта. |
| Ошибки и границы | С PHP 8.5 пустая или многобайтовая строка deprecated. Историческое чтение только первого байта не является декодированием Unicode. |
| Версия / расширение | PHP 4+; длина≠1 deprecated PHP 8.5+; standard |
- В одном байте "A" хранится значение 65.
- ord("A") возвращает целое 65.
- В учебном пакете байт по индексу 2 хранит яркость; ord превращает его в число 80.
<?php
echo 'Код A: ', ord('A'), "\n";
echo 'Нулевой байт: ', ord("\0"), "\n";
Ожидаемый результат
Код A: 65
Нулевой байт: 0
Нулевой байт законен и даёт число 0. В PHP-строке он не обозначает конец строки. В ord передают ровно один байт; пустая строка или целая многобайтовая строка с PHP 8.5 вызывают deprecation.
<?php
$packet = "\x02\x10\x50\x03";
if (strlen($packet) !== 4 || $packet[0] !== "\x02" || $packet[3] !== "\x03") {
throw new InvalidArgumentException('Повреждён учебный пакет');
}
$command = ord($packet[1]);
$brightness = ord($packet[2]);
if ($command === 16 && $brightness <= 100) {
echo 'Контроллер сообщил яркость: ', $brightness, "%\n";
} else {
echo "Неизвестная команда или значение\n";
}
Ожидаемый результат
Контроллер сообщил яркость: 80%
Квадратные скобки у строки берут байт по индексу с нуля. Прежде чем обращаться к индексам, проверяем длину и маркеры. Затем команду и данные проверяем по смыслу нашего учебного протокола. ord сама не делает ни одной из этих проверок и не подтверждает подлинность ответа.
ord не проверяет всю команду и не заменяет парсер бинарного протокола. Внешний пакет сначала проверяют по длине и структуре; одиночный байт может законно быть0.
Сопоставьте: chr, strlen, bin2hex. Байты, символы и выбор операции.
bin2hexВ импортируемом файле перед первой колонкой оказался невидимый знак. bin2hex помогает увидеть точные байты: каждый байт превращается в две шестнадцатеричные цифры. Это способ диагностики, а не шифрование.
bin2hex(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Любая байтовая строка; это не текст числа в двоичной системе. |
| Результат | ASCII-строка с двумя строчными HEX-цифрами на каждый байт; размер удваивается. |
| Ошибки и границы | Неподходящий тип строгого вызова — TypeError. Кодировка не определяется и содержимое не скрывается. |
| Версия / расширение | PHP 4+; standard |
- Перед словом name находятся три байта EF BB BF — UTF-8 BOM.
- substr($text, 0, 3) берёт только начало учебного образца.
- bin2hex показывает efbbbf; теперь причина лишнего префикса видима.
<?php
$text = "\xEF\xBB\xBFname;stock";
$prefix = substr($text, 0, 3);
echo 'Первые три байта: ', bin2hex($prefix), "\n";
echo 'Обычная буква A: ', bin2hex('A'), "\n";
Ожидаемый результат
Первые три байта: efbbbf
Обычная буква A: 41
Запись \xEF в двойных кавычках создаёт один байт с кодом EF. Шестнадцатеричная запись компактно показывает непечатаемые байты. Функция не определяет автоматически формат файла; здесь конкретный известный префикс проверяется нами.
<?php
$header = "\xEF\xBB\xBFname;stock";
$bom = "\xEF\xBB\xBF";
if (str_starts_with($header, $bom)) {
$header = substr($header, strlen($bom));
}
$columns = explode(';', $header);
echo 'Первая колонка: [', $columns[0], "]\n";
echo 'Вторая колонка: [', $columns[1], "]\n";
Ожидаемый результат
Первая колонка: [name]
Вторая колонка: [stock]
Теперь ключ колонки действительно name, а не name с невидимым началом. Снимается только точная последовательность в начале; нельзя удалять любые «странные символы» из всего файла. Для полноценного CSV нужен fgetcsv. Небольшой искусственный образец безопасен для диагностики; не выводите в HEX реальные токены и личные документы.
HEX не является шифрованием или хешем: исходные байты полностью восстанавливаются. Не пишите таким способом пароли, токены и полные персональные документы в логи.
Сопоставьте: hex2bin, ord, substr. Байты, символы и выбор операции.
hex2binВ документации или журнале пакет часто записан символами 02105003, но устройству нужны четыре настоящих байта. hex2bin восстанавливает байты из такой шестнадцатеричной записи. Это не превращение всей записи в одно число.
hex2bin(string $string): string|false| Часть договора | Что передать и что получить |
|---|---|
| string | Чётное количество HEX-цифр0–9/A–F/a–f без0x и разделителей; пустая строка допустима. |
| Результат | Байтовая строка либо false; это не преобразование в int. "00" даёт байт NUL, не false. |
| Ошибки и границы | Нечётная длина или не-HEX — E_WARNING и false; не передавайте непроверенный вход в надежде на исключение. |
| Версия / расширение | PHP 5.4+; standard |
- Текст "02105003" состоит из восьми ASCII-символов.
- Каждая пара HEX-цифр описывает один байт; hex2bin создаёт четыре байта.
- В третьем байте находится 50 HEX, то есть число 80.
<?php
$hex = '02105003';
$packet = hex2bin($hex);
echo 'Символов в HEX: ', strlen($hex), "\n";
echo 'Байтов в пакете: ', strlen($packet), "\n";
echo 'Яркость: ', ord($packet[2]), "%\n";
Ожидаемый результат
Символов в HEX: 8
Байтов в пакете: 4
Яркость: 80%
Исходник задан автором и корректен. Для внешнего HEX сначала нужны проверки следующего примера. При обратном bin2hex($packet) получим исходную запись в нижнем регистре. Код ничего не отправляет устройству.
<?php
$fields = ['4142', '00', '123', '0x41'];
foreach ($fields as $field) {
if (strlen($field) > 16 || strlen($field) % 2 !== 0 || !ctype_xdigit($field)) {
echo $field, ": неверный HEX\n";
continue;
}
$bytes = hex2bin($field);
if ($bytes === false) {
echo "Не удалось декодировать\n";
continue;
}
echo $field, ': получено ', strlen($bytes), " байт\n";
}
Ожидаемый результат
4142: получено 2 байт
00: получено 1 байт
123: неверный HEX
0x41: неверный HEX
16 символов — выбранный лимит учебного поля, % 2 проверяет чётность, ctype_xdigit — состав. Префикс 0x не входит в допустимый HEX этой функции. Без предварительной проверки ошибочный вход вызвал бы warning и дал false, а не исключение. Пустая строка допустима для hex2bin, но наше поле намеренно требует хотя бы одну цифру.
У корректного HEX всё ещё может быть неверный размер, контрольная сумма или смысл пакета. Для числовой записи используют hexdec/base_convert с их собственными ограничениями точности, а не hex2bin.
Сопоставьте: bin2hex, strlen, ord. Байты, символы и выбор операции.
striposСлужебная метка в ASCII-тексте может прийти с разным регистром. stripos находит её позицию без учёта ASCII-регистра; в PHP 8.2+ русские буквы так не сравниваются.
stripos(string $haystack, string $needle, int $offset = 0): int|falseВ $position = stripos($text, "=") результат сохраняется в переменную. Запись int|false в описании означает два разных вида результата; === проверяет значение вместе с типом.
| Часть договора | Что передать и что получить |
|---|---|
| haystack | Исходные байты; возвращаемая позиция всегда отсчитывается от начала этой строки. |
| needle | Полная искомая подстрока. В PHP 8+ может быть пустой; целое число больше не означает код символа. |
| offset | Неотрицательное значение задаёт старт поиска; отрицательное отсчитывает старт от конца. Это байтовое смещение, не номер буквы. |
| Результат | Позиция первого подходящего вхождения или false; 0 — корректный результат. Позиции нумеруются с нуля. |
| Ошибки и границы | Смещение вне диапазона от -strlen(haystack) до strlen(haystack) даёт ValueError в PHP 8. Пустая иголка разрешена с PHP 8.0. |
| Версия / расширение | PHP 4+; standard |
- "Status: READY" содержит READY с позиции 8.
- stripos($text, "ready") возвращает 8.
- После проверки !== false можно извлечь сообщение или выбрать действие.
<?php
$text = 'Status: READY';
$position = stripos($text, 'ready');
if ($position !== false) {
echo 'Метка найдена в позиции ', $position, "\n";
}
Ожидаемый результат
Метка найдена в позиции 8
Метод поиска не меняет текст. Проверка !== false нужна и здесь: слово могло стоять в самом начале и дать 0. Проверять состояние заказа по произвольному комментарию нельзя — пример относится к известной служебной строке; отдельное поле состояния надёжнее.
<?php
$lines = ['ERROR: printer offline', 'info: started', 'Error: paper empty'];
foreach ($lines as $line) {
if (stripos($line, 'error:') === 0) {
echo trim(substr($line, 6)), "\n";
}
}
var_dump(stripos('ERROR', 'missing'));
var_dump(stripos('Я', 'я'));
Ожидаемый результат
printer offline
paper empty
bool(false)
bool(false)
=== 0 требует метку в начале, а не в любом месте текста. После неё снимаются ровно шесть байтов error:. Последние проверки показывают отсутствие и ограничение ASCII-регистра. Для UTF-8 есть mb_stripos; реальный структурный журнал разбирают по его формату, а не по слову, случайно встретившемуся в сообщении.
Проверка if ($position) теряет найденную позицию 0: используйте !== false. Нельзя считать полученный индекс номером Unicode-буквы. С PHP 8.2 сравнение регистра затрагивает только ASCII и не зависит от setlocale; для Unicode нужен mb_stripos/mb_strripos с явной кодировкой. Отрицательное смещение поддерживается с PHP 7.1. Если нужен только логический ответ, str_contains проще; для Unicode-позиций выбирайте согласованное семейство mb_*.
Сопоставьте: strpos, strrpos, strripos, substr, str_contains. Байты, символы и выбор операции.
strrposИмя файла лежит в составном текстовом пути. Нужно найти последний /, чтобы отделить папку от имени. strrpos возвращает последнюю подходящую позицию; регистр учитывается.
strrpos(string $haystack, string $needle, int $offset = 0): int|falseВ $position = strrpos($text, "=") результат сохраняется в переменную. Запись int|false в описании означает два разных вида результата; === проверяет значение вместе с типом.
| Часть договора | Что передать и что получить |
|---|---|
| haystack | Исходные байты; возвращаемая позиция всегда отсчитывается от начала этой строки. |
| needle | Полная искомая подстрока. В PHP 8+ может быть пустой; целое число больше не означает код символа. |
| offset | Неотрицательное значение исключает более ранние начала совпадений. Отрицательное задаёт крайнюю разрешённую позицию начала, считая от конца: поиск идёт влево от неё включительно. |
| Результат | Позиция последнего подходящего вхождения или false; 0 — корректный результат. Позиции нумеруются с нуля. |
| Ошибки и границы | Смещение вне диапазона от -strlen(haystack) до strlen(haystack) даёт ValueError в PHP 8. Пустая иголка разрешена с PHP 8.0. |
| Версия / расширение | PHP 4+; standard |
- В "reports/2026/orders.csv" последний / стоит после 2026.
- strrpos находит именно этот разделитель, а не первый.
- До него — папка reports/2026; после него — orders.csv.
<?php
$path = 'reports/2026/orders.csv';
$position = strrpos($path, '/');
echo 'Папка: ', substr($path, 0, $position), "\n";
echo 'Имя: ', substr($path, $position + 1), "\n";
Ожидаемый результат
Папка: reports/2026
Имя: orders.csv
Пример работает с заданной строкой, в которой разделитель известен. Это разбор текста с прямыми слешами, а не универсальный разбор Windows/Linux-путей. Для файловых путей сравните dirname, basename и pathinfo; отдельно решайте, какие папки разрешено читать.
<?php
foreach (['reports/orders.csv', 'orders.csv'] as $path) {
$position = strrpos($path, '/');
$name = $position === false ? $path : substr($path, $position + 1);
echo $name, "\n";
}
echo 'Последнее совпадение: ', strrpos('AB|ab|AB', 'AB'), "\n";
echo 'Искать не правее позиции 5: ', strrpos('AB|ab|AB', 'AB', -3), "\n";
Ожидаемый результат
orders.csv
orders.csv
Последнее совпадение: 6
Искать не правее позиции 5: 0
Выражение условие ? значение1 : значение2 выбирает результат в зависимости от наличия разделителя. Отрицательное смещение у поиска последнего вхождения задаёт крайнюю позицию начала совпадения справа: это не просто запуск обычного поиска с этой позиции. Позиции всегда байтовые; выход смещения за диапазон — ValueError.
Проверка if ($position) теряет найденную позицию 0: используйте !== false. Нельзя считать полученный индекс номером Unicode-буквы. Отрицательная граница ограничивает начало совпадения, а не обязательно все его байты. Если нужен только логический ответ, str_contains проще; для Unicode-позиций выбирайте согласованное семейство mb_*.
Сопоставьте: strpos, stripos, strripos, substr, str_contains. Байты, символы и выбор операции.
strriposИмя файла лежит в составном текстовом пути. Нужно найти последний /, чтобы отделить папку от имени. strripos возвращает последнюю подходящую позицию; strripos игнорирует ASCII-регистр.
strripos(string $haystack, string $needle, int $offset = 0): int|falseВ $position = strripos($text, "=") результат сохраняется в переменную. Запись int|false в описании означает два разных вида результата; === проверяет значение вместе с типом.
| Часть договора | Что передать и что получить |
|---|---|
| haystack | Исходные байты; возвращаемая позиция всегда отсчитывается от начала этой строки. |
| needle | Полная искомая подстрока. В PHP 8+ может быть пустой; целое число больше не означает код символа. |
| offset | Неотрицательное значение исключает более ранние начала совпадений. Отрицательное задаёт крайнюю разрешённую позицию начала, считая от конца: поиск идёт влево от неё включительно. |
| Результат | Позиция последнего подходящего вхождения или false; 0 — корректный результат. Позиции нумеруются с нуля. |
| Ошибки и границы | Смещение вне диапазона от -strlen(haystack) до strlen(haystack) даёт ValueError в PHP 8. Пустая иголка разрешена с PHP 8.0. |
| Версия / расширение | PHP 4+; standard |
- В "reports/2026/orders.csv" последний / стоит после 2026.
- strripos находит именно этот разделитель, а не первый.
- До него — папка reports/2026; после него — orders.csv.
<?php
$path = 'reports/2026/orders.csv';
$position = strripos($path, '/');
echo 'Папка: ', substr($path, 0, $position), "\n";
echo 'Имя: ', substr($path, $position + 1), "\n";
Ожидаемый результат
Папка: reports/2026
Имя: orders.csv
Пример работает с заданной строкой, в которой разделитель известен. Это разбор текста с прямыми слешами, а не универсальный разбор Windows/Linux-путей. Для файловых путей сравните dirname, basename и pathinfo; отдельно решайте, какие папки разрешено читать.
<?php
foreach (['reports/orders.csv', 'orders.csv'] as $path) {
$position = strripos($path, '/');
$name = $position === false ? $path : substr($path, $position + 1);
echo $name, "\n";
}
echo 'Последнее совпадение: ', strripos('AB|ab|AB', 'ab'), "\n";
echo 'Искать не правее позиции 5: ', strripos('AB|ab|AB', 'ab', -3), "\n";
Ожидаемый результат
orders.csv
orders.csv
Последнее совпадение: 6
Искать не правее позиции 5: 3
Выражение условие ? значение1 : значение2 выбирает результат в зависимости от наличия разделителя. Отрицательное смещение у поиска последнего вхождения задаёт крайнюю позицию начала совпадения справа: это не просто запуск обычного поиска с этой позиции. Позиции всегда байтовые; выход смещения за диапазон — ValueError.
Проверка if ($position) теряет найденную позицию 0: используйте !== false. Нельзя считать полученный индекс номером Unicode-буквы. С PHP 8.2 сравнение регистра затрагивает только ASCII и не зависит от setlocale; для Unicode нужен mb_stripos/mb_strripos с явной кодировкой. Отрицательная граница ограничивает начало совпадения, а не обязательно все его байты. Если нужен только логический ответ, str_contains проще; для Unicode-позиций выбирайте согласованное семейство mb_*.
Сопоставьте: strpos, stripos, strrpos, substr, str_contains. Байты, символы и выбор операции.
strstrИз простой записи имя=значение нужно получить часть до или после первого разделителя. В отличие от strpos здесь возвращается сама строка, а не её позиция.
strstr(string $haystack, string $needle, bool $before_needle = false): string|falseНеобязательное true — логическое значение без кавычек. Строка "false" была бы непустой строкой, которая в слабом вызове может превратиться в true; передавайте настоящий bool.
| Часть договора | Что передать и что получить |
|---|---|
| haystack | Текст, из которого возвращается часть; его регистр и байты сохраняются. |
| needle | Полная подстрока для поиска с учётом регистра. |
| before_needle | false: часть от начала совпадения до конца, включая совпадение; true: часть перед совпадением, не включая его. |
| Результат | Строка найденной части или false, если совпадения нет. Пустая возвращённая строка может быть успешным ответом. |
| Ошибки и границы | С PHP 8 пустой needle разрешён: при false возвращается весь haystack, при true — "". Целый needle больше не трактуется как код байта. |
| Версия / расширение | PHP 4+; standard |
- delivery=pickup — исходная запись.
- strstr находит первое совпадение =.
- Обычный вызов возвращает =pickup вместе с разделителем; true выбирает часть перед ним.
<?php
$record = 'delivery=pickup';
echo 'От совпадения: [', strstr($record, '='), "]\n";
echo 'До совпадения: [', strstr($record, '=', true), "]\n";
Ожидаемый результат
От совпадения: [=pickup]
До совпадения: [delivery]
Третий аргумент true меняет сторону извлечения. Сам разделитель включён в хвост и не включён в начало. Квадратные скобки показывают оставшиеся пробелы. Исходный текст не меняется.
<?php
foreach (['delivery=pickup', 'unknown'] as $record) {
$tail = strstr($record, '=');
if ($tail === false) {
echo "Нужное поле не найдено\n";
continue;
}
echo 'Значение: ', substr($tail, strlen('=')), "\n";
}
Ожидаемый результат
Значение: pickup
Нужное поле не найдено
false означает отсутствие совпадения. От пустой строки его отличают строго: пустое начало перед совпадением может быть нормальным результатом. strlen метки задаёт число пропускаемых байтов. Для нескольких полей key=value часто удобнее explode с limit=2; для формального протокола используйте его парсер. Пустой needle разрешён с PHP 8 и имеет отдельное поведение, описанное в договоре.
Чтобы только проверить наличие, не создавая фрагмент для дальнейшей работы, используйте str_contains или stripos !== false. Домен/адрес в модели — учебная запись, не полноценный валидатор электронной почты.
Сопоставьте: strchr, stristr, strpos, str_contains. Байты, символы и выбор операции.
strchrИз простой записи имя=значение нужно получить часть до или после первого разделителя. strchr — псевдоним strstr, с тем же поведением. В отличие от strpos здесь возвращается сама строка, а не её позиция.
strchr(string $haystack, string $needle, bool $before_needle = false): string|falseНеобязательное true — логическое значение без кавычек. Строка "false" была бы непустой строкой, которая в слабом вызове может превратиться в true; передавайте настоящий bool.
| Часть договора | Что передать и что получить |
|---|---|
| haystack | Текст, из которого возвращается часть; его регистр и байты сохраняются. |
| needle | Полная подстрока для поиска с учётом регистра. |
| before_needle | false: часть от начала совпадения до конца, включая совпадение; true: часть перед совпадением, не включая его. |
| Результат | Строка найденной части или false, если совпадения нет. Пустая возвращённая строка может быть успешным ответом. |
| Ошибки и границы | С PHP 8 пустой needle разрешён: при false возвращается весь haystack, при true — "". Целый needle больше не трактуется как код байта. |
| Версия / расширение | PHP 4+; standard |
- delivery=pickup — исходная запись.
- strchr находит первое совпадение =.
- Обычный вызов возвращает =pickup вместе с разделителем; true выбирает часть перед ним.
<?php
$record = 'delivery=pickup';
echo 'От совпадения: [', strchr($record, '='), "]\n";
echo 'До совпадения: [', strchr($record, '=', true), "]\n";
Ожидаемый результат
От совпадения: [=pickup]
До совпадения: [delivery]
Третий аргумент true меняет сторону извлечения. Сам разделитель включён в хвост и не включён в начало. Квадратные скобки показывают оставшиеся пробелы. Исходный текст не меняется.
<?php
foreach (['delivery=pickup', 'unknown'] as $record) {
$tail = strchr($record, '=');
if ($tail === false) {
echo "Нужное поле не найдено\n";
continue;
}
echo 'Значение: ', substr($tail, strlen('=')), "\n";
}
Ожидаемый результат
Значение: pickup
Нужное поле не найдено
false означает отсутствие совпадения. От пустой строки его отличают строго: пустое начало перед совпадением может быть нормальным результатом. strlen метки задаёт число пропускаемых байтов. Для нескольких полей key=value часто удобнее explode с limit=2; для формального протокола используйте его парсер. Пустой needle разрешён с PHP 8 и имеет отдельное поведение, описанное в договоре.
strchr полностью повторяет strstr, несмотря на имя: ищет всю needle, а не один символ. Чтобы только проверить наличие, не создавая фрагмент для дальнейшей работы, используйте str_contains или stripos !== false. Домен/адрес в модели — учебная запись, не полноценный валидатор электронной почты.
Сопоставьте: strstr, stristr, strpos, str_contains. Байты, символы и выбор операции.
stristrВ служебном ответе нужно получить часть текста от метки STATUS:, независимо от её ASCII-регистра. В отличие от strpos здесь возвращается сама строка, а не её позиция.
stristr(string $haystack, string $needle, bool $before_needle = false): string|falseНеобязательное true — логическое значение без кавычек. Строка "false" была бы непустой строкой, которая в слабом вызове может превратиться в true; передавайте настоящий bool.
| Часть договора | Что передать и что получить |
|---|---|
| haystack | Текст, из которого возвращается часть; его регистр и байты сохраняются. |
| needle | Полная подстрока для поиска без учёта ASCII-регистра. |
| before_needle | false: часть от начала совпадения до конца, включая совпадение; true: часть перед совпадением, не включая его. |
| Результат | Строка найденной части или false, если совпадения нет. Пустая возвращённая строка может быть успешным ответом. |
| Ошибки и границы | С PHP 8 пустой needle разрешён: при false возвращается весь haystack, при true — "". Целый needle больше не трактуется как код байта. |
| Версия / расширение | PHP 4+; standard |
- Order 42; STATUS:ready — исходная запись.
- stristr находит первое совпадение status:.
- Обычный вызов возвращает STATUS:ready вместе с разделителем; true выбирает часть перед ним.
<?php
$record = 'Order 42; STATUS:ready';
echo 'От совпадения: [', stristr($record, 'status:'), "]\n";
echo 'До совпадения: [', stristr($record, 'status:', true), "]\n";
Ожидаемый результат
От совпадения: [STATUS:ready]
До совпадения: [Order 42; ]
Третий аргумент true меняет сторону извлечения. Сам разделитель включён в хвост и не включён в начало. Квадратные скобки показывают оставшиеся пробелы. Исходный текст не меняется.
<?php
foreach (['Order 42; STATUS:ready', 'unknown'] as $record) {
$tail = stristr($record, 'status:');
if ($tail === false) {
echo "Нужное поле не найдено\n";
continue;
}
echo 'Значение: ', substr($tail, strlen('status:')), "\n";
}
Ожидаемый результат
Значение: ready
Нужное поле не найдено
false означает отсутствие совпадения. От пустой строки его отличают строго: пустое начало перед совпадением может быть нормальным результатом. strlen метки задаёт число пропускаемых байтов. Для нескольких полей key=value часто удобнее explode с limit=2; для формального протокола используйте его парсер. Пустой needle разрешён с PHP 8 и имеет отдельное поведение, описанное в договоре.
С PHP 8.2 учитывается только ASCII-регистр; исходный регистр возвращаемого фрагмента сохраняется. Чтобы только проверить наличие, не создавая фрагмент для дальнейшей работы, используйте str_contains или stripos !== false. Домен/адрес в модели — учебная запись, не полноценный валидатор электронной почты.
Сопоставьте: strstr, strchr, strpos, str_contains. Байты, символы и выбор операции.
strrchrУ имени выгрузки несколько точек, и нужно получить часть от последней. strrchr ищет последний разделитель, но использует только первый байт needle. Для полного окончания вроде .tar.gz это не специальный анализ расширений.
strrchr(string $haystack, string $needle, bool $before_needle = false): string|false| Часть договора | Что передать и что получить |
|---|---|
| haystack | Исходная строка. |
| needle | Используется только первый байт этой строки. Пустая строка означает поиск NUL, а не совпадение с любым концом. |
| before_needle | С PHP 8.3: true возвращает часть перед найденным байтом; по умолчанию false возвращает хвост вместе с ним. |
| Результат | Хвост/начало около последнего совпавшего байта, либо false при отсутствии. |
| Ошибки и границы | Многобайтовая буква в needle не является Unicode-разделителем: поиск использует её первый байт. С PHP 8 needle должен быть строкой, не кодом байта. |
| Версия / расширение | PHP 4+; before_needle — PHP 8.3+; standard |
- archive.stock.csv содержит две точки.
- Последняя точка стоит перед csv.
- strrchr возвращает .csv; необязательный true с PHP 8.3 возвращает archive.stock.
<?php
$name = 'archive.stock.csv';
echo 'Последний суффикс: ', strrchr($name, '.'), "\n";
echo 'До последней точки: ', strrchr($name, '.', true), "\n";
Ожидаемый результат
Последний суффикс: .csv
До последней точки: archive.stock
Второй вывод использует before_needle, доступный с PHP 8.3. Для разбора настоящего файлового пути обычно удобнее pathinfo; здесь изучается ровно операция над строкой.
<?php
foreach (['report.csv', 'README'] as $name) {
$suffix = strrchr($name, '.csv');
echo $name, ': ', $suffix === false ? 'нет точки' : $suffix, "\n";
}
echo strrchr('report.txt', '.csv'), "\n";
Ожидаемый результат
report.csv: .csv
README: нет точки
.txt
Передача .csv не заставляет искать .csv целиком: берётся только первый байт — точка. Поэтому report.txt тоже даёт результат. Используйте str_ends_with для проверки точного суффикса. Пустой needle у strrchr ищет NUL, а не любую пустую границу; Unicode-разделитель также не обрабатывается целой буквой.
strrchr($text, "::") ищет последний двоеточный байт. Это принципиально отличается от strrpos($text, "::"). Для имени файла можно предпочесть pathinfo; одно расширение не доказывает тип содержимого.
Сопоставьте: strrpos, substr, strstr. Байты, символы и выбор операции.
strpbrkВ короткой служебной записи первый разделитель может быть запятой или точкой с запятой. strpbrk находит первый байт из выбранного набора и возвращает хвост от него. Это позволяет определить, где закончился первый элемент.
strpbrk(string $string, string $characters): string|false| Часть договора | Что передать и что получить |
|---|---|
| string | Исходная строка. |
| characters | Непустой набор отдельных байтов. Запись ,; означает запятую ИЛИ точку с запятой; диапазонов и регулярных выражений нет. |
| Результат | Хвост исходной строки от первого найденного байта включительно, либо false. |
| Ошибки и границы | Пустой characters вызывает ValueError в PHP 8; функция чувствительна к регистру и работает с байтами. |
| Версия / расширение | PHP 5+; standard |
- TOOL-42;8,main — запись с двумя видами разделителей.
- Набор ,; означает запятая ИЛИ точка с запятой.
- Первой встречается ;, поэтому ответ ;8,main.
<?php
$record = 'TOOL-42;8,main';
$tail = strpbrk($record, ',;');
echo 'Хвост: ', $tail, "\n";
echo 'Сам разделитель: ', $tail[0], "\n";
Ожидаемый результат
Хвост: ;8,main
Сам разделитель: ;
В известной строке разделитель есть. [0] читает первый байт возвращённого хвоста. Маска ,; — набор, а не двухсимвольная последовательность. Диапазоны вида a-z здесь не задаются.
<?php
foreach (['TOOL-42', 'TOOL-42;TAPE-8', ''] as $code) {
if ($code === '') {
echo "Код не указан\n";
} elseif (strpbrk($code, ',;') !== false) {
echo "Передайте один код, а не список\n";
} else {
echo 'Принят один код: ', $code, "\n";
}
}
Ожидаемый результат
Принят один код: TOOL-42
Передайте один код, а не список
Код не указан
Отсутствие разделителей — только одно правило нашего поля. Допустимый алфавит, длина и существование товара проверяются отдельно. Пустой второй аргумент самой strpbrk вызывает ValueError, поэтому набор разделителей задаёт программа.
UTF-8-символ в наборе распадается на отдельные байты. Для позиции первого разделителя можно вычислить strlen($text)-strlen($tail), но strcspn выражает эту задачу прямо. Для одного полного слова используйте strstr.
Сопоставьте: strcspn, strspn, strstr. Байты, символы и выбор операции.
strspnИз номера партии вида 123-PAINT нужно узнать длину начальной цифровой части. strspn идёт с начала выбранного участка, пока каждый байт входит в разрешённый набор. Число результата говорит о длине префикса, а не о числовом значении.
strspn(string $string, string $characters, int $offset = 0, ?int $length = null): int| Часть договора | Что передать и что получить |
|---|---|
| string | Строка, из которой выбирается окно проверки. |
| characters | Набор байтов разрешённых; диапазона 0..9 здесь НЕТ — перечисляйте 0123456789. |
| offset | Начало окна в байтах; отрицательное — отсчёт от конца. |
| length | null: до конца. Положительное ограничивает окно; отрицательное исключает столько байтов с конца; 0 задаёт пустое окно. |
| Результат | Длина начальной части окна, в которой все байты входят в набор. Это длина, а не индекс в исходной строке. |
| Ошибки и границы | Пустой набор допустим: длина всегда 0. Границы окна работают как при выборе подстроки; это не валидация остальной записи. |
| Версия / расширение | PHP 4+; nullable length — PHP 8.0+; standard |
- В 123-PAINT первые три байта — ASCII-цифры.
- На дефисе, которого нет в наборе, просмотр останавливается.
- Возвращается 3; substr может взять по этой длине строку 123.
<?php
$batch = '123-PAINT';
$length = strspn($batch, '0123456789');
echo 'Длина цифрового префикса: ', $length, "\n";
echo 'Префикс: ', substr($batch, 0, $length), "\n";
Ожидаемый результат
Длина цифрового префикса: 3
Префикс: 123
Набор перечисляет каждый допустимый байт. Запись 0-9 не означает диапазон в этой функции. strspn не превращает префикс в число и не подтверждает правильность остальной части кода.
<?php
foreach (['0', '123', '12kg', ''] as $quantity) {
$allDigits = $quantity !== '' && strspn($quantity, '0123456789') === strlen($quantity);
echo '[', $quantity, ']: ', $allDigits ? 'только цифры' : 'не подходит', "\n";
}
Ожидаемый результат
[0]: только цифры
[123]: только цифры
[12kg]: не подходит
[]: не подходит
Разрешённый префикс должен занимать всю непустую строку. Так не принимаются 12kg и пустота, но сохраняется 0. Для такой проверки также подходит ctype_digit. Допустимый диапазон количества и защита от переполнения при преобразовании — отдельный шаг.
Набор проверяет отдельные байты; многобайтовые символы и классы Unicode здесь не распознаются. Ограниченный length позволяет проверить только префикс, поэтому нельзя выдавать его результат за проверку всей строки. Для полной проверки ASCII-цифр удобна также ctype_digit с непустой строкой.
Сопоставьте: strcspn, strpbrk, substr. Байты, символы и выбор операции.
strcspnНужно найти длину первого поля до любого из нескольких разделителей. strcspn считает начальные байты, пока они НЕ входят в запрещённый набор. Это обратное по смыслу правило относительно strspn.
strcspn(string $string, string $characters, int $offset = 0, ?int $length = null): int| Часть договора | Что передать и что получить |
|---|---|
| string | Строка, из которой выбирается окно проверки. |
| characters | Набор байтов останавливающих подсчёт; диапазона 0..9 здесь НЕТ — перечисляйте 0123456789. |
| offset | Начало окна в байтах; отрицательное — отсчёт от конца. |
| length | null: до конца. Положительное ограничивает окно; отрицательное исключает столько байтов с конца; 0 задаёт пустое окно. |
| Результат | Длина начальной части окна, в которой ни один байт не входит в набор. Это длина, а не индекс в исходной строке. |
| Ошибки и границы | Пустой набор допустим: в PHP 8.4+ подсчитывается всё окно, включая NUL; ранее пустой набор ошибочно останавливался на NUL. Границы окна работают как при выборе подстроки; это не валидация остальной записи. |
| Версия / расширение | PHP 4+; nullable length — PHP 8.0+; standard |
- TOOL-42;8,main начинается с семи байтов, не равных , или ;.
- На ; подсчёт останавливается и возвращает 7.
- substr($record, 0, 7) извлекает TOOL-42.
<?php
$record = 'TOOL-42;8,main';
$length = strcspn($record, ',;');
echo 'Длина первого поля: ', $length, "\n";
echo 'Код: ', substr($record, 0, $length), "\n";
Ожидаемый результат
Длина первого поля: 7
Код: TOOL-42
Набор состоит из отдельных байтов. Длина возвращается числом, хвост не создаётся. Для самого хвоста подходит strpbrk. Это простая служебная запись без CSV-кавычек.
<?php
foreach (['TOOL-42', ';8', 'TOOL-42,8'] as $record) {
$first = substr($record, 0, strcspn($record, ',;'));
echo '[', $first, "]\n";
}
Ожидаемый результат
[TOOL-42]
[]
[TOOL-42]
При отсутствии запрещённого байта длина равна размеру всей строки; если он первый, длина 0. Это нормальные ответы, а не false. Строковые поля с кавычками и вложенной структурой нужно разбирать специализированным парсером. Дополнительные start/length ограничивают выбранный участок по правилам таблицы параметров.
Набор проверяет отдельные байты; многобайтовые символы и классы Unicode здесь не распознаются. Ограниченный length позволяет проверить только префикс, поэтому нельзя выдавать его результат за проверку всей строки. Для полной проверки ASCII-цифр удобна также ctype_digit с непустой строкой.
Сопоставьте: strspn, strpbrk, substr. Байты, символы и выбор операции.
substr_countВ текстовом шаблоне нужно узнать, сколько мест для подстановки заданной метки. substr_count считает неперекрывающиеся точные вхождения. Он не заменяет их и не считает слова по языковым правилам.
substr_count(string $haystack, string $needle, int $offset = 0, ?int $length = null): int| Часть договора | Что передать и что получить |
|---|---|
| haystack | Исходная строка. |
| needle | Непустая подстрока, регистр значим. |
| offset | Начало окна в байтах; отрицательное отсчитывается от конца. |
| length | null — до конца; положительное ограничивает окно; отрицательное исключает байты с конца. Окно должно помещаться в строку. 0 даёт 0. |
| Результат | Число неперекрывающихся полных вхождений в выбранном окне. |
| Ошибки и границы | В PHP 8 пустой needle, недопустимое смещение или выход длины за границы дают ValueError. Окно короче needle нормально даёт 0. |
| Версия / расширение | PHP 4+; отрицательные offset/length и length=0 — PHP 7.1; null length — PHP 8.0+; standard |
- В шаблоне "Заказ {order}. Номер {order}" два вхождения метки.
- substr_count ищет именно строку {order} и возвращает 2.
- Это помогает проверить свой шаблон до подстановки.
<?php
$template = 'Заказ {order}. При получении назовите номер {order}.';
echo 'Мест для номера: ', substr_count($template, '{order}'), "\n";
echo 'Мест для имени: ', substr_count($template, '{name}'), "\n";
Ожидаемый результат
Мест для номера: 2
Мест для имени: 0
Оба аргумента — строки, а ответ — число. Если важен просто факт наличия, str_contains точнее выражает задачу. Поиск чувствителен к регистру.
<?php
echo 'В aaaa пар aa: ', substr_count('aaaa', 'aa'), "\n";
echo 'Только в теле: ', substr_count('head:ERR|body:ERR ERR', 'ERR', 9), "\n";
echo 'В первых 8 байтах: ', substr_count('head:ERR|body:ERR ERR', 'ERR', 0, 8), "\n";
Ожидаемый результат
В aaaa пар aa: 2
Только в теле: 2
В первых 8 байтах: 1
После найденного aa поиск продолжается после него, поэтому не считает перекрывающуюся пару с середины. offset и length выбирают байтовый участок. Пустой needle и недопустимые границы вызывают ValueError. Для смыслового подсчёта событий журнала сначала разберите записи: ERR может оказаться частью другого слова.
Не используйте подсчёт подстрок для разбора HTML, JSON или CSV. Для Unicode-поиска с согласованной кодировкой есть mb_substr_count; для перекрывающихся совпадений нужен иной явный алгоритм. В актуальном PHP 8 проверяйте исключения ValueError: старые описания предупреждений относятся к прежним версиям.
Сопоставьте: strpos, substr, explode. Байты, символы и выбор операции.
substr_compareВ технической строке известны позиции отдельных полей. substr_compare сравнивает участок с ожидаемой меткой, не требуя сначала выделять его в отдельную строку через substr.
substr_compare(string $haystack, string $needle, int $offset, ?int $length = null, bool $case_insensitive = false): int| Часть договора | Что передать и что получить |
|---|---|
| haystack | Основная строка. |
| needle | Строка для сравнения с окном основной. |
| offset | Обязательная начальная позиция в haystack; отрицательная — от конца. |
| length | Максимальное число сравниваемых байтов. null сравнивает до большей из длин needle и оставшегося haystack, поэтому не означает «только длина needle». |
| case_insensitive | false — точные байты; true — сравнение ASCII без учёта регистра в современном PHP. |
| Результат | Отрицательное число, 0 или положительное число. Нужен знак, а не конкретная величина. |
| Ошибки и границы | В PHP 8 offset за допустимой границей и отрицательная length дают ValueError. Нулевая длина означает равенство пустых частей; offset на самом конце допустим с PHP 7.2.18/7.3.5. |
| Версия / расширение | PHP 5+; nullable length — PHP 8.0+; standard |
- В "ID:READY:42" READY начинается в позиции 3.
- substr_compare сравнивает пять байтов от позиции 3 с READY.
- Результат 0 подтверждает именно это поле, а не всю строку.
<?php
$record = 'ID:READY:42';
if (substr_compare($record, 'READY', 3, 5) === 0) {
echo "В записи указана готовность\n";
}
Ожидаемый результат
В записи указана готовность
Аргументы — исходная строка, сравниваемая строка, начальная позиция и длина. Ответ использует знак порядка как strcmp; для совпадения сравнивайте с нулём. Формат записи должен быть известен заранее.
<?php
$file = 'report.CSV';
echo substr_compare($file, '.csv', -4, 4) === 0 ? "Совпало\n" : "Регистр различается\n";
echo substr_compare($file, '.csv', -4, 4, true) === 0 ? "Суффикс совпал без ASCII-регистра\n" : "Не совпал\n";
Ожидаемый результат
Регистр различается
Суффикс совпал без ASCII-регистра
Отрицательная позиция отсчитывается от конца. Пятый аргумент true включает ASCII-сравнение без регистра. При точной проверке суффикса проще str_ends_with. Суффикс имени не доказывает формат содержимого файла и не заменяет проверку загружаемого файла.
Не сравнивайте результат с -1 или 1: сравнивайте с нулём. Для простого начала/конца строки проще str_starts_with/str_ends_with. Байтовый фрагмент не является Unicode-словом и может проходить через середину буквы.
Сопоставьте: strcmp, strncmp, substr, str_starts_with. Байты, символы и выбор операции.
strcmpДве технические строки нужно сравнить побайтово: равны ли они и какая должна стоять раньше. Это лексикографическое сравнение, а не разбор чисел внутри имён. Регистр значим.
strcmp(string $string1, string $string2): intПорядок аргументов важен: перестановка меняет направление сравнения. Результат int используется как знак, а не как true/false.
| Часть договора | Что передать и что получить |
|---|---|
| string1 | Первый сравниваемый текст. |
| string2 | Второй сравниваемый текст. |
| Результат | Число < 0, если первая строка меньше; 0 при равенстве; > 0, если больше. Конкретная величина не является договором. |
| Ошибки и границы | Неверный тип в строгом вызове — TypeError. С PHP 8.2 особенно нельзя полагаться на разность длин как на значение ответа. |
| Версия / расширение | PHP 4+; standard |
<?php
$left = 'photo2.jpg';
$right = 'photo10.jpg';
$order = strcmp($left, $right);
if ($order < 0) {
echo $left, ' раньше ', $right, "\n";
} elseif ($order > 0) {
echo $right, ' раньше ', $left, "\n";
} else {
echo "Строки равны по этому правилу\n";
}
Ожидаемый результат
photo10.jpg раньше photo2.jpg
Отрицательный результат означает «левое раньше правого», ноль — равенство, положительный — наоборот. Программа использует знак, а не конкретное значение вроде -1. Равенство по правилу без учёта регистра не означает совпадение исходных байтов.
<?php
$files = ['photo10.jpg', 'photo2.jpg', 'photo1.jpg'];
usort($files, 'strcmp');
echo implode("\n", $files), "\n";
echo strcmp('READY', 'ready') === 0 ? "Регистр не различён\n" : "Регистр различён\n";
Ожидаемый результат
photo1.jpg
photo10.jpg
photo2.jpg
Регистр различён
В usort передаётся имя функции строкой: PHP сам вызывает её для сравнения пар при сортировке. Самостоятельно обходить все пары не нужно. usort меняет порядок массива и переиндексирует числовые ключи. Для сохранения ключей есть uasort. Локализованный порядок русских слов и имён требует Collator, а не ASCII-сравнения.
Это лексикографическое байтовое сравнение, а не числовое преобразование: "10" может предшествовать "2". Эти функции не предназначены для сравнения секретов с постоянным временем: для уже вычисленных ожидаемого и полученного токенов есть hash_equals. Для простого точного равенства обычных строк часто яснее ===.
Сопоставьте: strcasecmp, strncmp, strncasecmp, strnatcmp, strnatcasecmp. Байты, символы и выбор операции.
strcasecmpДве технические строки нужно сравнить побайтово: равны ли они и какая должна стоять раньше. Это лексикографическое сравнение, а не разбор чисел внутри имён. ASCII-регистр при сравнении игнорируется.
strcasecmp(string $string1, string $string2): intПорядок аргументов важен: перестановка меняет направление сравнения. Результат int используется как знак, а не как true/false.
| Часть договора | Что передать и что получить |
|---|---|
| string1 | Первый сравниваемый текст. |
| string2 | Второй сравниваемый текст. |
| Результат | Число < 0, если первая строка меньше; 0 при равенстве; > 0, если больше. Конкретная величина не является договором. |
| Ошибки и границы | Неверный тип в строгом вызове — TypeError. С PHP 8.2 особенно нельзя полагаться на разность длин как на значение ответа. |
| Версия / расширение | PHP 4+; standard |
<?php
$left = 'photo2.jpg';
$right = 'photo10.jpg';
$order = strcasecmp($left, $right);
if ($order < 0) {
echo $left, ' раньше ', $right, "\n";
} elseif ($order > 0) {
echo $right, ' раньше ', $left, "\n";
} else {
echo "Строки равны по этому правилу\n";
}
Ожидаемый результат
photo10.jpg раньше photo2.jpg
Отрицательный результат означает «левое раньше правого», ноль — равенство, положительный — наоборот. Программа использует знак, а не конкретное значение вроде -1. Равенство по правилу без учёта регистра не означает совпадение исходных байтов.
<?php
$files = ['photo10.jpg', 'photo2.jpg', 'photo1.jpg'];
usort($files, 'strcasecmp');
echo implode("\n", $files), "\n";
echo strcasecmp('READY', 'ready') === 0 ? "Регистр не различён\n" : "Регистр различён\n";
Ожидаемый результат
photo1.jpg
photo10.jpg
photo2.jpg
Регистр не различён
В usort передаётся имя функции строкой: PHP сам вызывает её для сравнения пар при сортировке. Самостоятельно обходить все пары не нужно. usort меняет порядок массива и переиндексирует числовые ключи. Для сохранения ключей есть uasort. Локализованный порядок русских слов и имён требует Collator, а не ASCII-сравнения.
Это лексикографическое байтовое сравнение, а не числовое преобразование: "10" может предшествовать "2". Не обещает Unicode case folding; strcasecmp/strncasecmp с PHP 8.2 работают независимо от локали и затрагивают только ASCII. Эти функции не предназначены для сравнения секретов с постоянным временем: для уже вычисленных ожидаемого и полученного токенов есть hash_equals. Для простого точного равенства обычных строк часто яснее ===.
Сопоставьте: strcmp, strncmp, strncasecmp, strnatcmp, strnatcasecmp. Байты, символы и выбор операции.
strncmpНужно сравнить только первые несколько байтов технического сообщения, оставив остальную часть нетронутой. strncmp ограничивает сравнение длиной, но не проверяет, что строки полностью равны.
strncmp(string $string1, string $string2, int $length): intПорядок аргументов важен: перестановка меняет направление сравнения. Результат int используется как знак, а не как true/false.
| Часть договора | Что передать и что получить |
|---|---|
| string1 | Первый сравниваемый текст. |
| string2 | Второй сравниваемый текст. |
| length | Неотрицательное максимальное число сравниваемых байтов. При 0 любые две строки дают 0. |
| Результат | Число < 0, если первая строка меньше; 0 при равенстве; > 0, если больше. Конкретная величина не является договором. |
| Ошибки и границы | Отрицательная length вызывает ValueError в PHP 8. Неверный тип в строгом вызове — TypeError. С PHP 8.2 особенно нельзя полагаться на разность длин как на значение ответа. |
| Версия / расширение | PHP 4+; standard |
- В READY:42 и READY:99 первые пять байтов одинаковы.
- strncmp с length=5 возвращает ноль.
- Номера после двоеточия различаются: ноль здесь подтверждает только сравниваемый префикс.
<?php
$message = 'READY:42';
if (strncmp($message, 'READY:', 6) === 0) {
echo 'Готов заказ ', substr($message, 6), "\n";
}
Ожидаемый результат
Готов заказ 42
Третий аргумент ограничивает сравнение шестью байтами, включая :. Ноль означает совпадение этого участка. Для чувствительной к регистру проверки начала в PHP 8+ выразительнее str_starts_with; ограниченное сравнение полезно при работе с существующим протоколом или кодом.
<?php
echo strncmp('READY:42', 'READY:99', 6) === 0 ? "Префикс совпадает\n" : "Префикс различается\n";
echo strncmp('READY:42', 'READY:99', 8) === 0 ? "Вся строка совпадает\n" : "Номера различаются\n";
echo strncmp('A', 'B', 0) === 0 ? "Ноль байтов всегда совпадает\n" : "Различается\n";
Ожидаемый результат
Префикс совпадает
Номера различаются
Ноль байтов всегда совпадает
Нулевая длина не проверяет ничего и даёт равенство. Отрицательная длина вызывает ValueError. Позиции относятся к байтам, а не русским буквам. Если длина берётся извне, сначала проверяйте её смысл; нельзя превращать пустой критерий в разрешение доступа.
Это лексикографическое байтовое сравнение, а не числовое преобразование: "10" может предшествовать "2". Эти функции не предназначены для сравнения секретов с постоянным временем: для уже вычисленных ожидаемого и полученного токенов есть hash_equals. Для простого точного равенства обычных строк часто яснее ===.
Сопоставьте: strcmp, strcasecmp, strncasecmp, strnatcmp, strnatcasecmp. Байты, символы и выбор операции.
strncasecmpНужно сравнить только первые несколько байтов технического сообщения, оставив остальную часть нетронутой. strncasecmp ограничивает сравнение длиной, но не проверяет, что строки полностью равны. Регистр ASCII игнорируется.
strncasecmp(string $string1, string $string2, int $length): intПорядок аргументов важен: перестановка меняет направление сравнения. Результат int используется как знак, а не как true/false.
| Часть договора | Что передать и что получить |
|---|---|
| string1 | Первый сравниваемый текст. |
| string2 | Второй сравниваемый текст. |
| length | Неотрицательное максимальное число сравниваемых байтов. При 0 любые две строки дают 0. |
| Результат | Число < 0, если первая строка меньше; 0 при равенстве; > 0, если больше. Конкретная величина не является договором. |
| Ошибки и границы | Отрицательная length вызывает ValueError в PHP 8. Неверный тип в строгом вызове — TypeError. С PHP 8.2 особенно нельзя полагаться на разность длин как на значение ответа. |
| Версия / расширение | PHP 4+; standard |
- В READY:42 и READY:99 первые пять байтов одинаковы.
- strncasecmp с length=5 возвращает ноль.
- Номера после двоеточия различаются: ноль здесь подтверждает только сравниваемый префикс.
<?php
$message = 'READY:42';
if (strncasecmp($message, 'ready:', 6) === 0) {
echo 'Готов заказ ', substr($message, 6), "\n";
}
Ожидаемый результат
Готов заказ 42
Третий аргумент ограничивает сравнение шестью байтами, включая :. Ноль означает совпадение этого участка. Для чувствительной к регистру проверки начала в PHP 8+ выразительнее str_starts_with; ограниченное сравнение полезно при работе с существующим протоколом или кодом.
<?php
echo strncasecmp('READY:42', 'READY:99', 6) === 0 ? "Префикс совпадает\n" : "Префикс различается\n";
echo strncasecmp('READY:42', 'READY:99', 8) === 0 ? "Вся строка совпадает\n" : "Номера различаются\n";
echo strncasecmp('A', 'B', 0) === 0 ? "Ноль байтов всегда совпадает\n" : "Различается\n";
Ожидаемый результат
Префикс совпадает
Номера различаются
Ноль байтов всегда совпадает
Нулевая длина не проверяет ничего и даёт равенство. Отрицательная длина вызывает ValueError. Позиции относятся к байтам, а не русским буквам. Если длина берётся извне, сначала проверяйте её смысл; нельзя превращать пустой критерий в разрешение доступа.
Это лексикографическое байтовое сравнение, а не числовое преобразование: "10" может предшествовать "2". Не обещает Unicode case folding; strcasecmp/strncasecmp с PHP 8.2 работают независимо от локали и затрагивают только ASCII. Эти функции не предназначены для сравнения секретов с постоянным временем: для уже вычисленных ожидаемого и полученного токенов есть hash_equals. Для простого точного равенства обычных строк часто яснее ===.
Сопоставьте: strcmp, strcasecmp, strncmp, strnatcmp, strnatcasecmp. Байты, символы и выбор операции.
strnatcmpНомера фотографий в списке должны идти как 1, 2, 10, а не 1, 10, 2. Естественное сравнение замечает группы цифр внутри строк. Регистр значим.
strnatcmp(string $string1, string $string2): intПорядок аргументов важен: перестановка меняет направление сравнения. Результат int используется как знак, а не как true/false.
| Часть договора | Что передать и что получить |
|---|---|
| string1 | Первый сравниваемый текст. |
| string2 | Второй сравниваемый текст. |
| Результат | Число < 0, если первая строка меньше; 0 при равенстве; > 0, если больше. Конкретная величина не является договором. |
| Ошибки и границы | Неверный тип в строгом вызове — TypeError. С PHP 8.2 особенно нельзя полагаться на разность длин как на значение ответа. |
| Версия / расширение | PHP 4+; standard |
<?php
$left = 'photo2.jpg';
$right = 'photo10.jpg';
$order = strnatcmp($left, $right);
if ($order < 0) {
echo $left, ' раньше ', $right, "\n";
} elseif ($order > 0) {
echo $right, ' раньше ', $left, "\n";
} else {
echo "Строки равны по этому правилу\n";
}
Ожидаемый результат
photo2.jpg раньше photo10.jpg
Отрицательный результат означает «левое раньше правого», ноль — равенство, положительный — наоборот. Программа использует знак, а не конкретное значение вроде -1. Равенство по правилу без учёта регистра не означает совпадение исходных байтов.
<?php
$files = ['photo10.jpg', 'photo2.jpg', 'photo1.jpg'];
usort($files, 'strnatcmp');
echo implode("\n", $files), "\n";
echo strnatcmp('READY', 'ready') === 0 ? "Регистр не различён\n" : "Регистр различён\n";
Ожидаемый результат
photo1.jpg
photo2.jpg
photo10.jpg
Регистр различён
В usort передаётся имя функции строкой: PHP сам вызывает её для сравнения пар при сортировке. Самостоятельно обходить все пары не нужно. usort меняет порядок массива и переиндексирует числовые ключи. Для сохранения ключей есть uasort. Локализованный порядок русских слов и имён требует Collator, а не ASCII-сравнения.
Естественный порядок полезен для файлов, но не гарантирует языковую сортировку, правила денежных значений или версий. Для локали используйте Collator, для версий — version_compare. Эти функции не предназначены для сравнения секретов с постоянным временем: для уже вычисленных ожидаемого и полученного токенов есть hash_equals. Для простого точного равенства обычных строк часто яснее ===.
Сопоставьте: strcmp, strcasecmp, strncmp, strncasecmp, strnatcasecmp. Байты, символы и выбор операции.
strnatcasecmpНомера фотографий в списке должны идти как 1, 2, 10, а не 1, 10, 2. Естественное сравнение замечает группы цифр внутри строк. ASCII-регистр при сравнении игнорируется.
strnatcasecmp(string $string1, string $string2): intПорядок аргументов важен: перестановка меняет направление сравнения. Результат int используется как знак, а не как true/false.
| Часть договора | Что передать и что получить |
|---|---|
| string1 | Первый сравниваемый текст. |
| string2 | Второй сравниваемый текст. |
| Результат | Число < 0, если первая строка меньше; 0 при равенстве; > 0, если больше. Конкретная величина не является договором. |
| Ошибки и границы | Неверный тип в строгом вызове — TypeError. С PHP 8.2 особенно нельзя полагаться на разность длин как на значение ответа. |
| Версия / расширение | PHP 4+; standard |
<?php
$left = 'photo2.jpg';
$right = 'photo10.jpg';
$order = strnatcasecmp($left, $right);
if ($order < 0) {
echo $left, ' раньше ', $right, "\n";
} elseif ($order > 0) {
echo $right, ' раньше ', $left, "\n";
} else {
echo "Строки равны по этому правилу\n";
}
Ожидаемый результат
photo2.jpg раньше photo10.jpg
Отрицательный результат означает «левое раньше правого», ноль — равенство, положительный — наоборот. Программа использует знак, а не конкретное значение вроде -1. Равенство по правилу без учёта регистра не означает совпадение исходных байтов.
<?php
$files = ['photo10.jpg', 'photo2.jpg', 'photo1.jpg'];
usort($files, 'strnatcasecmp');
echo implode("\n", $files), "\n";
echo strnatcasecmp('READY', 'ready') === 0 ? "Регистр не различён\n" : "Регистр различён\n";
Ожидаемый результат
photo1.jpg
photo2.jpg
photo10.jpg
Регистр не различён
В usort передаётся имя функции строкой: PHP сам вызывает её для сравнения пар при сортировке. Самостоятельно обходить все пары не нужно. usort меняет порядок массива и переиндексирует числовые ключи. Для сохранения ключей есть uasort. Локализованный порядок русских слов и имён требует Collator, а не ASCII-сравнения.
Естественный порядок полезен для файлов, но не гарантирует языковую сортировку, правила денежных значений или версий. Для локали используйте Collator, для версий — version_compare. Не обещает Unicode case folding; strcasecmp/strncasecmp с PHP 8.2 работают независимо от локали и затрагивают только ASCII. Эти функции не предназначены для сравнения секретов с постоянным временем: для уже вычисленных ожидаемого и полученного токенов есть hash_equals. Для простого точного равенства обычных строк часто яснее ===.
Сопоставьте: strcmp, strcasecmp, strncmp, strncasecmp, strnatcmp. Байты, символы и выбор операции.
joinВ чужом коде встретилось join, а вам нужно собрать названия товаров в одну подпись. Это другое имя implode с тем же поведением, а не отдельный способ соединения. Можно читать оба имени; внутри проекта полезно выбрать одно соглашение.
join(string $separator, array $array): string; join(array $array): string| Часть договора | Что передать и что получить |
|---|---|
| separator | Строка между соседними элементами; в одноаргументной форме это "". |
| array | Массив строк в нужном порядке. Ключи не печатаются. Одноаргументная форма не поддерживает именованные аргументы. |
| Результат | Одна строка; пустой массив даёт "", один элемент — его текст без добавленного разделителя по краям. |
| Ошибки и границы | Неподходящий тип аргумента вызывает TypeError. Вложенные массивы не являются строками для такого объединения. Историческая форма (array, separator) устарела в 7.4 и удалена в PHP 8. |
| Версия / расширение | PHP 4+; standard |
- В массиве три названия: Кисть, Валик, Лента.
- join помещает запятую с пробелом между соседними значениями.
- Результат — одна строка Кисть, Валик, Лента.
<?php
$names = ['Кисть', 'Валик', 'Лента'];
echo join(', ', $names), "\n";
echo implode(', ', $names), "\n";
Ожидаемый результат
Кисть, Валик, Лента
Кисть, Валик, Лента
Первый аргумент — разделитель, второй — массив значений. Оба вызова возвращают строку, а echo выводит её. Ключи массива в подпись не входят. Исторический обратный порядок аргументов в PHP 8 не поддерживается.
<?php
$lines = ['Заказ 42', 'Кисть — 2 шт.', 'Выдача: завтра'];
$receipt = join("\n", $lines);
echo $receipt, "\n";
echo 'Пустой список: [', join(', ', []), "]\n";
Ожидаемый результат
Заказ 42
Кисть — 2 шт.
Выдача: завтра
Пустой список: []
\n в двойных кавычках означает перенос строки. Так можно подготовить тело текстового уведомления. Функция не отправляет письмо и не сериализует CSV: если разделитель может встречаться внутри значения, для формата нужен соответствующий кодировщик.
implode/join не экранируют данные. Для SQL IN формируйте placeholders и привязки, для URL используйте http_build_query, для CSV — fputcsv. Не передавайте сюда многомерный массив в надежде получить сериализацию. Новый код обычно выбирает основное имя implode.
Сопоставьте: implode, explode, str_getcsv. Байты, символы и выбор операции.
str_ireplaceВ служебном англоязычном уведомлении встречается один и тот же маркер с разным регистром. str_ireplace заменяет все совпадения без учёта ASCII-регистра. Русские буквы в современном PHP так не сопоставляются.
str_ireplace(array|string $search, array|string $replace, string|array $subject, int &$count = null): string|array| Часть договора | Что передать и что получить |
|---|---|
| search | Одна подстрока или список подстрок. Пустая подстрока не вставляет текст между символами. |
| replace | Одна замена для всех search либо список замен по порядку. Если список короче search, недостающие замены — пустые строки. |
| subject | Строка либо массив строк; форма результата соответствует форме этого аргумента. |
| count | Необязательная переменная для числа замен во всём вызове. & в сигнатуре означает запись по ссылке; в месте вызова & не пишут. |
| Результат | Новая строка/массив с заменами. Исходный subject не изменяется; count получает общее число замен. |
| Ошибки и границы | В строгом вызове неверные типы — TypeError. Массив replace не сочетают с одиночной строкой search: задайте согласованные списки или одну строковую замену. |
| Версия / расширение | PHP 5+; standard |
- Текст содержит READY и ready.
- Обе подстроки совпадают с поиском ready по ASCII-регистру.
- Обе заменяются одним выбранным текстом; счётчик получает 2.
<?php
$text = 'READY: order 42; ready: order 43';
$count = 0;
$updated = str_ireplace('ready', 'Available', $text, $count);
echo $updated, "\n";
echo 'Заменено меток: ', $count, "\n";
Ожидаемый результат
Available: order 42; Available: order 43
Заменено меток: 2
Первые три аргумента — что найти, замена и исходный текст. Четвёртый получает число замен. Это точная подстрока без проверки границ слова: ready заменилось бы и внутри already. Для состояния заказа лучше отдельное поле, чем анализ произвольного комментария.
<?php
echo str_ireplace(['DRAFT', 'READY'], ['READY', 'SENT'], 'draft ready'), "\n";
echo str_ireplace('КИСТЬ', 'Валик', 'кисть широкая'), "\n";
Ожидаемый результат
SENT SENT
кисть широкая
Массив поиска обрабатывается последовательно: DRAFT становится READY, затем тоже SENT. Вставленные значения могут попасть под следующую замену. Для независимых меток лучше карта strtr. С PHP 8.2 игнорируется только ASCII-регистр; для русскоязычного поиска сначала выбирают подходящий Unicode-инструмент.
С PHP 8.2 результат не зависит от setlocale: non-ASCII байты сравниваются точно. Для Unicode-регистра понадобится отдельный подход, например регулярное выражение с явным Unicode-режимом после изучения его правил. Это замена фиксированных байтов, не разбор языка шаблонов и не проверка HTML/SQL. Вставленные пользовательские значения экранируют для конечного контекста отдельно. Большие списки замен могут многократно проходить по тексту; для независимой карты рассмотрите strtr.
Сопоставьте: str_replace, strtr, substr_replace. Байты, символы и выбор операции.
strtoupperМагазин принимает регистронезависимый ASCII-промокод. Перед поиском нужно привести введённую копию к согласованному верхнему регистру. Это допустимо, только если правило промокодов действительно не различает регистр.
strtoupper(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Исходный текст; изменяются все ASCII-буквы нужного регистра. |
| Результат | Новая строка; остальные байты и исходная переменная не меняются. Пустая строка остаётся пустой. |
| Ошибки и границы | Неподходящий тип в строгом вызове — TypeError. С PHP 8.2 преобразование не зависит от setlocale и касается только ASCII. |
| Версия / расширение | PHP 4+; standard |
- Покупатель ввёл save10.
- strtoupper возвращает SAVE10.
- Сравнение с сохранённым кодом работает одинаково для save10 и SAVE10.
<?php
$entered = 'save10';
$code = strtoupper($entered);
echo 'Ищем промокод: ', $code, "\n";
Ожидаемый результат
Ищем промокод: SAVE10
Канонический вид — выбранная одинаковая запись значения. strtoupper меняет ASCII-буквы, а не проверяет наличие кода и право на скидку.
<?php
$promotions = ['SAVE10' => 'Скидка 10%'];
$entered = ' Save10 ';
$code = strtoupper(trim($entered));
echo $promotions[$code] ?? 'Промокод не найден', "\n";
echo strtoupper('скидка'), "\n";
Ожидаемый результат
Скидка 10%
скидка
Сначала trim нормализует оговорённые пробелы, затем strtoupper — регистр. ?? даёт запасной текст при отсутствии ключа. Срок действия, совместимость скидок и количество применений — отдельные проверки. Пароли и чувствительные к регистру токены так не нормализуют. Для русских букв нужен mb_strtoupper.
Для русских и других Unicode-букв нужны mb_strtolower/mb_strtoupper; для первой буквы в PHP 8.4+ существуют mb_lcfirst/mb_ucfirst. Для сравнения идентификаторов заранее задайте правила регистра, не нормализуйте секреты и пароли.
Сопоставьте: strtolower, lcfirst, ucfirst, ucwords. Байты, символы и выбор операции.
lcfirstВ генераторе схемы нужно получить lowerCamelCase-имя технического поля из PascalCase. lcfirst меняет только первый ASCII-байт, если это буква нужного регистра. Остальная строка сохраняется.
lcfirst(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Исходный текст; изменяются только первая ASCII-буква, если она стоит в первом байте. |
| Результат | Новая строка; остальные байты и исходная переменная не меняются. Пустая строка остаётся пустой. |
| Ошибки и границы | Неподходящий тип в строгом вызове — TypeError. С PHP 8.2 преобразование не зависит от setlocale и касается только ASCII. |
| Версия / расширение | PHP 5.3+; standard |
- OrderStatus — исходное техническое имя.
- Меняется только первая буква.
- orderStatus — результат; внутренний регистр слова Status сохранён.
<?php
$field = 'OrderStatus';
echo lcfirst($field), "\n";
echo lcfirst(' OrderStatus'), "\n";
Ожидаемый результат
orderStatus
OrderStatus
Пробел в начале второй строки не пропускается в поиске первой буквы. Функция не выправляет весь регистр, не разбивает слова и не проверяет корректность PHP-имени.
<?php
$names = ['OrderStatus', 'DeliveryDate'];
foreach ($names as $field) {
echo lcfirst($field), "\n";
}
echo lcfirst('язык'), "\n";
Ожидаемый результат
orderStatus
deliveryDate
язык
Сценарий относится к вашим техническим ASCII-именам. PHP-имя класса или свойства нельзя без проверки строить из произвольного пользовательского текста. Unicode-регистр этим вызовом не обрабатывается.
lcfirst("URL") даст "uRL", а не "url"; границы сокращений она не понимает. Для русских и других Unicode-букв нужны mb_strtolower/mb_strtoupper; для первой буквы в PHP 8.4+ существуют mb_lcfirst/mb_ucfirst. Для сравнения идентификаторов заранее задайте правила регистра, не нормализуйте секреты и пароли.
Сопоставьте: strtolower, strtoupper, ucfirst, ucwords. Байты, символы и выбор операции.
ucfirstВ генераторе схемы нужно получить PascalCase-метку из lowerCamelCase. ucfirst меняет только первый ASCII-байт, если это буква нужного регистра. Остальная строка сохраняется.
ucfirst(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Исходный текст; изменяются только первая ASCII-буква, если она стоит в первом байте. |
| Результат | Новая строка; остальные байты и исходная переменная не меняются. Пустая строка остаётся пустой. |
| Ошибки и границы | Неподходящий тип в строгом вызове — TypeError. С PHP 8.2 преобразование не зависит от setlocale и касается только ASCII. |
| Версия / расширение | PHP 4+; standard |
- orderStatus — исходное техническое имя.
- Меняется только первая буква.
- OrderStatus — результат; внутренний регистр слова Status сохранён.
<?php
$field = 'orderStatus';
echo ucfirst($field), "\n";
echo ucfirst(' orderStatus'), "\n";
Ожидаемый результат
OrderStatus
orderStatus
Пробел в начале второй строки не пропускается в поиске первой буквы. Функция не выправляет весь регистр, не разбивает слова и не проверяет корректность PHP-имени.
<?php
$names = ['orderStatus', 'deliveryDate'];
foreach ($names as $field) {
echo ucfirst($field), "\n";
}
echo ucfirst('язык'), "\n";
Ожидаемый результат
OrderStatus
DeliveryDate
язык
Сценарий относится к вашим техническим ASCII-именам. PHP-имя класса или свойства нельзя без проверки строить из произвольного пользовательского текста. Unicode-регистр этим вызовом не обрабатывается.
Функция не приводит остальные буквы к нижнему регистру: ucfirst("sTOCK") даст "STOCK". Не используйте её как универсальное оформление имени человека. Для русских и других Unicode-букв нужны mb_strtolower/mb_strtoupper; для первой буквы в PHP 8.4+ существуют mb_lcfirst/mb_ucfirst. Для сравнения идентификаторов заранее задайте правила регистра, не нормализуйте секреты и пароли.
Сопоставьте: strtolower, strtoupper, lcfirst, ucwords. Байты, символы и выбор операции.
ucwordsВ собственном англоязычном каталоге нужно оформить короткую рубрику заглавными буквами в начале слов. ucwords меняет первые ASCII-буквы после выбранных разделителей, но не понимает правила имён людей или заголовков разных языков.
ucwords(string $string, string $separators = " \t\r\n\f\v"): string| Часть договора | Что передать и что получить |
|---|---|
| string | Исходный текст. |
| separators | Набор отдельных байтов-разделителей. По умолчанию пробел, TAB, CR, LF, form feed, вертикальная табуляция; дефиса в этом наборе нет. |
| Результат | Новая строка с заглавной первой ASCII-буквой каждой части; оставшиеся буквы не переводятся в нижний регистр. |
| Ошибки и границы | С PHP 8.2 поведение не зависит от локали; Unicode-заглавные буквы не поддерживаются. Неверный тип в строгом вызове — TypeError. |
| Версия / расширение | PHP 4+; standard |
- paint tools — два ASCII-слова через пробел.
- ucwords делает p и t заглавными.
- Ответ Paint Tools можно использовать как подпись рубрики.
<?php
echo ucwords('paint tools'), "\n";
echo ucwords('PAINT tools'), "\n";
echo ucwords('кисть широкая'), "\n";
Ожидаемый результат
Paint Tools
PAINT Tools
кисть широкая
Остальные буквы слова не переводятся в нижний регистр. Русские буквы не преобразуются: функция работает с ASCII. Для текста на естественном языке выбирайте Unicode-инструменты и редакторское правило, а не автоматически «исправляйте» имена людей.
<?php
echo ucwords('water-based paint', ' -'), "\n";
echo ucwords(strtolower('PAINT TOOLS')), "\n";
Ожидаемый результат
Water-Based Paint
Paint Tools
Второй аргумент — набор отдельных байтов-разделителей: здесь пробел и дефис. Во второй строке нижний регистр задаётся отдельной операцией, потому что ucwords не делает этого сама. Такое преобразование применимо к заранее согласованным ASCII-рубрикам, но может испортить аббревиатуры.
Для заголовочного регистра Unicode используйте mb_convert_case с MB_CASE_TITLE и явной кодировкой. Универсальное изменение регистра может испортить бренды, аббревиатуры и имена; сохраняйте их исходное написание, если правило не согласовано.
Сопоставьте: strtolower, ucfirst, str_replace. Байты, символы и выбор операции.
str_word_countНужно посчитать слова в короткой английской подписи, получить их список или позиции. str_word_count предлагает три режима. Для русского UTF-8-текста эта байтовая функция не подходит: выбор инструмента зависит от языка и того, что ваш продукт считает словом.
str_word_count(string $string, int $format = 0, ?string $characters = null): array|int| Часть договора | Что передать и что получить |
|---|---|
| string | Текст. Слова определяются по буквам текущей локали; внутри возможны апостроф и дефис, но не как начало слова. Многобайтовые локали не поддерживаются. |
| format | 0 — число слов; 1 — список слов; 2 — карта байтовая_позиция => слово. |
| characters | Дополнительные байты, разрешённые внутри слова; null означает отсутствие дополнений. |
| Результат | Число либо массив согласно format; отсутствие слов даёт 0 либо []. |
| Ошибки и границы | Значение format вне 0,1,2 в PHP 8 вызывает ValueError. Это не Unicode-сегментация текста; локаль влияет на определение букв. |
| Версия / расширение | PHP 4.3+; nullable characters — PHP 8.0+; standard |
- Steel bolt — два слова, разделённых пробелом.
- Режим 0 возвращает число 2; режим 1 — отдельные слова.
- Режим 2 дополнительно сохраняет байтовые позиции 0 и 6.
<?php
$title = 'Steel bolt';
echo 'Слов: ', str_word_count($title), "\n";
$words = str_word_count($title, 1);
echo 'Первое: ', $words[0], "\n";
echo 'Второе: ', $words[1], "\n";
Ожидаемый результат
Слов: 2
Первое: Steel
Второе: bolt
Без второго аргумента функция возвращает число. При 1 возвращается массив; [0] и [1] выбирают его элементы. Пример использует ASCII и стандартное начальное окружение C. Это не универсальный счётчик слов любого языка: классификация зависит от локали и не поддерживает многобайтовую локаль.
<?php
$text = 'Steel bolt ready';
foreach (str_word_count($text, 2) as $offset => $word) {
echo 'Позиция ', $offset, ': ', $word, "\n";
}
echo 'Обычное правило: ', implode(' / ', str_word_count('Bolt M10 ready', 1)), "\n";
echo 'Разрешены цифры: ', implode(' / ', str_word_count('Bolt M10 ready', 1, '0..9')), "\n";
Ожидаемый результат
Позиция 0: Steel
Позиция 6: bolt
Позиция 11: ready
Обычное правило: Bolt / M / ready
Разрешены цифры: Bolt / M10 / ready
В режиме 2 ключ — место начала слова в байтах. Дополнительный набор 0..9 разрешает цифры внутри выбранного понятия «слово», поэтому M10 не теряет 10. Это всё ещё не проверка артикула: бизнес-правила и поиск товара выполняются отдельно. Для Unicode-границ слов рассмотрите IntlBreakIterator.
Не используйте эту функцию для подсчёта русских слов в UTF-8 или лимита редакционного текста. Для языковых границ подходит IntlBreakIterator; правила продукта (числа, дефисы, эмодзи) нужно определить явно. В долгоживущем процессе изменение локали влияет на другие операции.
Сопоставьте: explode, strtok, setlocale. Байты, символы и выбор операции.
strtokВ простой настройке коды разделены запятыми, точками с запятой и пробелами. Нужно получать по одному непустому коду. strtok запоминает место чтения между вызовами; это удобно в коротком линейном разборе, но требует осторожности при вложенных разборах.
strtok(string $string, string $token): string|false; strtok(string $token): string|false| Часть договора | Что передать и что получить |
|---|---|
| string | Передаётся только при начале нового разбора; повторный двухаргументный вызов сбрасывает предыдущий разбор. |
| token | Набор отдельных байтов-разделителей. После первого вызова передают только этот набор; его можно менять. Альтернативная одноаргументная форма не поддерживает именованные аргументы. |
| Результат | Следующий непустой токен либо false в конце. Повторяющиеся и крайние разделители пропускаются, поэтому пустые поля не возвращаются. |
| Ошибки и границы | Проверяйте !== false: токен "0" допустим. С PHP 8.3 вызов продолжения без начатого разбора сообщает E_WARNING о недостающем token; сначала вызывайте форму с двумя аргументами. |
| Версия / расширение | PHP 4+; standard |
- Строка КИСТЬ,ВАЛИК состоит из двух кодов.
- Первый вызов получает всю строку и разделитель, возвращает КИСТЬ.
- Следующий получает только разделитель, возвращает ВАЛИК; затем возвращается false.
<?php
echo strtok('КИСТЬ,ВАЛИК', ','), "\n";
echo strtok(','), "\n";
$next = strtok(',');
if ($next === false) {
echo "Все коды прочитаны\n";
}
Ожидаемый результат
КИСТЬ
ВАЛИК
Все коды прочитаны
Во второй вызов исходная строка не передаётся: функция продолжает прошлый разбор. false означает конец, а не пустой код. Разделитель здесь один ASCII-байт; кириллические слова остаются целыми, потому что их байты не входят в набор разделителей.
<?php
$text = ' КИСТЬ,,ВАЛИК;0 ';
$code = strtok($text, ' ,;');
while ($code !== false) {
echo 'Код: ', $code, "\n";
$code = strtok(' ,;');
}
Ожидаемый результат
Код: КИСТЬ
Код: ВАЛИК
Код: 0
Каждый символ второго аргумента — отдельный разделитель. Повторные разделители пропускаются. while повторяет тело до false; проверка по истинности ошибочно остановилась бы на строке "0". Для таблицы, где пустое поле значимо, этот разбор не подходит; explode сохраняет пустые поля, CSV разбирают str_getcsv.
<?php
echo strtok('КИСТЬ,ВАЛИК', ','), "\n";
echo strtok('склад|магазин', '|'), "\n";
echo strtok('|'), "\n";
if (strtok(',') === false) {
echo "Продолжения списка товаров больше нет\n";
}
Ожидаемый результат
КИСТЬ
склад
магазин
Продолжения списка товаров больше нет
Второй двухаргументный вызов заменил внутреннее состояние. ВАЛИК потерялся для продолжения, хотя исходный текст не менялся. Для независимых или вложенных разборов удобнее получить массив explode/preg_split и хранить его в собственной переменной.
Состояние не принадлежит вашей функции или объекту: вложенные вызовы библиотек могут его заменить. Для независимых разборов чаще проще explode/preg_split или собственный объект. strtok не знает кавычек CSV и не поддерживает многобайтовый разделитель как целую подстроку.
Сопоставьте: explode, str_getcsv, strpbrk. Байты, символы и выбор операции.
count_charsВ строке хранится короткий ответ устройства: A означает один вид события, B — другой. Нужно посчитать частоту каждого байта, а не искать отдельную подстроку. count_chars возвращает такую частотную таблицу; для русских букв UTF-8 это неподходящая единица подсчёта.
count_chars(string $string, int $mode = 0): array|string| Часть договора | Что передать и что получить |
|---|---|
| string | Любая последовательность байтов, включая NUL. |
| mode | 0 — все 256 частот; 1 — только встреченные байты; 2 — только отсутствующие (частота 0); 3 — строка разных встреченных байтов; 4 — строка отсутствующих. Ключи частот — коды 0…255. |
| Результат | Массив частот для 0/1/2 либо строка байтов для 3/4. Порядок байтов соответствует их кодам, а не первым появлениям. |
| Ошибки и границы | Неизвестный mode в PHP 8 даёт ValueError; ранее ошибка возвращала false. Режимы 3/4 могут вернуть непечатные байты. |
| Версия / расширение | PHP 4+; standard |
- В журнале ABBAA: A встречается трижды, B — дважды.
- count_chars с режимом 1 возвращает код байта и его частоту.
- chr превращает код 65 обратно в A: отчёт показывает A: 3.
<?php
$events = 'ABBAA';
$counts = count_chars($events, 1);
echo 'Событий A: ', $counts[ord('A')], "\n";
echo 'Событий B: ', $counts[ord('B')], "\n";
Ожидаемый результат
Событий A: 3
Событий B: 2
Режим 1 оставляет только встреченные байты. Ключи массива — их числовые коды; ord("A") даёт 65. В этом примере наличие A и B видно во входе. Для произвольного журнала отсутствующий код читают через ?? 0.
<?php
$events = 'AABXBA';
foreach (count_chars($events, 1) as $byte => $count) {
echo 'Код ', chr($byte), ': ', $count, "\n";
}
echo 'Набор кодов: ', count_chars($events, 3), "\n";
Ожидаемый результат
Код A: 3
Код B: 2
Код X: 1
Набор кодов: ABX
Так диагностический отчёт обнаруживает неожиданный X, который можно сопоставить с документацией устройства. Режим 3 возвращает каждый байт один раз в порядке его кода. Для неизвестного бинарного ввода печатайте bin2hex(chr($byte)), иначе управляющие байты повлияют на терминал.
<?php
$source = 'BOLT';
foreach (['TOBL', 'TOOL'] as $answer) {
$same = count_chars($source, 1) === count_chars($answer, 1);
echo $answer, ': ', $same ? 'буквы совпали' : 'состав отличается', "\n";
}
echo 'Всего возможных байтов: ', count(count_chars('AB', 0)), "\n";
echo 'Отсутствующих байтов: ', count(count_chars('AB', 2)), "\n";
echo 'Размер строки отсутствующих байтов: ', strlen(count_chars('AB', 4)), "\n";
Ожидаемый результат
TOBL: буквы совпали
TOOL: состав отличается
Всего возможных байтов: 256
Отсутствующих байтов: 254
Размер строки отсутствующих байтов: 254
Важно количество каждой буквы, а не только набор: TOOL имеет лишнюю O и не имеет B. Эта проверка ещё не доказывает, что ответ является словом. Последние строки показывают режимы 0, 2 и 4; строку отсутствующих байтов не выводим напрямую, потому что она содержит управляющие значения.
Это статистика байтов, не частоты Unicode-букв и не анаграммы естественного языка: UTF-8-буква состоит из нескольких байтов. Не показывайте режим 4 прямо в HTML/терминале — используйте bin2hex для диагностики.
Сопоставьте: ord, chr, bin2hex, str_shuffle. Байты, символы и выбор операции.
str_shuffleДля простой головоломки нужно переставить английские буквы слова и показать их игроку. str_shuffle возвращает новую строку с теми же байтами в случайном порядке. Для русских букв UTF-8 побайтовая перестановка разрушит символы.
str_shuffle(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Байты, которые нужно переставить. |
| Результат | Одна случайная перестановка такой же длины и с теми же частотами байтов. Исходный порядок тоже может выпасть. |
| Ошибки и границы | Не обеспечивает криптографическую непредсказуемость. С PHP 7.1 использует общий Mt19937; mt_srand/srand и другие функции с этим генератором влияют на общую последовательность. |
| Версия / расширение | PHP 4.3+; standard |
- BOLT — исходное слово из четырёх ASCII-букв.
- str_shuffle переставляет их; для воспроизводимого учебного запуска ниже задан seed 42.
- Появляется OBTL. Игрок должен восстановить BOLT.
<?php
mt_srand(42);
$word = 'BOLT';
$puzzle = str_shuffle($word);
echo 'Задание: ', $puzzle, "\n";
echo 'Ответ: ', $word, "\n";
Ожидаемый результат
Задание: OBTL
Ответ: BOLT
mt_srand(42) фиксирует начало псевдослучайной последовательности только для повторения учебного результата в используемом PHP 8.5. В игре эту строку убирают: перестановка тогда может отличаться, а иногда совпадёт с исходным словом. Seed влияет на общий генератор процесса, поэтому не устанавливайте его внутри библиотечной функции. str_shuffle не меняет $word.
<?php
$word = 'BOLT';
foreach ([' bolt ', 'TOOL'] as $entered) {
$answer = strtoupper(trim($entered));
if ($answer === $word) {
echo "Верно: BOLT означает болт\n";
} else {
echo 'Попробуйте ещё: ', $answer, "\n";
}
}
Ожидаемый результат
Верно: BOLT означает болт
Попробуйте ещё: TOOL
Это продолжение головоломки: trim убирает крайние пробелы, strtoupper нормализует ASCII-регистр, === проверяет точный ответ. Перемешанный вариант предназначен для показа, а правильное слово хранится отдельно. Для нового кода Random\Randomizer изолирует состояние генератора; для секретов нужны random_bytes/random_int или Secure engine, а не str_shuffle.
Не создавайте так пароли, токены восстановления или ключи. Для нового кода PHP рекомендует Random\Randomizer; для секретов выбирайте Secure engine либо random_bytes/random_int. Фиксированный seed пригоден для воспроизведения учебного сценария, но не делает результат безопасным.
Сопоставьте: count_chars, str_split. Байты, символы и выбор операции.
str_rot13Скрыть ответ ASCII-загадки от случайного взгляда обратимым ROT13-преобразованием.
str_rot13(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Текст: латинские A…Z и a…z сдвигаются на 13 букв, остальные байты остаются прежними. |
| Результат | Преобразованная строка; повторный ROT13 возвращает исходную. |
| Ошибки и границы | Это не шифрование и не защита секретов; ключа нет. Неподходящий тип в строгом вызове — TypeError. |
| Версия / расширение | PHP 4.2+; standard |
- BOLT-41 → ASCII-буквы и цифры.
- B→O, O→B, L→Y, T→G; -41 остаётся.
- Получается OBYG-41; повторение возвращает BOLT-41.
<?php
$encoded = str_rot13('BOLT-41');
echo $encoded, "\n", str_rot13($encoded), "\n";
Ожидаемый результат
OBYG-41
BOLT-41
Одна функция выполняет оба направления, потому что два сдвига по 13 дают полный латинский алфавит из 26 букв.
<?php
$answer = 'steel bolt / болт';
$spoiler = str_rot13($answer);
echo $spoiler, "\n";
var_dump(str_rot13($spoiler) === $answer);
Ожидаемый результат
fgrry obyg / болт
bool(true)
Русское слово осталось видимым. Модель годится для соглашения о спойлерах в ASCII-тексте; для конфиденциальности она не годится даже с дополнительным Base64.
ROT13 не проверяет целостность и легко узнаётся. Для хранения паролей используйте password_hash, для защищённого обмена — специализированные криптографические протоколы. Не проектируйте собственное шифрование из строковых преобразований.
Сопоставьте: strtr, str_shuffle. Байты, символы и выбор операции.
addslashesВ старом текстовом формате кавычки сохранялись с обратной косой чертой: O'Neil записывался как O\'Neil. addslashes создаёт именно такое представление. Это полезно при поддержке согласованного старого формата, но не является универсальной защитой пользовательских данных.
addslashes(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Текст, где экранируются одинарная/двойная кавычки, обратная косая черта и NUL. |
| Результат | Новая строка с обратными косыми чертами перед выбранными байтами; NUL получает запись \0. |
| Ошибки и границы | Не является экранированием для SQL, HTML, URL, shell или JSON. Неверный тип в строгом вызове — TypeError. |
| Версия / расширение | PHP 4+; standard |
- Имя O'Neil содержит настоящую одинарную кавычку.
- addslashes ставит перед ней обратную косую черту.
- Получившаяся строка O\'Neil отличается от исходного имени; для вывода человеку этот слой обычно не нужен.
<?php
$name = "O'Neil";
$stored = addslashes($name);
echo 'Имя: ', $name, "\n";
echo 'Старое представление: ', $stored, "\n";
Ожидаемый результат
Имя: O'Neil
Старое представление: O\'Neil
Двойные кавычки в PHP-коде позволяют записать одинарную без дополнительного экранирования самого кода. Обратная черта в $stored — реальный добавленный байт. $name не изменился. Функция также обрабатывает двойную кавычку, обратную черту и NUL.
<?php
$original = "A\0B\\C";
$stored = addslashes($original);
$restored = stripslashes($stored);
echo 'Записано: ', $stored, "\n";
echo 'Восстановленные байты: ', bin2hex($restored), "\n";
echo $restored === $original ? "Исходные байты сохранены\n" : "Данные изменились\n";
Ожидаемый результат
Записано: A\0B\\C
Восстановленные байты: 4100425c43
Исходные байты сохранены
NUL является байтом 00, а не окончанием PHP-строки. Для наглядности восстановленные данные показаны в HEX. Парная операция уместна потому, что формат точно известен. Для SQL применяют параметры prepare/execute, для HTML — htmlspecialchars, для JSON — json_encode. Переносить сюда addslashes нельзя.
Старые magic_quotes удалены из PHP ещё в 5.4: современный HTTP-ввод не нужно автоматически «исправлять» stripslashes. Для SQL нужны параметры подготовленного запроса, для HTML htmlspecialchars, для генерации PHP-литерала var_export; не используйте eval ради проверки экранирования.
Сопоставьте: stripslashes, addcslashes, htmlspecialchars. Байты, символы и выбор операции.
stripslashesВ известном старом хранилище имя содержит лишний слой addslashes, и человеку показывается O\'Neil. stripslashes снимает этот слой. Если строка не была записана таким способом, функция может удалить полезные обратные черты и испортить данные.
stripslashes(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Строка в согласованном старом slash-формате: \' → ' , \" → ", \\ → \, \0 → NUL. |
| Результат | Строка со снятым одним уровнем экранирования. Это не универсальный декодер C/JSON. |
| Ошибки и границы | Не проверяет, действительно ли источник был addslashes; удаление черт из обычного текста может повредить пути и данные. |
| Версия / расширение | PHP 4+; standard |
- Сохранённое O\'Neil содержит черту перед кавычкой.
- stripslashes снимает один слой старого экранирования.
- Результат O'Neil можно показывать как обычное имя, подготовив его для нужного контекста вывода.
<?php
$stored = "O\\'Neil";
$name = stripslashes($stored);
echo 'В хранилище: ', $stored, "\n";
echo 'Имя: ', $name, "\n";
Ожидаемый результат
В хранилище: O\'Neil
Имя: O'Neil
Две черты внутри двойных кавычек PHP создают одну настоящую черту в строке. Функция получает уже это значение, а не текст исходного PHP-файла. Современные $_POST и записи базы не нуждаются в автоматическом stripslashes: старые magic_quotes удалены ещё в PHP 5.4.
<?php
$path = 'C:\temp\report.txt';
echo 'Исходный путь: ', $path, "\n";
echo 'После stripslashes: ', stripslashes($path), "\n";
echo 'Строка с буквальным n: ', stripslashes('\n'), "\n";
Ожидаемый результат
Исходный путь: C:\temp\report.txt
После stripslashes: C:tempreport.txt
Строка с буквальным n: n
Обратные черты были частью пути, а не служебным слоем. Функция не может сама определить происхождение строки. Последний вызов оставляет букву n: stripslashes не является C-декодером переводов строк, для такого отдельного договора существует stripcslashes.
Не вызывайте автоматически для $_POST или значений базы: современные источники не обязаны содержать addslashes-слой. Функция не снимает HTML-сущности и не заменяет json_decode. После декодирования HTML-вывод всё равно требует собственного экранирования.
Сопоставьте: addslashes, stripcslashes, htmlspecialchars. Байты, символы и выбор операции.
addcslashesВ диагностической строке есть табуляция или перенос, и нужно увидеть их как символы \t и \n, не разрывая запись журнала. addcslashes экранирует выбранный набор байтов. Что именно экранировать, указывается во втором аргументе.
addcslashes(string $string, string $characters): string| Часть договора | Что передать и что получить |
|---|---|
| string | Исходные байты. |
| characters | Набор экранируемых байтов с диапазонами вида \0..\37. Управляющие байты получают известную C-запись или восьмеричную форму. |
| Результат | Новая строка с выбранными экранированными байтами. |
| Ошибки и границы | Диапазон A..z включает также [, обратную черту, ], ^, _, ` между Z и a. Обратный диапазон не задаёт диапазон: рассматриваются крайние байты и точки. Будьте осторожны с буквами 0,a,b,f,n,r,t,v: добавленная черта может создать управляющую последовательность при обратном разборе. |
| Версия / расширение | PHP 4+; standard |
- Исходная запись содержит Кисть, затем TAB, число 12 и LF.
- Маска выбирает настоящие TAB и LF.
- Получается одна видимая строка Кисть\t12\n.
<?php
$record = "Кисть\t12\n";
$visible = addcslashes($record, "\t\n");
echo 'Получено: ', $visible, "\n";
Ожидаемый результат
Получено: Кисть\t12\n
\t и \n внутри двойных кавычек исходного кода создают настоящие управляющие байты. Результат содержит уже два печатных символа на каждый из них: черту и букву. Так легче увидеть, почему поле неожиданно переносится. Сам журнал может содержать чувствительные данные — кодирование не делает их публичными.
<?php
$raw = "A\0B\tC\n";
$visible = addcslashes($raw, "\0..\37\\");
echo 'Диагностика: ', $visible, "\n";
echo 'После обратного разбора: ', bin2hex(stripcslashes($visible)), "\n";
echo 'Опасная буквенная маска: ', addcslashes('name', 'a..z'), "\n";
Ожидаемый результат
Диагностика: A\000B\tC\n
После обратного разбора: 41004209430a
Опасная буквенная маска: \n\a\m\e
Диапазон \0..\37 задаёт байты от 0 до 31 в восьмеричной записи; также выбрана обратная черта. Произвольная буквенная маска опасна: добавив черту к n, вы создадите последовательность, которая при обратном разборе станет переносом. Это не экранирование для SQL, HTML или shell.
Функция не знает контекста HTML, SQL, JSON или shell. Диагностическую строку для веб-страницы дополнительно экранируют как HTML. Публичный журнал не должен содержать секреты даже в экранированном виде. Для структурированного обмена используйте стандартный сериализатор.
Сопоставьте: stripcslashes, addslashes, bin2hex. Байты, символы и выбор операции.
stripcslashesВ оговорённом текстовом формате переносы записаны буквальными символами \n. Нужно восстановить настоящий многострочный текст. stripcslashes понимает C-подобные последовательности; применять её к любой строке из формы или к пути нельзя.
stripcslashes(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Строка, содержащая C-последовательности вроде \n, \r, \t, восьмеричные и шестнадцатеричные записи. |
| Результат | Декодированная строка; её байты могут включать перевод строки и NUL. |
| Ошибки и границы | Не выполняет строгую проверку собственного формата и не является JSON-парсером. Нельзя безусловно применять к путям или необработанному вводу. |
| Версия / расширение | PHP 4+; standard |
- Строка Кисть\nВалик содержит видимую черту и букву n.
- stripcslashes заменяет эту пару одним LF.
- В выводе Кисть и Валик оказываются на двух строках.
<?php
$encoded = 'Кисть\nВалик';
echo 'До: ', $encoded, "\n";
echo "После:\n", stripcslashes($encoded), "\n";
Ожидаемый результат
До: Кисть\nВалик
После:
Кисть
Валик
Одинарные кавычки PHP сохраняют \n как два обычных символа. Именно функция создаёт LF. В консоли он виден как новая строка; при выводе обычного HTML перенос сам по себе не создаёт <br>. Для HTML используют нужную разметку после экранирования текста.
<?php
$encoded = '\x42\12241';
echo 'Полученный код: ', stripcslashes($encoded), "\n";
$packet = stripcslashes('BR41\0qty=12\r\n');
echo 'Байты пакета: ', bin2hex($packet), "\n";
Ожидаемый результат
Полученный код: BR41
Байты пакета: 42523431007174793d31320d0a
\x42 — шестнадцатеричная B, \122 — восьмеричная R. Это байты, а не универсальная запись Unicode-кодовых точек. Пакет содержит NUL и CRLF, поэтому показан в HEX. Для JSON вызывают json_decode; для addslashes-слоя — stripslashes. У каждой записи свой синтаксис.
Перед выводом восстановленного текста подумайте о контексте: декодирование может создать переводы строк в журнале или управляющие байты терминала. Для JSON нужна json_decode; для addslashes — stripslashes. Не выполняйте восстановленную строку как код.
Сопоставьте: addcslashes, stripslashes, bin2hex. Байты, символы и выбор операции.
quotemetaПонять старый ограниченный способ экранирования метасимволов и выбрать полный инструмент для регулярного выражения.
quotemeta(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Текст, где перед . \ + * ? [ ^ ] ( $ ) добавляется обратная косая черта. |
| Результат | Строка с экранированным указанным набором; с PHP 8 пустой вход возвращает "" (раньше false). |
| Ошибки и границы | Набор неполный для современного PCRE: например | и фигурные скобки не экранируются; разделитель регулярного выражения тоже не учитывается. |
| Версия / расширение | PHP 4+; standard |
- bolt(4)+washer.txt → техническая подпись.
- Скобки, плюс и точка получают обратную черту.
- Результат показывает только предусмотренный исторический набор.
<?php
echo quotemeta('bolt(4)+washer.txt'), "\n";
echo quotemeta('a|b{2}/c'), "\n";
var_dump(quotemeta(''));
Ожидаемый результат
bolt\(4\)\+washer\.txt
a|b{2}/c
string(0) ""
Второй вывод специально содержит символы, которые не были защищены. Нельзя переносить договор quotemeta на весь синтаксис PCRE.
<?php
$label = 'bolt(4)|washer';
$pattern = '~' . preg_quote($label, '~') . '~';
var_dump(preg_match($pattern, 'item: bolt(4)|washer') === 1);
var_dump(preg_match($pattern, 'item: bolt(4)') === 1);
Ожидаемый результат
bool(true)
bool(false)
Для реальной задачи выбирается preg_quote с тем же разделителем ~, что у шаблона. Вертикальная черта ищется буквально, а не превращает шаблон в альтернативу.
Если нужен только буквальный поиск без правил регулярного выражения, str_contains проще. Не применяйте quotemeta для HTML, SQL или shell. Сначала определите язык назначения, затем используйте его экранирование.
Сопоставьте: str_contains, addcslashes, htmlspecialchars. Байты, символы и выбор операции.
echoПрограмма может вычислить цену и сохранить её в переменной, но человек ничего не увидит, пока вы не направите результат в вывод. echo делает именно это. В консоли появляется текст; в веб-приложении этот текст входит в HTTP-ответ, который затем отображает браузер.
echo expression, ...; <?= expression ?>Запишите echo, затем выражение, затем ;. Нельзя присвоить «ответ echo» переменной: ответа нет.
| Часть договора | Что передать и что получить |
|---|---|
| expressions | Одно или несколько выражений через запятую. Это конструкция языка, а не вызываемая функция; общий список не заключают в (). |
| Результат | Ничего не возвращает; отправляет строковые представления в текущий вывод без автоматических пробелов и переводов строк. |
| Ошибки и границы | Нельзя использовать как callback/переменную функцию или с именованными аргументами. Не-строковые значения преобразуются даже при strict_types=1; массив вызывает предупреждение, объект без __toString — Error. |
| Версия / расширение | PHP 4+; standard |
- $quantity хранит число 2. Само присваивание ничего не показывает.
- echo получает подпись и значение $quantity.
- Человек видит Количество: 2. Переменная по-прежнему хранит 2.
<?php
$quantity = 2;
echo 'Количество: ', $quantity, "\n";
Ожидаемый результат
Количество: 2
echo — конструкция языка. После неё перечислены три части: подпись в кавычках, переменная без кавычек и перенос \n. Запятые отделяют части, точка с запятой завершает команду. echo не добавляет ни пробелов, ни переносов самостоятельно.
<?php
$quantity = 2;
echo 'Было: ', $quantity, "\n";
$quantity = $quantity + 1;
echo 'Стало: ', $quantity, "\n";
echo 'Заказ ', 42, ': готов', "\n";
Ожидаемый результат
Было: 2
Стало: 3
Заказ 42: готов
Программа выполняется сверху вниз. В каждом месте echo читает текущее значение переменной. Ранее выведенная строка не обновляется, когда переменная меняется. Для построения строки без немедленного вывода используют соединение точкой или sprintf.
<?php
$name = 'Кисть <Pro> & валик';
$quantity = 2;
echo '<p>Товар: ', htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), "</p>\n";
echo '<p>Количество: ', $quantity, "</p>\n";
Ожидаемый результат
<p>Товар: Кисть <Pro> & валик</p>
<p>Количество: 2</p>
Авторские <p> создают абзацы. Название проходит htmlspecialchars, чтобы <Pro> отображалось буквальным текстом. Число в этом примере задано автором. В окне ниже показан браузерный результат; в текстовом выводе выше видна отправленная разметка. Обычный \n внутри HTML не заменяет отдельный абзац.
Товар: Кисть <Pro> & валик
Количество: 2
<?php $name = 'Кисть'; ?>
<p>Товар: <?= htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></p>
Ожидаемый результат
<p>Товар: Кисть</p>
Короткая запись <?= выражение ?> означает вывод выражения и работает независимо от short_open_tag. HTML вокруг остаётся обычной разметкой. Внутри шаблона тоже нужно защищать внешние данные для места вывода.
Товар: Кисть
echo не добавляет разделители, не экранирует и не ждёт браузерного подтверждения. echo("text") допустимо как выражение в скобках, но echo("a", "b") — синтаксическая ошибка: это не функция. В сложных выражениях явно группируйте сумму/условие; несколько аргументов часто проще конкатенации. true выводится как "1", false/null как пустой текст — для диагностики типов нужен var_dump.
Сопоставьте: print, printf, htmlspecialchars. Байты, символы и выбор операции.
printНужно вывести готовое сообщение одной командой. print, как и echo, направляет текст в текущий вывод. В старом коде вы также можете встретить использование результата print: это всегда число 1, а не длина текста и не подтверждение доставки клиенту.
print expression| Часть договора | Что передать и что получить |
|---|---|
| expression | Одно выражение. Конструкция языка; скобки могут группировать выражение, но не превращают print в функцию. |
| Результат | Печатает строковое представление без автоматического перевода строки и всегда возвращает int 1. |
| Ошибки и границы | Не является callback, не поддерживает именованные аргументы и несколько значений через запятую. Преобразование к строке выполняется даже в strict_types. |
| Версия / расширение | PHP 4+; standard |
- $message содержит Заказ сохранён.
- print выводит эту строку.
- Если присвоить ответ print переменной, в ней окажется 1.
<?php
$message = 'Заказ сохранён';
print $message . "\n";
Ожидаемый результат
Заказ сохранён
print принимает одно выражение. Здесь точка соединяет сообщение с переносом строки. В отличие от echo перечислить несколько частей через запятую нельзя. Для повседневного вывода обе конструкции подходят, выбор не меняет правила безопасности HTML.
<?php
$result = print "Сохранено 2 товара\n";
echo 'Ответ print: ', $result, "\n";
Ожидаемый результат
Сохранено 2 товара
Ответ print: 1
Сначала выполняется print и появляется сообщение, затем $result получает 1. Два сохранённых товара, длина строки и число 1 — разные вещи. Не используйте этот результат для проверки записи в базу: такую проверку делают по результату операции с базой.
<?php
$count = 0;
$message = $count === 0 ? 'Каталог пока пуст' : 'Товары загружены';
print $message . "\n";
Ожидаемый результат
Каталог пока пуст
Условный оператор ?: выбирает одну из двух строк. Выбор и вывод записаны отдельно, поэтому не приходится угадывать приоритет print внутри сложного условия. Конструкция всегда возвращает 1: делать вывод условием успешности операции не нужно.
print("text") && false может быть прочитан языком иначе, чем ожидает новичок: скобки только вокруг аргумента не группируют всю конструкцию. Избегайте вывода как побочного действия в сложных условиях. Для HTML пользовательские данные экранируют независимо от выбора echo или print.
Сопоставьте: echo, printf, htmlspecialchars. Байты, символы и выбор операции.
printfНужна строка отчёта сразу в выводе: printf печатает результат форматирования и возвращает число выведенных байтов. В отличие от sprintf его результатом не является сам текст.
printf(string $format, mixed ...$values): intПрочитайте вызов слева направо: как оформить, затем каждое значение отдельно. Результат функции описан отдельно от её вывода.
| Часть договора | Что передать и что получить |
|---|---|
| format | Фиксированный шаблон: %s — строка, %d — целое, %.2F — дробное с точкой и двумя знаками. Полная грамматика разобрана в отдельном разделе ниже по ссылке. |
| values | Отдельные значения после формата; ... в сигнатуре означает любое их число. Каждый спецификатор потребляет нужное значение. |
| Результат | Печатает в текущий вывод и возвращает число записанных байтов, не Unicode-букв. |
| Ошибки и границы | Ошибочный формат/номер/ширина/точность — ValueError; недостаток значений — ArgumentCountError. С PHP 8 эти функции не используют старый false как универсальный ответ ошибки формата. |
| Версия / расширение | PHP 4+; standard |
- Шаблон "%s: %d шт." и значения Кисть, 3 передаются в printf.
- Текст сразу попадает в вывод.
- Возвращённое число — размер вывода в байтах; сохранять его вместо сообщения было бы ошибкой.
<?php
printf("%s: %d шт.\n", 'Кисть', 3);
Ожидаемый результат
Кисть: 3 шт.
Шаблон в двойных кавычках содержит настоящий перевод строки \n. %s заменяется названием, %d — целым количеством. Дополнительный echo вокруг printf не нужен: функция уже печатает.
<?php
$written = printf('SKU=%s', 'A42');
echo "\nВозвращено байтов: ", $written, "\n";
$saved = sprintf('SKU=%s', 'A42');
echo 'Сохранённый текст: ', $saved, "\n";
Ожидаемый результат
SKU=A42
Возвращено байтов: 7
Сохранённый текст: SKU=A42
В $written находится 7, а не SKU=A42. Когда сообщение нужно передать в другую функцию или вернуть из своей функции, используйте sprintf. Байты не равны числу русских букв. Все флаги, позиционные аргументы и ошибки формата описаны в общей грамматике.
Формат задаёт автор программы, пользовательский текст передают через %s. Не путайте оформление с экранированием и проверкой типов/диапазонов. Ширина задаёт минимум, а не обрезание. Все спецификаторы, флаги, точность, аргументы по номеру и ошибки.
Сопоставьте: sprintf, fprintf, vprintf, vsprintf, vfprintf, number_format. Байты, символы и выбор операции.
fprintfНужно записать оформленную строку прямо в открытый файл отчёта. fprintf принимает файловый поток, формат и отдельные значения. Имя файла сначала открывают через fopen; функция не принимает путь вместо потока.
fprintf(resource $stream, string $format, mixed ...$values): intПрочитайте вызов слева направо: куда писать, как оформить, затем каждое значение отдельно. Результат функции описан отдельно от её вывода.
| Часть договора | Что передать и что получить |
|---|---|
| stream | Открытый ресурс потока, обычно из fopen; нужен режим, разрешающий запись. Поток не закрывается функцией. |
| format | Фиксированный шаблон: %s — строка, %d — целое, %.2F — дробное с точкой и двумя знаками. Полная грамматика разобрана в отдельном разделе ниже по ссылке. |
| values | Отдельные значения после формата; ... в сигнатуре означает любое их число. Каждый спецификатор потребляет нужное значение. |
| Результат | Пишет в stream и возвращает число записанных байтов, не Unicode-букв. |
| Ошибки и границы | Ошибочный формат/номер/ширина/точность — ValueError; недостаток значений — ArgumentCountError. Неверный или закрытый ресурс не является рабочим потоком; обработайте TypeError и ошибки записи, не считайте наличие строки в памяти подтверждением сохранения файла. С PHP 8 эти функции не используют старый false как универсальный ответ ошибки формата. |
| Версия / расширение | PHP 5+; standard |
- fopen открывает новый учебный файл и возвращает ресурс потока.
- fprintf форматирует строку и записывает её в этот поток.
- После fclose файл содержит Кисть: 3 шт.; чтение ниже показывает его настоящее содержимое.
<?php
fprintf(STDOUT, "%s: %d шт.\n", 'Кисть', 3);
Ожидаемый результат
Кисть: 3 шт.
STDOUT — заранее открытый поток стандартного вывода в PHP CLI. Благодаря ему первый вызов можно запустить без работы с файлами. Первым передаётся поток, вторым формат, затем значения. В обработчике HTTP этот CLI-поток не предполагается: для обычного вывода страницы есть echo/printf. Следующий пример открывает собственный файл.
<?php
$path = __DIR__ . '/stock-report-demo.txt';
$stream = fopen($path, 'xb');
if ($stream === false) {
throw new RuntimeException('Не удалось создать новый файл отчёта');
}
try {
$written = fprintf($stream, "%s: %d шт.\n", 'Кисть', 3);
if ($written !== strlen("Кисть: 3 шт.\n")) {
throw new RuntimeException('Строка записана не полностью');
}
} finally {
fclose($stream);
}
echo "Содержимое созданного отчёта:\n";
echo file_get_contents($path);
Ожидаемый результат
Содержимое созданного отчёта:
Кисть: 3 шт.
Запускайте в отдельной учебной папке. Режим xb создаёт новый файл и отказывается перезаписывать существующий; повторный запуск требует нового имени/папки. __DIR__ привязывает путь к папке PHP-файла. Возвращаемое число сравнивается с длиной известной строки, чтобы не принять неполную запись за успех. finally закрывает поток и при исключении. Пример действительно записывает и читает файл. В реальной работе обрабатывают и предупреждения/отказы открытия; общее устройство потоков объясняется в главе о файлах.
Формат задаёт автор программы, пользовательский текст передают через %s. Не путайте оформление с экранированием и проверкой типов/диапазонов. Ширина задаёт минимум, а не обрезание. Все спецификаторы, флаги, точность, аргументы по номеру и ошибки.
Сопоставьте: printf, sprintf, vprintf, vsprintf, vfprintf, number_format. Байты, символы и выбор операции.
vprintfДанные уже находятся в массиве: название товара и количество. vprintf подставляет элементы массива в формат по порядку. Сразу выводит строку и возвращает число выведенных байтов.
vprintf(string $format, array $values): intПрочитайте вызов слева направо: как оформить, затем массив значений. Результат функции описан отдельно от её вывода.
| Часть договора | Что передать и что получить |
|---|---|
| format | Фиксированный шаблон: %s — строка, %d — целое, %.2F — дробное с точкой и двумя знаками. Полная грамматика разобрана в отдельном разделе ниже по ссылке. |
| values | Массив значений в порядке спецификаторов; используются его значения, не имена ключей. |
| Результат | Печатает в текущий вывод и возвращает число записанных байтов, не Unicode-букв. |
| Ошибки и границы | Ошибочный формат/номер/ширина/точность — ValueError; недостаток элементов values — ValueError. С PHP 8 эти функции не используют старый false как универсальный ответ ошибки формата. |
| Версия / расширение | PHP 4.1+; standard |
- $values = ["Кисть", 3] содержит два значения.
- %s получает элемент 0, %d — элемент 1.
- Получается Кисть: 3 шт.; ключи массива не являются именами меток.
<?php
$format = '%s: %d шт.';
$values = ['Кисть', 3];
vprintf($format . "\n", $values);
Ожидаемый результат
Кисть: 3 шт.
Буква v обозначает форму с массивом аргументов. Это удобно, когда значения уже подготовлены как список. Формат принадлежит программе; не передавайте вместо него произвольный текст клиента.
<?php
$rows = [['Кисть', 3], ['Валик', 2]];
foreach ($rows as $values) {
vprintf("%s: %d шт.\n", $values);
}
Ожидаемый результат
Кисть: 3 шт.
Валик: 2 шт.
Внешний массив содержит строки заказа, внутренний — значения одной строки. foreach передаёт каждый внутренний массив форматтеру. При недостаточном числе значений возникает ошибка; задавайте и проверяйте схему данных до форматирования. Для HTML полученный текст нужно экранировать.
Формат задаёт автор программы, пользовательский текст передают через %s. Не путайте оформление с экранированием и проверкой типов/диапазонов. Ширина задаёт минимум, а не обрезание. Все спецификаторы, флаги, точность, аргументы по номеру и ошибки.
Сопоставьте: printf, sprintf, fprintf, vsprintf, vfprintf, number_format. Байты, символы и выбор операции.
vsprintfДанные уже находятся в массиве: название товара и количество. vsprintf подставляет элементы массива в формат по порядку. Возвращает готовую строку для дальнейшей работы.
vsprintf(string $format, array $values): stringПрочитайте вызов слева направо: как оформить, затем массив значений. Результат функции описан отдельно от её вывода.
| Часть договора | Что передать и что получить |
|---|---|
| format | Фиксированный шаблон: %s — строка, %d — целое, %.2F — дробное с точкой и двумя знаками. Полная грамматика разобрана в отдельном разделе ниже по ссылке. |
| values | Массив значений в порядке спецификаторов; используются его значения, не имена ключей. |
| Результат | Возвращает сформированную строку и ничего не печатает. |
| Ошибки и границы | Ошибочный формат/номер/ширина/точность — ValueError; недостаток элементов values — ValueError. С PHP 8 эти функции не используют старый false как универсальный ответ ошибки формата. |
| Версия / расширение | PHP 4.1+; standard |
- $values = ["Кисть", 3] содержит два значения.
- %s получает элемент 0, %d — элемент 1.
- Получается Кисть: 3 шт.; ключи массива не являются именами меток.
<?php
$format = '%s: %d шт.';
$values = ['Кисть', 3];
$message = vsprintf($format, $values);
echo $message, "\n";
Ожидаемый результат
Кисть: 3 шт.
Буква v обозначает форму с массивом аргументов. Это удобно, когда значения уже подготовлены как список. Формат принадлежит программе; не передавайте вместо него произвольный текст клиента.
<?php
$rows = [['Кисть', 3], ['Валик', 2]];
foreach ($rows as $values) {
$line = vsprintf("%s: %d шт.", $values); echo $line, "\n";
}
Ожидаемый результат
Кисть: 3 шт.
Валик: 2 шт.
Внешний массив содержит строки заказа, внутренний — значения одной строки. foreach передаёт каждый внутренний массив форматтеру. При недостаточном числе значений возникает ошибка; задавайте и проверяйте схему данных до форматирования. Для HTML полученный текст нужно экранировать.
Формат задаёт автор программы, пользовательский текст передают через %s. Не путайте оформление с экранированием и проверкой типов/диапазонов. Ширина задаёт минимум, а не обрезание. Все спецификаторы, флаги, точность, аргументы по номеру и ошибки.
Сопоставьте: printf, sprintf, fprintf, vprintf, vfprintf, number_format. Байты, символы и выбор операции.
vfprintfНужно записать оформленную строку прямо в открытый файл отчёта. vfprintf принимает файловый поток, формат и массив значений. Имя файла сначала открывают через fopen; функция не принимает путь вместо потока.
vfprintf(resource $stream, string $format, array $values): intПрочитайте вызов слева направо: куда писать, как оформить, затем массив значений. Результат функции описан отдельно от её вывода.
| Часть договора | Что передать и что получить |
|---|---|
| stream | Открытый ресурс потока, обычно из fopen; нужен режим, разрешающий запись. Поток не закрывается функцией. |
| format | Фиксированный шаблон: %s — строка, %d — целое, %.2F — дробное с точкой и двумя знаками. Полная грамматика разобрана в отдельном разделе ниже по ссылке. |
| values | Массив значений в порядке спецификаторов; используются его значения, не имена ключей. |
| Результат | Пишет в stream и возвращает число записанных байтов, не Unicode-букв. |
| Ошибки и границы | Ошибочный формат/номер/ширина/точность — ValueError; недостаток элементов values — ValueError. Неверный или закрытый ресурс не является рабочим потоком; обработайте TypeError и ошибки записи, не считайте наличие строки в памяти подтверждением сохранения файла. С PHP 8 эти функции не используют старый false как универсальный ответ ошибки формата. |
| Версия / расширение | PHP 5+; standard |
- fopen открывает новый учебный файл и возвращает ресурс потока.
- vfprintf форматирует строку и записывает её в этот поток.
- После fclose файл содержит Кисть: 3 шт.; чтение ниже показывает его настоящее содержимое.
<?php
vfprintf(STDOUT, "%s: %d шт.\n", ['Кисть', 3]);
Ожидаемый результат
Кисть: 3 шт.
STDOUT — заранее открытый поток стандартного вывода в PHP CLI. Благодаря ему первый вызов можно запустить без работы с файлами. Первым передаётся поток, вторым формат, затем значения. В обработчике HTTP этот CLI-поток не предполагается: для обычного вывода страницы есть echo/printf. Следующий пример открывает собственный файл.
<?php
$path = __DIR__ . '/stock-report-demo.txt';
$stream = fopen($path, 'xb');
if ($stream === false) {
throw new RuntimeException('Не удалось создать новый файл отчёта');
}
try {
$written = vfprintf($stream, "%s: %d шт.\n", ['Кисть', 3]);
if ($written !== strlen("Кисть: 3 шт.\n")) {
throw new RuntimeException('Строка записана не полностью');
}
} finally {
fclose($stream);
}
echo "Содержимое созданного отчёта:\n";
echo file_get_contents($path);
Ожидаемый результат
Содержимое созданного отчёта:
Кисть: 3 шт.
Запускайте в отдельной учебной папке. Режим xb создаёт новый файл и отказывается перезаписывать существующий; повторный запуск требует нового имени/папки. __DIR__ привязывает путь к папке PHP-файла. Возвращаемое число сравнивается с длиной известной строки, чтобы не принять неполную запись за успех. finally закрывает поток и при исключении. Пример действительно записывает и читает файл. В реальной работе обрабатывают и предупреждения/отказы открытия; общее устройство потоков объясняется в главе о файлах.
Формат задаёт автор программы, пользовательский текст передают через %s. Не путайте оформление с экранированием и проверкой типов/диапазонов. Ширина задаёт минимум, а не обрезание. Все спецификаторы, флаги, точность, аргументы по номеру и ошибки.
Сопоставьте: printf, sprintf, fprintf, vprintf, vsprintf, number_format. Байты, символы и выбор операции.
sscanfУстройство или старая программа передаёт короткую строку известного формата, например T=23.5 H=48. sscanf извлекает значения по шаблону и сразу преобразует числовые части. После разбора нужно проверить, что получены все поля и не осталось неожиданного хвоста.
sscanf(string $string, string $format, mixed &...$vars): array|int|null| Часть договора | Что передать и что получить |
|---|---|
| string | Входная строка. |
| format | Формат разбора: %s читает до пробела, %d десятичное целое; пробельный символ в формате подходит к пробельному разделению входа. |
| vars | Необязательные переменные для записи по ссылке. Без них возвращается массив значений; с ними — число присваиваний, при конце входа до первого значения — -1. |
| Результат | Без vars: массив разобранных значений, null на месте недостающей конверсии; пустой вход может вернуть null целиком. С vars: число присвоенных значений; неполученные переменные могут сохранить старое значение. |
| Ошибки и границы | Не является строгим валидатором всей строки: хвост может остаться непрочитанным. Проверяйте число/типы результатов и при необходимости %n. Несогласованный формат/число ссылочных переменных вызывает ошибки; старое значение переменной нельзя считать новым успехом. |
| Версия / расширение | PHP 4.0.1+; standard |
- Вход T=23.5 H=48 соответствует шаблону T=%f H=%d.
- %f читает дробное 23.5, %d — целое 48.
- Массив результата содержит температуру и влажность в указанном порядке.
<?php
$values = sscanf('T=23.5 H=48', 'T=%f H=%d');
echo 'Температура: ', $values[0], " °C\n";
echo 'Влажность: ', $values[1], "%\n";
Ожидаемый результат
Температура: 23.5 °C
Влажность: 48%
При двух аргументах ответ — массив, индексы 0 и 1 соответствуют спецификаторам формата. Здесь строка известна и корректна. Для данных извне нельзя сразу обращаться к результату и считать частично прочитанную запись успешной.
<?php
$records = ['T=23.5 H=48', 'T=23.5', 'T=23.5 H=48oops'];
foreach ($records as $record) {
$temperature = $humidity = $used = null;
$assigned = sscanf($record, 'T=%f H=%d%n', $temperature, $humidity, $used);
if ($assigned !== 3 || $used !== strlen($record) || $humidity < 0 || $humidity > 100) {
echo "Измерение отклонено\n";
continue;
}
echo 'Принято: ', $temperature, ' °C, ', $humidity, "%\n";
}
Ожидаемый результат
Принято: 23.5 °C, 48%
Измерение отклонено
Измерение отклонено
Дополнительные аргументы получают значения по ссылке; ответ функции теперь число присваиваний. %n сохраняет количество прочитанных байтов. Сравнение с strlen запрещает хвост oops. Инициализация null не позволяет случайно использовать прошлое значение после неудачи. Проверка влажности — правило нашего формата, не свойство sscanf.
Форматы scanf отличаются от printf: функция не зависит от локали; F/g/G/b не поддерживаются; D — десятичное, i определяет основание, n считает прочитанные байты. %s останавливается на пробелах; * подавляет присваивание конверсии, это не динамическая ширина printf. Ширина ограничивает чтение поля. Для сложных вложенных/кавычечных форматов используйте их парсер; для строгого договора артикула отдельно проверяйте его алфавит и длину.
Сопоставьте: sprintf, explode, str_getcsv. Байты, символы и выбор операции.
number_formatЧеловек должен увидеть «12 345,50 руб.», а в программе хранится число 12345.5. number_format создаёт строку с выбранными разделителями и количеством дробных знаков. Это завершающий этап отображения, а не способ хранить деньги или разбирать ввод.
number_format(float $num, int $decimals = 0, ?string $decimal_separator = ".", ?string $thousands_separator = ","): string| Часть договора | Что передать и что получить |
|---|---|
| num | Число для оформления; это float, не точная десятичная запись денег. |
| decimals | Число знаков после разделителя; 0 скрывает дробный разделитель. С PHP 8.3 отрицательное значение округляет слева от него (например -2 до сотен). |
| decimal_separator | Строка дробного разделителя, по умолчанию точка; null использует стандартное значение. |
| thousands_separator | Строка разделителя групп тысяч, по умолчанию запятая; "" отключает группировку. null использует стандартное значение. |
| Результат | Оформленная строка с округлением half up. Её не нужно использовать дальше как число для расчётов. |
| Ошибки и границы | До PHP 8 допустимы 1,2 или 4 аргумента, но не 3; теперь допустим и 3. До PHP 8.3 отрицательные decimals работали как 0; с PHP 7.2 не выводится "-0" при округлении малого отрицательного числа. |
| Версия / расширение | PHP 4+; отрицательная точность — PHP 8.3+; standard |
- Число 12345.5 передаётся для оформления.
- 2 задаёт два знака, запятая — дробный разделитель, пробел — разделитель тысяч.
- Получается строка "12 345,50", к которой добавляется подпись руб.
<?php
$value = 12345.5;
$shown = number_format($value, 2, ',', ' ');
echo $shown, " руб.\n";
echo 'Тип результата: ', get_debug_type($shown), "\n";
Ожидаемый результат
12 345,50 руб.
Тип результата: string
Порядок аргументов: число, дробные знаки, дробный разделитель, разделитель тысяч. Ответ — строка. Её не складывают с другой оформленной ценой: расчёты выполняют до оформления, используя согласованное представление денег.
<?php
$kopecks = 1234550;
$roubles = intdiv($kopecks, 100);
$fraction = $kopecks % 100;
$shown = number_format($roubles, 0, ',', ' ') . ',' . sprintf('%02d', $fraction);
echo $shown, " руб.\n";
Ожидаемый результат
12 345,50 руб.
В этом примере неотрицательная сумма хранится целым числом: intdiv берёт целые рубли, % — остаток копеек. sprintf дополняет 5 копеек до 05. Это избегает деления суммы на float в показанном диапазоне обычных сумм; number_format всё равно принимает float, поэтому для огромных целых нужна иная стратегия, а отрицательные суммы требуют отдельно обработать знак.
<?php
echo number_format(12345.678, 2, ',', ' '), "\n";
echo number_format(12345, -2, ',', ' '), "\n";
echo number_format(-0.01, 0), "\n";
echo number_format(12345.5, 2, '.', ''), "\n";
Ожидаемый результат
12 345,68
12 300
0
12345.50
decimals=-2 округляет до сотен с PHP 8.3. Малое отрицательное число при округлении до нуля не выводится как -0 с PHP 7.2. Пустой четвёртый аргумент отключает группировку. Округление представления не должно незаметно менять бизнес-расчёт; десятичные финансовые правила задаются отдельно.
Не разбирайте результат обратным float-приведением: "1 299,50" не является машинной десятичной записью. Для валютных правил разных локалей есть NumberFormatter; для точной арифметики больших/дробных сумм выбирайте подходящий числовой тип. Не обещайте точного округления произвольной десятичной строки после её превращения во float.
Сопоставьте: sprintf, localeconv. Байты, символы и выбор операции.
parse_strЕсть строка параметров URL, например из сохранённой ссылки на фильтр. parse_str разбирает её в PHP-массив, как это обычно делает PHP для query-параметров запроса. Ответ записывается во второй аргумент, а не возвращается как значение функции.
parse_str(string $string, array &$result): voidВызов записывает ответ в $fields: $fields = parse_str(...) неверно, потому что возвращаемого массива у функции нет.
| Часть договора | Что передать и что получить |
|---|---|
| string | Только query-часть без полного URL: имя=значение&имя=значение; percent-encoding декодируется, + становится пробелом. |
| result | Обязательная переменная для массива результата. С PHP 8 нельзя опускать второй аргумент; старое создание локальных переменных устарело в 7.2. |
| Результат | Ничего не возвращает; записывает полученные значения в result. Значения обычно строки или вложенные массивы согласно [] в именах. |
| Ошибки и границы | max_input_vars ограничивает число параметров: превышение вызывает E_WARNING и усечение массива. Имена с пробелами/точками нормализуются в подчёркивания; повторное обычное имя перезаписывает прежнее значение. |
| Версия / расширение | PHP 4+; standard |
- name=Anna+Petrova&qty=0 содержит две пары имя=значение.
- parse_str разделяет пары, декодирует + как пробел и записывает массив в $fields.
- $fields["name"] = "Anna Petrova", $fields["qty"] = "0" — оба значения строки.
<?php
$query = 'name=Anna+Petrova&qty=0';
parse_str($query, $fields);
echo 'Имя: ', $fields['name'], "\n";
echo 'Количество: ', $fields['qty'], "\n";
echo 'Тип количества: ', get_debug_type($fields['qty']), "\n";
Ожидаемый результат
Имя: Anna Petrova
Количество: 0
Тип количества: string
Переменную $fields создаёт/заполняет вызов. Запись $fields = parse_str(...) была бы ошибочной: возвращаемое значение void не содержит массива. Для обычного текущего HTTP-запроса параметры уже доступны в $_GET; повторный ручной разбор обычно не нужен.
<?php
parse_str('tag=paint&tag=tools', $single);
parse_str('tag[]=paint&tag[]=tools&user.name=Anna', $multiple);
echo 'Обычный повтор: ', $single['tag'], "\n";
echo 'Явный список: ', implode(', ', $multiple['tag']), "\n";
echo 'Ключ после нормализации: ', $multiple['user_name'], "\n";
Ожидаемый результат
Обычный повтор: tools
Явный список: paint, tools
Ключ после нормализации: Anna
Обычный повтор перезаписывает предыдущее значение, [] явно формирует массив. Точки и пробелы в именах PHP заменяет на подчёркивания. Это конкретные правила PHP, а не универсальные правила любого API. Если поле должно быть строкой, проверяйте is_string: пользователь может передать name[]=... .
Декодирование не равно валидации. Не вызывайте urldecode второй раз: это может изменить данные повторно. Перед использованием ожидаемого строкового поля проверьте is_string — клиент может прислать name[]. parse_str не разбирает URL целиком и не является JSON-декодером; result не следует смешивать напрямую с конфигурацией или переменными через extract.
Сопоставьте: str_getcsv, explode. Байты, символы и выбор операции.
str_getcsvВ выгрузке поставщика название товара содержит запятую и кавычки. Обычное explode разделило бы название на лишние поля. str_getcsv понимает кавычки одной CSV-записи и возвращает именно её поля.
str_getcsv(string $string, string $separator = ",", string $enclosure = "\"", string $escape = "\\"): array| Часть договора | Что передать и что получить |
|---|---|
| string | Одна логическая CSV-запись. Запись может содержать перевод строки внутри кавычек; не делите CSV-файл обычным explode("\n"). |
| separator | Разделитель полей, ровно один байт. |
| enclosure | Кавычка, ровно один байт; внутри заключённого поля удваивается для буквального символа. |
| escape | Один байт нестандартного escape либо "", отключающее механизм. Передавайте "" явно; это поддерживается с PHP 7.4. |
| Результат | Индексный массив полей. Пустая входная строка даёт [null], поэтому пустую запись обрабатывают отдельным правилом. |
| Ошибки и границы | С PHP 8.4 неправильная длина separator/enclosure/escape вызывает ValueError; использование значения escape по умолчанию deprecated. LC_CTYPE может влиять на однобайтовые кодировки. Наличие массива не удостоверяет строгую корректность всего CSV. |
| Версия / расширение | PHP 5.3+; escape="" — PHP 7.4; строгая длина параметров / default escape deprecated — PHP 8.4+; standard |
- Строка "Кисть ""Профи"", широкая",8 содержит два поля, несмотря на запятую в названии.
- Кавычки ограничивают первое поле, удвоенная кавычка означает буквальную кавычку.
- Ответ: название Кисть "Профи", широкая и текстовое количество "8".
<?php
$record = '"Кисть ""Профи"", широкая",8';
$fields = str_getcsv($record, ',', '"', '');
echo 'Название: ', $fields[0], "\n";
echo 'Остаток: ', $fields[1], "\n";
echo 'Число полей: ', count($fields), "\n";
Ожидаемый результат
Название: Кисть "Профи", широкая
Остаток: 8
Число полей: 2
Переданы запись, запятая-разделитель, двойная кавычка и пустой escape. Последний аргумент отключает дополнительный нестандартный backslash-механизм; оставлять его по умолчанию deprecated с PHP 8.4. Полученный остаток ещё строка: число полей, типы и диапазоны проверяются по схеме импорта.
<?php
$record = "\"Кисть\nширокая\",8";
$fields = str_getcsv($record, ',', '"', '');
echo 'Полей: ', count($fields), "\n";
echo "Название:\n";
echo $fields[0], "\n";
echo 'Остаток: ', $fields[1], "\n";
$blank = str_getcsv('', ',', '"', '');
var_dump($blank);
Ожидаемый результат
Полей: 2
Название:
Кисть
широкая
Остаток: 8
array(1) {
[0]=>
NULL
}
Первое поле включает настоящий перевод строки. Поэтому целый CSV-файл нельзя сначала поделить через explode("\n"): это разрушит одну запись. Для чтения файла по логическим записям есть fgetcsv. Пустая CSV-строка даёт [null], а не пустой массив — правило пропуска пустых записей задайте явно.
При escape="\" сочетание черты и кавычки сохраняет оба байта в результате; удвоенная enclosure работает независимо от escape. PHP рекомендует явный escape="": другое значение может нарушить RFC 4180 и round-trip. Значение по умолчанию изменится не раньше PHP 9.0. С PHP 8.3 последнее поле из одной незакрытой кавычки стало "" вместо NUL. Для целого файла используйте fgetcsv: физические строки не всегда совпадают с логическими записями. После разбора проверяйте число столбцов, тип/диапазон количества и кодировку.
Сопоставьте: explode, parse_str. Байты, символы и выбор операции.
nl2brПокупатель написал комментарий в две строки, а на странице они слились. В обычном HTML перевод строки в исходнике не создаёт визуальный перенос. nl2br вставляет тег br перед переводом строки, поэтому комментарий можно показать с сохранением строк.
nl2br(string $string, bool $use_xhtml = true): string| Часть договора | Что передать и что получить |
|---|---|
| string | Текст с CRLF, LFCR, LF или CR. |
| use_xhtml | true (по умолчанию) вставляет <br />; false — <br>. |
| Результат | Строка с тегом перед каждым переводом строки. Исходные переводы строк остаются: это вставка, а не замена. |
| Ошибки и границы | Функция не экранирует HTML. Для пользовательского текста сначала htmlspecialchars, затем nl2br. |
| Версия / расширение | PHP 4+; standard |
- В $comment между предложениями находится настоящий перевод строки \n.
- Сначала htmlspecialchars защищает текст, затем nl2br добавляет разрешённые теги br.
- Браузер показывает два предложения на разных строках; <Б> остаётся буквальным текстом.
<?php
$comment = "Привезите после 18:00.\nПозвоните у ворот <Б>.";
$safeComment = htmlspecialchars($comment, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$htmlComment = nl2br($safeComment, false);
echo '<p>', $htmlComment, "</p>\n";
Ожидаемый результат
<p>Привезите после 18:00.<br>
Позвоните у ворот <Б>.</p>
Обработка выполняется в двух отдельных строках, чтобы был виден порядок. false выбирает HTML-тег <br>; значение по умолчанию true даёт <br />. Перевод строки остаётся и в исходнике: функция добавляет тег, а не удаляет LF. Читатель ниже видит обе строки результата.
Привезите после 18:00.
Позвоните у ворот <Б>.
<?php
$text = "Кисть\nВалик";
$wrong = htmlspecialchars(nl2br($text, false), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$right = nl2br(htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), false);
echo 'Обратный порядок: ', $wrong, "\n";
echo 'Нужный порядок: ', $right, "\n";
Ожидаемый результат
Обратный порядок: Кисть<br>
Валик
Нужный порядок: Кисть<br>
Валик
В первом случае htmlspecialchars защищает и добавленный автором <br>, превращая его в текст. В обычном HTML появится надпись <br>. Во втором защищены только исходные данные, а авторский тег добавлен после этого. В textarea и pre переносы отображаются и без nl2br; туда вставляют экранированный исходный текст.
<?php
echo nl2br('Кисть\nВалик', false), "\n";
echo nl2br("Кисть\nВалик", false), "\n";
Ожидаемый результат
Кисть\nВалик
Кисть<br>
Валик
В одинарных кавычках \n сохраняется как два обычных символа, поэтому функция не находит перенос. В двойных кавычках PHP создаёт настоящий LF. Для отображения текста также можно оставить PHP-строку без br и задать контейнеру CSS white-space: pre-wrap; экранирование всё равно требуется.
Если HTML должен оставаться простым текстовым узлом, можно вместо вставки тегов использовать CSS white-space: pre-wrap после обычного экранирования. nl2br не создаёт абзацы и не преобразует Markdown. Не применяйте её к JSON или значению HTML-атрибута.
Сопоставьте: htmlspecialchars, str_replace. Байты, символы и выбор операции.
htmlentitiesДля совместимости с HTML-представлением нужно записать символы, у которых есть именованные сущности, через эти сущности. htmlentities делает больше замен, чем htmlspecialchars. Для обычной UTF-8-страницы чаще достаточно htmlspecialchars: все буквы в сущности превращать не требуется.
htmlentities(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ?string $encoding = null, bool $double_encode = true): stringENT_* — именованные целые константы; | объединяет их биты. Вызов возвращает строку, а не печатает её. Флаги и UTF-8 пишутся явно, чтобы настройка окружения не меняла договор.
| Часть договора | Что передать и что получить |
|---|---|
| string | Исходный текст, корректный в выбранной кодировке. |
| flags | Какие кавычки обрабатывать, как обходиться с повреждением и для какого типа документа. Основной безопасный текстовый пример: ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5. |
| encoding | Явное имя, например UTF-8. null использует default_charset; это не автоматическая проверка кодировки. |
| double_encode | true обрабатывает все амперсанды, включая уже записанные сущности. false оставляет распознанные сущности данного типа документа. |
| Результат | Строка с всеми доступными именованными сущностями выбранной таблицы. Одиночная кавычка для HTML401 становится ', для XML1/XHTML/HTML5 — '. |
| Ошибки и границы | Повреждённые байты без ENT_SUBSTITUTE/ENT_IGNORE могут дать ""; неизвестная encoding вызывает предупреждение и fallback. ENT_IGNORE не рекомендуют. С PHP 8.1 изменились flags по умолчанию; nullable encoding поддерживается в PHP 8. |
| Версия / расширение | PHP 4+; standard |
- В тексте "© 2026 Кисть & валик" есть ©, &, а также кириллические буквы.
- В наборе HTML5 есть сущности и для этих букв: например, К становится К, © — ©.
- Браузер восстановит тот же видимый текст; PHP возвращает подготовленный HTML-текст.
<?php
$text = '© 2026 Кисть & валик';
echo htmlentities($text, ENT_QUOTES | ENT_HTML5, 'UTF-8'), "\n";
echo htmlspecialchars($text, ENT_QUOTES | ENT_HTML5, 'UTF-8'), "\n";
Ожидаемый результат
© 2026 Кисть & валик
© 2026 Кисть & валик
В словаре HTML5 есть именованные сущности для использованных кириллических букв, поэтому первая строка заметно длиннее. htmlspecialchars сохранила буквы и ©, изменив только специальный знак &. Результаты имеют одинаковый вид в браузере, но разную текстовую запись. Для обычного вывода русского текста в HTML не требуется заменять буквы сущностями.
<?php
$text = "Цена: 5 €; кавычка: '";
echo htmlentities($text, ENT_QUOTES | ENT_HTML5 | ENT_SUBSTITUTE, 'UTF-8'), "\n";
echo htmlentities($text, ENT_QUOTES | ENT_HTML401 | ENT_SUBSTITUTE, 'UTF-8'), "\n";
Ожидаемый результат
Цена: 5 €; кавычка: '
Цена: 5 €; кавычка: '
HTML5 имеет больше именованных сущностей: здесь заменились кириллица, двоеточия и точка с запятой. HTML 4.01 оставил их исходными символами. Флаги типа документа меняют допустимый словарь, а ENT_SUBSTITUTE отвечает за повреждённую последовательность кодировки. Не смешивайте HTML-кодирование с URL-кодированием, SQL-параметрами или JSON: каждое предназначено для своей границы.
Не все Unicode-символы имеют именованную сущность. При UTF-8 обычно достаточно htmlspecialchars; превращение é в é не усиливает защиту обычного текстового узла. Экранируйте на границе нужного вывода, храните исходный текст. Не вызывайте декодер «на всякий случай» перед экранированием: это меняет буквальные сущности пользователя. URL в href дополнительно требует проверки допустимой схемы/формирования самого URL; скрипт и CSS имеют другие контексты. Полные флаги, их приоритет, кодировки и правила повреждённых данных.
Сопоставьте: htmlspecialchars, html_entity_decode, htmlspecialchars_decode, get_html_translation_table. Байты, символы и выбор операции.
htmlspecialchars_decodeВ старом хранилище название сохранено как HTML-запись, например Кисть & валик. Для обычного текстового отчёта нужно вернуть исходный знак &. htmlspecialchars_decode раскрывает только специальные HTML-знаки, а не все именованные сущности.
htmlspecialchars_decode(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401): string| Часть договора | Что передать и что получить |
|---|---|
| string | Строка с сущностями специальных HTML-знаков. |
| flags | Выбор кавычек и типа документа: ENT_COMPAT — двойные, ENT_QUOTES — обе, ENT_NOQUOTES — ни одной; тип документа определяет допустимые сущности. |
| Результат | Строка с восстановленными &, <, > и выбранными кавычками. Нет отдельного параметра encoding; полный декодер сущностей — html_entity_decode. |
| Ошибки и границы | Декодирование может снова создать разметку; не печатайте результат в HTML как безопасный текст. По умолчанию flags изменены в PHP 8.1. |
| Версия / расширение | PHP 5.1+; standard |
- Кисть & валик — закодированная строка из известного старого формата.
- htmlspecialchars_decode раскрывает & в один знак &.
- Получается обычный текст; при последующем выводе в HTML его снова защищают для этого контекста.
<?php
$stored = 'Кисть & валик';
$text = htmlspecialchars_decode($stored, ENT_QUOTES | ENT_HTML5);
echo $text, "\n";
echo htmlspecialchars_decode('© <Pro>', ENT_QUOTES | ENT_HTML5), "\n";
Ожидаемый результат
Кисть & валик
© <Pro>
Функция раскрыла специальные < и >, но не ©: © осталась как запись. Для всех сущностей выбранного типа документа существует html_entity_decode. Уже раскрытая <Pro> является обычной PHP-строкой, но браузер может принять её за тег, если вывести без экранирования.
<?php
$stored = '"Pro" 'Blue' &';
echo htmlspecialchars_decode($stored, ENT_NOQUOTES), "\n";
echo htmlspecialchars_decode($stored, ENT_COMPAT), "\n";
echo htmlspecialchars_decode($stored, ENT_QUOTES), "\n";
Ожидаемый результат
"Pro" 'Blue' &
"Pro" 'Blue' &
"Pro" 'Blue' &
ENT_NOQUOTES сохраняет оба вида кавычек закодированными; ENT_COMPAT раскрывает двойные, ENT_QUOTES — оба вида. Знак & раскрывается во всех трёх примерах. Настройки должны соответствовать формату источника; многократное декодирование неизвестного текста может изменить намеренные буквальные последовательности.
Используйте только когда источник действительно хранит слой специальных сущностей и задача требует его снять. Функция не разбирает HTML-документ, не удаляет теги и не гарантирует одноразового декодирования всех вложенных слоёв. Полные флаги, их приоритет, кодировки и правила повреждённых данных.
Сопоставьте: htmlspecialchars, html_entity_decode, strip_tags. Байты, символы и выбор операции.
html_entity_decodeИз известного HTML-представления нужно получить обычную подпись для текстового отчёта: © должно стать ©, & — &. html_entity_decode раскрывает сущности выбранного типа документа. Самостоятельно теги она не удаляет.
html_entity_decode(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ?string $encoding = null): string| Часть договора | Что передать и что получить |
|---|---|
| string | Текст с именованными и числовыми сущностями. |
| flags | Кавычки и тип документа; ENTITY должна быть допустима для этого документа. XML1 не раскрывает произвольные имена из внешней DTD. |
| encoding | Кодировка результата, задавайте UTF-8 явно. Символ должен быть представим в ней и допустим для документа, иначе соответствующая сущность остаётся. |
| Результат | Строка с раскрытыми допустимыми сущностями; неизвестные/неподходящие остаются исходными. |
| Ошибки и границы | Не является санитайзером HTML. Повторное декодирование может менять намеренный буквальный текст. default flags изменены в PHP 8.1; encoding nullable с PHP 8.0. |
| Версия / расширение | PHP 4.3+; standard |
- Источник: © 2026 Кисть & валик.
- html_entity_decode с HTML5 и UTF-8 восстанавливает символы.
- Ответ: © 2026 Кисть & валик — обычный текст для выбранного текстового канала.
<?php
$encoded = '© 2026 Кисть & валик';
$text = html_entity_decode($encoded, ENT_QUOTES | ENT_HTML5, 'UTF-8');
echo $text, "\n";
Ожидаемый результат
© 2026 Кисть & валик
Вход должен действительно быть HTML-представлением. Если пользователь написал буквальное © в объяснении кода, автоматическое декодирование исказит его сообщение. Не декодируйте строку повторно без знания источника.
<?php
$encoded = ' Кисть ';
$text = html_entity_decode($encoded, ENT_QUOTES | ENT_HTML5, 'UTF-8');
echo 'Размер после trim: ', strlen(trim($text)), " байт\n";
echo 'Первый символ в HEX: ', bin2hex(substr($text, 0, 2)), "\n";
echo 'После mb_trim: [', mb_trim($text, encoding: 'UTF-8'), "]\n";
Ожидаемый результат
Размер после trim: 14 байт
Первый символ в HEX: c2a0
После mb_trim: [Кисть]
превратился в неразрывный пробел U+00A0, а не в обычный ASCII-пробел. Для выбранного правила очистки здесь подходит mb_trim из PHP 8.4 и mbstring. Декодирование и очистка краёв — две разные операции.
<?php
$encoded = '<strong>Кисть</strong>';
$text = html_entity_decode($encoded, ENT_QUOTES | ENT_HTML5, 'UTF-8');
echo 'Раскрытый текст: ', $text, "\n";
echo 'Для буквального показа в HTML: ', htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), "\n";
Ожидаемый результат
Раскрытый текст: <strong>Кисть</strong>
Для буквального показа в HTML: <strong>Кисть</strong>
В CLI обе строки видны как текст. В браузере первая могла бы стать оформлением, а вторая сохранит буквальное написание тегов. Декодер не проверяет допустимость разметки и не является санитайзером. Если нужна разрешённая пользовательская разметка, проверяется структура, атрибуты и ссылки отдельным инструментом.
Не удаляйте произвольно все Unicode-пробелы, если они значимы для поля; в PHP 8.4+ mb_trim решает именно согласованное удаление Unicode-пробелов. Декодирование сущностей не переводит строки из Windows-1251 в UTF-8 и не заменяет HTML-парсер. Полные флаги, их приоритет, кодировки и правила повреждённых данных.
Сопоставьте: htmlentities, htmlspecialchars_decode, trim. Байты, символы и выбор операции.
get_html_translation_tableПри отладке кодирования нужно понять, какие символы выбранный HTML-режим заменит и чем. get_html_translation_table возвращает таблицу соответствий. Это инструмент изучения и диагностики, а не повод писать собственную замену htmlspecialchars.
get_html_translation_table(int $table = HTML_SPECIALCHARS, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, string $encoding = "UTF-8"): array| Часть договора | Что передать и что получить |
|---|---|
| table | HTML_SPECIALCHARS — специальный набор; HTML_ENTITIES — весь набор именованных сущностей. |
| flags | Какие кавычки включить и для какого типа документа построить таблицу. |
| encoding | Кодировка ключей таблицы; по умолчанию UTF-8, в отличие от null/default_charset у основных кодеров. |
| Результат | Массив исходный_символ => выбранная_сущность. Он содержит только форму, которую используют кодеры, а не все возможные числовые/именованные записи одного символа. |
| Ошибки и границы | Неизвестная кодировка сообщает предупреждение и использует fallback. Для table договор задаёт только HTML_SPECIALCHARS и HTML_ENTITIES; не рассчитывайте на отказ при произвольном другом числе. Значения flags по умолчанию изменены в PHP 8.1. |
| Версия / расширение | PHP 4+; standard |
- Запрос HTML_SPECIALCHARS выбирает только специальные знаки.
- Ключ массива — исходный символ, значение — его сущность.
- Для & таблица даёт &, для < — <.
<?php
$table = get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES | ENT_HTML5, 'UTF-8');
echo '& записывается как ', $table['&'], "\n";
echo '< записывается как ', $table['<'], "\n";
echo 'Всего специальных символов: ', count($table), "\n";
Ожидаемый результат
& записывается как &
< записывается как <
Всего специальных символов: 5
Массив удобно читать по ключу-символу. Тип документа и флаги кавычек влияют на таблицу. Сама функция не кодирует переданный пользовательский текст: такого аргумента у неё нет.
<?php
$special = get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES | ENT_HTML5, 'UTF-8');
$all = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES | ENT_HTML5, 'UTF-8');
echo 'В специальных: ', $special['©'] ?? 'нет замены', "\n";
echo 'В полной таблице HTML5: ', $all['©'], "\n";
Ожидаемый результат
В специальных: нет замены
В полной таблице HTML5: ©
Это наглядное различие HTML_SPECIALCHARS и HTML_ENTITIES. Простая strtr по таблице не повторит все проверки повреждённого UTF-8, double_encode и флагов настоящего кодировщика. Обратный array_flip также не заменяет декодер: один символ может иметь несколько сущностей.
Не заменяйте htmlspecialchars ручной strtr по этой таблице: потеряются её проверки повреждённых последовательностей, double_encode и другие правила. Не переворачивайте массив для полного декодера: один символ может иметь несколько разных сущностей. Полные флаги, их приоритет, кодировки и правила повреждённых данных.
Сопоставьте: htmlspecialchars, htmlentities, html_entity_decode. Байты, символы и выбор операции.
strip_tagsИз собственного короткого HTML-фрагмента нужно получить черновой текст без тегов, например для внутренней подписи. strip_tags удаляет теги, но не понимает смысл абзацев и не является очистителем безопасного HTML. Разница видна непосредственно в результате.
strip_tags(string $string, array|string|null $allowed_tags = null): string| Часть договора | Что передать и что получить |
|---|---|
| string | Строка с HTML/PHP-подобной разметкой и возможными NUL-байтами. |
| allowed_tags | null — убрать все теги; строка вида <p><br> либо с PHP 7.4 массив ["p","br"]. Указывайте br, не <br/>: такой допуск охватывает и самозакрывающуюся запись. |
| Результат | Строка без NUL и удалённых тегов. Текст внутри тега обычно сохраняется. Комментарии HTML и PHP-теги удаляются независимо от allowed_tags. |
| Ошибки и границы | Не проверяет корректность HTML. Оборванные теги могут удалить больше текста; имена тегов длиннее 1023 байтов рассматриваются как некорректные. Разрешённые теги сохраняют атрибуты без очистки. |
| Версия / расширение | PHP 4+; allowed_tags array — PHP 7.4; null — PHP 8.0+; standard |
- <p>Кисть</p><p>Валик</p> — два соседних абзаца в HTML.
- strip_tags убирает обе пары тегов, не вставляя между ними пробелы.
- Получается КистьВалик. Для полноценного преобразования HTML в текст нужен разбор структуры.
<?php
$html = '<strong>Кисть</strong> — 8 шт.';
$text = strip_tags($html);
echo $text, "\n";
echo strip_tags('<p>Кисть</p><p>Валик</p>'), "\n";
Ожидаемый результат
Кисть — 8 шт.
КистьВалик
В первом случае пробелы уже находились в тексте. Во втором абзацы разделялись только тегами, поэтому слова склеились. Не добавляйте поверх этого случайные замены для произвольной страницы: текстовый экспорт должен учитывать структуру документа.
<?php
$ownHtml = '<strong>Кисть & валик</strong><!-- служебная заметка -->';
$withoutTags = strip_tags($ownHtml);
$plainText = html_entity_decode($withoutTags, ENT_QUOTES | ENT_HTML5, 'UTF-8');
echo 'После удаления тегов: ', $withoutTags, "\n";
echo 'Обычный текст: ', $plainText, "\n";
Ожидаемый результат
После удаления тегов: Кисть & валик
Обычный текст: Кисть & валик
Это преобразование собственного известного фрагмента в обычный текст, например для текстового отчёта. Если полученный текст затем снова помещают в HTML, его заново экранируют для этого контекста. Декодирование может восстановить угловые скобки и поэтому не делает строку безопасной разметкой.
<?php
$html = '<b class="stock" data-note="internal">Кисть</b><i> 8 шт.</i>';
echo strip_tags($html, ['b']), "\n";
Ожидаемый результат
<b class="stock" data-note="internal">Кисть</b> 8 шт.
Массив allowed_tags оставляет тег b целиком, включая атрибуты. Так же могли бы сохраниться опасные атрибуты событий: список тегов не проверяет атрибуты и URL. Если разрешаете пользователю форматированный HTML, нужен специализированный санитайзер; если нужен буквальный текст — htmlspecialchars. Показанный результат намеренно выводится как исходный HTML, а не как обещание безопасности.
PHP прямо запрещает считать strip_tags защитой от XSS. Если нужен разрешённый пользовательский HTML, нужен специализированный санитайзер с разбором дерева, тегов, атрибутов и URL; allowed_tags таким санитайзером не является. Не используйте регулярное выражение как универсальную замену HTML-парсеру.
Сопоставьте: htmlspecialchars, html_entity_decode, nl2br. Байты, символы и выбор операции.
setlocaleВ приложении число 12.5 должно превратиться в строку amount=12.50 для старого текстового протокола. Некоторые операции форматирования учитывают локаль процесса и могут выбрать иной разделитель. setlocale меняет системные правила выбранной категории; это не перевод текста и не перекодирование строк.
setlocale(int $category, ?string $locales, string ...$rest): string|false; setlocale(int $category, array $locale_array): string|false| Часть договора | Что передать и что получить |
|---|---|
| category | Именованная LC_* категория; полный список в разделе локалей. |
| locales | Имя доступной локали; строка "0" только читает текущую. ""/null выбирают окружение категорий/LANG; Windows с пустой строкой использует региональные настройки системы. |
| rest / locale_array | Альтернативы, проверяемые по порядку до первого успеха. Массивная альтернативная сигнатура не поддерживает именованные аргументы. |
| Результат | Системная строка установленной/прочитанной локали либо false при отсутствии поддержки/имени. Формат строки зависит от ОС. |
| Ошибки и границы | Неверная категория даёт предупреждение и false. В PHP 8.5 специальная поддержка целого 0 убрана: передавайте строку "0"; в строгом вызове integer 0 даёт TypeError. Не рассчитывайте на слабое приведение типа. |
| Версия / расширение | PHP 4+; standard |
- LC_NUMERIC означает только правила числового оформления.
- Локаль C задаёт точку как дробный разделитель.
- sprintf с %f получает строку amount=12.50; прежнюю локаль затем возвращают.
<?php
$before = setlocale(LC_NUMERIC, '0');
$chosen = setlocale(LC_NUMERIC, 'C');
echo 'Выбрана локаль: ', $chosen, "\n";
echo sprintf('amount=%.2f', 12.5), "\n";
if ($before !== false) {
setlocale(LC_NUMERIC, $before);
}
Ожидаемый результат
Выбрана локаль: C
amount=12.50
Строка "0" запрашивает текущее имя без изменения. C — базовая доступная локаль, LC_NUMERIC ограничивает область изменения. В долгоживущем обработчике восстановление при ошибке также обязательно; следующий пример использует finally. Для фиксированного протокола часто проще сразу выбрать независимый от локали %F.
<?php
$before = setlocale(LC_NUMERIC, '0');
if ($before === false || setlocale(LC_NUMERIC, 'C') === false) {
throw new RuntimeException('Не удалось выбрать числовые правила');
}
try {
echo sprintf('amount=%.2f', 12.5), "\n";
} finally {
setlocale(LC_NUMERIC, $before);
}
echo 'Без изменения локали: ', sprintf('amount=%.2F', 12.5), "\n";
Ожидаемый результат
amount=12.50
Без изменения локали: amount=12.50
finally выполняется и при исключении внутри try, поэтому соседний код не получает чужую настройку. Заглавная F создаёт дробную точку независимо от LC_NUMERIC; для этого простого протокола она избавляет от изменения общего окружения. На реальной операции сначала выбирают формат данных, а не глобально ставят LC_ALL.
<?php
declare(strict_types=1);
$before = setlocale(LC_COLLATE, '0');
$chosen = setlocale(LC_COLLATE, ['php_manual_missing_locale_41', 'C']);
echo 'Доступная альтернатива: ', $chosen, "\n";
if ($before !== false) {
setlocale(LC_COLLATE, $before);
}
try {
setlocale(LC_COLLATE, 0);
} catch (TypeError) {
echo "Для чтения нужна строка 0, а не целое число\n";
}
Ожидаемый результат
Доступная альтернатива: C
Для чтения нужна строка 0, а не целое число
Имена проверяются по порядку: выдуманной локали нет, C доступна. Если не подошло ни одно имя, возвращается false — нельзя продолжать так, будто нужный язык установлен. Последний вызов намеренно показывает правило PHP 8.5 в строгом режиме; в обычном коде пишут "0". Имена локалей и их наличие различаются между ОС.
Имена Linux/macOS и Windows различаются: наличие ru_RU.UTF-8 или Russian_Russia.1251 нельзя предполагать. В многопоточном SAPI локаль обычно общая для процесса; Windows хранит её на поток с PHP 7.0.5. Даже сохранение/восстановление не устраняет гонку другой нити. Объектные ICU-инструменты принимают локаль явно. Категории и поля.
Сопоставьте: localeconv, strcoll, nl_langinfo, number_format. Байты, символы и выбор операции.
localeconvНужно узнать, какие разделители сейчас выбраны системными правилами числа: точка или запятая, есть ли группировка тысяч. localeconv возвращает массив этих сведений. Она сама не форматирует сумму и не определяет валюту по тексту пользователя.
localeconv(): array| Часть договора | Что передать и что получить |
|---|---|
| Результат | Ассоциативный массив обычных и денежных разделителей, знаков, группировки и расположения валюты. |
| Ошибки и границы | Не все денежные поля определены в каждой локали. Пустые значения и CHAR_MAX означают отсутствие сведений; не подставляйте их без проверки в параметры форматирования. |
| Версия / расширение | PHP 4.0.5+; standard |
- Для воспроизводимости выбрана LC_NUMERIC=C.
- localeconv возвращает decimal_point="." и пустой thousands_sep.
- Из этих полей получается 1234.50; отдельно выбранные правила витрины могут дать 1 234,50.
<?php
$before = setlocale(LC_NUMERIC, '0');
setlocale(LC_NUMERIC, 'C');
$rules = localeconv();
echo 'Дробный разделитель: [', $rules['decimal_point'], "]\n";
echo 'Разделитель тысяч: [', $rules['thousands_sep'], "]\n";
if ($before !== false) {
setlocale(LC_NUMERIC, $before);
}
Ожидаемый результат
Дробный разделитель: [.]
Разделитель тысяч: []
Ключи массива называют свойства результата. Скобки показывают, что thousands_sep действительно пуст, а не потерялся в выводе. Денежные поля относятся к LC_MONETARY и могут быть не определены в C; их нельзя автоматически считать готовой настройкой валюты.
<?php
$before = setlocale(LC_NUMERIC, '0');
setlocale(LC_NUMERIC, 'C');
try {
$rules = localeconv();
echo 'Правила C: ', number_format(1234.5, 2, $rules['decimal_point'], $rules['thousands_sep']), "\n";
echo 'Подпись витрины: ', number_format(1234.5, 2, ',', ' '), " руб.\n";
} finally {
if ($before !== false) {
setlocale(LC_NUMERIC, $before);
}
}
Ожидаемый результат
Правила C: 1234.50
Подпись витрины: 1 234,50 руб.
Это оформление уже известного числа; исходное значение не изменилось. number_format умеет выбранные разделители, но не реализует всю системную группировку и положение валюты. Для полноценной локализованной валюты используйте NumberFormatter. Правило витрины не должно случайно зависеть от настроек сервера.
Для полной валютной локализации не собирайте сумму вручную по нескольким случайным полям: используйте NumberFormatter. Все 18 полей, grouping и значения sign_posn.
Сопоставьте: setlocale, number_format, nl_langinfo. Байты, символы и выбор операции.
strcollНужно расположить текстовые подписи по правилам выбранной системной локали. strcoll сравнивает две строки по этим правилам. Её результат используют для порядка списка; точное равенство идентификаторов и секретов проверяют другими средствами.
strcoll(string $string1, string $string2): int| Часть договора | Что передать и что получить |
|---|---|
| string1 | Первый текст в кодировке выбранной LC_COLLATE. |
| string2 | Второй текст в том же договоре кодировки. |
| Результат | Знак порядка: <0, 0, >0. В локали C/POSIX для обычного текста соответствует strcmp; регистр значим. |
| Ошибки и границы | Не binary-safe: NUL может прекратить системное сравнение. Результат для национальных букв зависит от установленных локалей, ОС и библиотек. |
| Версия / расширение | PHP 4.0.5+; standard |
- Сравниваются bolt и washer в локали C.
- Отрицательный результат означает: bolt стоит раньше washer.
- При сортировке список washer, bolt, clamp превращается в bolt, clamp, washer.
<?php
$before = setlocale(LC_COLLATE, '0');
setlocale(LC_COLLATE, 'C');
$comparison = strcoll('bolt', 'washer');
if ($comparison < 0) {
echo "bolt располагается раньше washer\n";
}
echo strcoll('bolt', 'bolt') === 0 ? "Одинаковый порядок для bolt и bolt\n" : "Порядок различается\n";
if ($before !== false) {
setlocale(LC_COLLATE, $before);
}
Ожидаемый результат
bolt располагается раньше washer
Одинаковый порядок для bolt и bolt
Важно, меньше ли число нуля, равно или больше; конкретное значение -1 не обещается. C выбрана ради воспроизводимости и не означает естественный алфавитный порядок любого языка. Для русского Unicode-каталога удобнее Collator с явно заданной локалью.
<?php
$labels = ['washer', 'bolt', 'clamp'];
$before = setlocale(LC_COLLATE, '0');
setlocale(LC_COLLATE, 'C');
try {
usort($labels, 'strcoll');
echo implode(', ', $labels), "\n";
} finally {
if ($before !== false) {
setlocale(LC_COLLATE, $before);
}
}
Ожидаемый результат
bolt, clamp, washer
usort вызывает переданный компаратор много раз с парами элементов. Строка "strcoll" здесь является именем вызываемой функции. usort меняет массив и перенумеровывает числовые ключи; если ключи связывают подпись с товаром, изучите uasort. Во время сортировки нельзя менять локаль.
<?php
$first = "BR\0A";
$second = "BR\0Z";
$before = setlocale(LC_COLLATE, '0');
setlocale(LC_COLLATE, 'C');
echo strcoll($first, $second) === 0 ? "strcoll не заметила различие после NUL\n" : "strcoll различила строки\n";
echo strcmp($first, $second) === 0 ? "Байты одинаковы\n" : "strcmp различила полные строки\n";
if ($before !== false) {
setlocale(LC_COLLATE, $before);
}
Ожидаемый результат
strcoll не заметила различие после NUL
strcmp различила полные строки
strcoll не binary-safe: NUL ограничивает строку для этой системной операции. Идентификаторы могут различаться после него, поэтому сортировка для человека не должна задавать правило авторизации или равенства ключей. Для точного равенства обычных строк подходит ===, для чувствительного сравнения хешей — hash_equals.
Для Unicode-каталога обычно удобнее Collator с явной локалью и настройками. strcoll не является case-insensitive компаратором и не угадывает кодировку. При setlocale=false нельзя выдавать C-порядок за требуемый языковой.
Сопоставьте: setlocale, strcmp, strcasecmp, strnatcmp. Байты, символы и выбор операции.
nl_langinfoПрочитать один системный элемент локали, например десятичный разделитель, на платформе с этой функцией.
nl_langinfo(int $item): string|false| Часть договора | Что передать и что получить |
|---|---|
| item | Именованная константа из доступной группы LC_TIME/LC_MONETARY/LC_NUMERIC/LC_MESSAGES/LC_CTYPE; список дан в разделе локалей. |
| Результат | Строка элемента либо false при неподходящем item. Даже константа может отсутствовать на данной платформе/для данной локали. |
| Ошибки и границы | В Windows функции нет: прямой вызов даст Error. Сначала function_exists, затем defined для выбранной константы. Отсутствие не исправляется передачей выдуманного числового кода. |
| Версия / расширение | PHP 4.1+; не реализована в Windows; standard |
- Запрос касается RADIXCHAR — десятичного разделителя.
- Windows не предоставляет nl_langinfo → выбирается localeconv.
- Оба пути при LC_NUMERIC=C должны дать точку; в этом проекте выполнен путь Windows.
<?php
if (!function_exists('nl_langinfo')) {
echo "В Windows используйте localeconv для числовых правил\n";
} else {
echo "nl_langinfo доступна: проверяйте нужную константу\n";
}
Ожидаемый результат
В Windows используйте localeconv для числовых правил
Точный ожидаемый вывод относится к проверенной Windows-сборке PHP 8.5.11. На Unix будет другая ветка; она не объявляется выполненной этим запуском.
<?php
$old = setlocale(LC_NUMERIC, '0'); setlocale(LC_NUMERIC, 'C');
$point = false;
if (function_exists('nl_langinfo') && defined('RADIXCHAR')) {
$point = nl_langinfo(constant('RADIXCHAR'));
}
if ($point === false) { $point = localeconv()['decimal_point']; }
echo $point, "\n";
if ($old !== false) { setlocale(LC_NUMERIC, $old); }
Ожидаемый результат
.
Короткое замыкание && не обращается к отсутствующей константе. В Windows проверен fallback, а не сама Unix-функция. Это ограничение доказательства сохранено в реестре примеров.
Не все возвращаемые строки предназначены для прямого вывода; отдельные системные поля кодируются байтами. CODESET сообщает настройку локали, а не определяет байты входного файла. Полный перечень item.
Сопоставьте: setlocale, localeconv. Байты, символы и выбор операции.
utf8_encodeВ старом импорте имя Café хранится в ISO-8859-1. Чтобы показать его на UTF-8-странице, нужны другие байты. utf8_encode исторически выполняла именно это одно преобразование. Несмотря на имя, она не умеет определять любую исходную кодировку; с PHP 8.2 функция устарела.
utf8_encode(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Байты ISO-8859-1 (Latin-1), а не произвольный текст. |
| Результат | UTF-8 из каждого байта Latin-1; неверное предположение об исходной кодировке даёт неверный текст без ошибки. |
| Ошибки и границы | Deprecated с PHP 8.2 (E_DEPRECATED), планируется удаление в будущей версии. С PHP 7.2 перенесена из XML в core. Это не средство «починить UTF-8». |
| Версия / расширение | PHP 4+; deprecated PHP 8.2; standard |
- В Latin-1 буква é записана одним байтом E9.
- Преобразование ISO-8859-1 → UTF-8 заменяет E9 двумя байтами C3 A9.
- Читатель видит Café. Windows-1251 и Windows-1252 требуют других явно заданных исходных кодировок.
<?php
$legacy = "Caf\xE9";
$text = mb_convert_encoding($legacy, 'UTF-8', 'ISO-8859-1');
echo 'Название: ', $text, "\n";
echo 'До: ', bin2hex($legacy), "\n";
echo 'После: ', bin2hex($text), "\n";
Ожидаемый результат
Название: Café
До: 436166e9
После: 436166c3a9
\xE9 в двойных кавычках задаёт один конкретный байт, поэтому редактор не перекодирует учебный источник незаметно. mb_convert_encoding получает сначала целевую, затем исходную кодировку. Слово осталось тем же, изменилась его байтовая запись. Требуется mbstring.
<?php
$deprecated = false;
set_error_handler(function (int $level) use (&$deprecated): bool {
if ($level === E_DEPRECATED) {
$deprecated = true;
return true;
}
return false;
});
try {
$text = utf8_encode("Caf\xE9");
} finally {
restore_error_handler();
}
echo 'Название: ', $text, "\n";
echo $deprecated ? "Нужно заменить устаревший вызов\n" : "Предупреждения нет\n";
Ожидаемый результат
Название: Café
Нужно заменить устаревший вызов
Собственно старое преобразование — одна строка $text = utf8_encode(...). Обработчик вокруг нужен только для явного наблюдения E_DEPRECATED в PHP 8.5 и затем снимается. В приложении заменяют вызов, а не скрывают предупреждение. Не пропускайте через utf8_encode текст, который уже является UTF-8: он будет испорчен.
<?php
$euroByte = "\x80";
echo 'Windows-1252: ', mb_convert_encoding($euroByte, 'UTF-8', 'Windows-1252'), "\n";
$latin1 = mb_convert_encoding($euroByte, 'UTF-8', 'ISO-8859-1');
echo 'Latin-1 даёт байты: ', bin2hex($latin1), "\n";
Ожидаемый результат
Windows-1252: €
Latin-1 даёт байты: c280
В Windows-1252 байт 80 означает евро, а в Latin-1 — управляющую кодовую точку U+0080. Её показываем в HEX, поскольку это не печатный знак. Кодировку узнают из договора поставщика, заголовка или достоверной настройки источника, а не из предположения «функция с UTF-8 в имени всё исправит».
ISO-8859-1 и Windows-1252 не тождественны: браузеры исторически могут трактовать метку Latin-1 как Windows-1252, но эти функции так не делают. Альтернативы — mb_convert_encoding, iconv и UConverter::transcode. Для воспроизведения замены ? в UConverter задают to_subst, для строгого экспорта лучше явно отклонять потери.
Сопоставьте: utf8_decode, bin2hex. Байты, символы и выбор операции.
utf8_decodeСтарый получатель умеет только ISO-8859-1, а сайт хранит UTF-8. utf8_decode исторически переводила текст в этот узкий набор. Символов русского языка и евро там нет: они теряются и превращаются в ?. Функция устарела с PHP 8.2; экспорт должен явно решать, допустимы ли потери.
utf8_decode(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Байты UTF-8 для преобразования только в ISO-8859-1. |
| Результат | Latin-1; некорректный UTF-8 и кодовые точки выше U+00FF заменяются вопросительным знаком ?. Данные могут быть потеряны. |
| Ошибки и границы | Deprecated с PHP 8.2 (E_DEPRECATED), планируется удаление в будущей версии. С PHP 7.2 перенесена из XML в core. Это не средство «починить UTF-8». |
| Версия / расширение | PHP 4+; deprecated PHP 8.2; standard |
- Café € Я — текст UTF-8.
- Latin-1 умеет é, но не € и Я.
- После старого преобразования остаётся Café ? ?. Восстановить потерянные знаки невозможно.
<?php
$text = 'Café';
$bytes = mb_convert_encoding($text, 'ISO-8859-1', 'UTF-8');
echo 'Байты для старого получателя: ', bin2hex($bytes), "\n";
echo 'Проверочный показ: ', mb_convert_encoding($bytes, 'UTF-8', 'ISO-8859-1'), "\n";
Ожидаемый результат
Байты для старого получателя: 436166e9
Проверочный показ: Café
Latin-1 байты нельзя просто вывести на UTF-8-страницу: они будут прочитаны в другой кодировке. Поэтому отправляемое представление показано как HEX, а для понятной подписи выполняется обратное преобразование. Этот конкретный текст полностью представим.
<?php
$oldSubstitute = mb_substitute_character();
mb_substitute_character(0x3F);
try {
foreach (['Café', 'Кисть €'] as $text) {
$bytes = mb_convert_encoding($text, 'ISO-8859-1', 'UTF-8');
$roundTrip = mb_convert_encoding($bytes, 'UTF-8', 'ISO-8859-1');
if ($roundTrip !== $text) {
echo 'Не экспортируем: ', $text, " потеряет символы\n";
} else {
echo 'Можно экспортировать: ', $text, "\n";
}
}
} finally {
mb_substitute_character($oldSubstitute);
}
Ожидаемый результат
Можно экспортировать: Café
Не экспортируем: Кисть € потеряет символы
Правило этого экспорта — никаких потерь. 0x3F задаёт вопросительный знак как замену для диагностического преобразования; настройка затем восстанавливается. Обратное преобразование сравнивается с исходным текстом. Перед этим внешний вход отдельно проверяют mb_check_encoding на корректность UTF-8; пример использует известные строки.
<?php
set_error_handler(function (int $level): bool {
return $level === E_DEPRECATED;
});
try {
$bytes = utf8_decode('Café € Я');
} finally {
restore_error_handler();
}
echo 'Байты: ', bin2hex($bytes), "\n";
echo 'Что осталось: ', mb_convert_encoding($bytes, 'UTF-8', 'ISO-8859-1'), "\n";
Ожидаемый результат
Байты: 436166e9203f203f
Что осталось: Café ? ?
Старый вызов намеренно показан для чтения существующего кода, предупреждение E_DEPRECATED локально перехвачено. Знаки евро и Я уже заменены, поэтому последующее преобразование в UTF-8 не может их восстановить. Новое приложение должно отвергать такую потерю либо иметь явно согласованное правило замены.
ISO-8859-1 и Windows-1252 не тождественны: браузеры исторически могут трактовать метку Latin-1 как Windows-1252, но эти функции так не делают. Альтернативы — mb_convert_encoding, iconv и UConverter::transcode. Для воспроизведения замены ? в UConverter задают to_subst, для строгого экспорта лучше явно отклонять потери.
Сопоставьте: utf8_encode, bin2hex. Байты, символы и выбор операции.
convert_cyr_stringПеренести старый кириллический импорт на явные имена кодировок в PHP 8.
Исторически: convert_cyr_string(string $str, string $from, string $to): string| Часть договора | Что передать и что получить |
|---|---|
| str | Байты старой однобайтовой кириллической кодировки. |
| from / to | Старые однобуквенные имена: k=KOI8-R; w=Windows-1251; i=ISO-8859-5; a/d=CP866; m=MacCyrillic. UTF-8 среди них нет. |
| Результат | Исторически возвращала преобразованные байты; в PHP 8 самой функции нет. |
| Ошибки и границы | Deprecated с PHP 7.4, удалена в PHP 8.0; прямой вызов в текущем PHP — Error. Сопоставьте оба имени кодировки перед заменой. |
| Версия / расширение | PHP 4…7; deprecated 7.4, removed 8.0; standard |
- C1 EE EB F2 → слово Болт в Windows-1251.
- Явные Windows-1251 → UTF-8 заменяют старые однобуквенные коды.
- Результат можно показать как Болт в UTF-8.
<?php
$legacy = "\xC1\xEE\xEB\xF2";
echo mb_convert_encoding($legacy, 'UTF-8', 'Windows-1251'), "\n";
var_dump(function_exists('convert_cyr_string'));
Ожидаемый результат
Болт
bool(false)
Проверяется современная миграция с mbstring. Исходные байты записаны hex, чтобы редактор файла не перекодировал их незаметно; удалённая функция не вызывается.
<?php
$dos = "\x81\xAE\xAB\xE2";
$text = mb_convert_encoding($dos, 'UTF-8', 'CP866');
echo $text, "\n";
echo bin2hex(mb_convert_encoding($text, 'Windows-1251', 'UTF-8')), "\n";
Ожидаемый результат
Болт
c1eeebf2
Сначала подтверждается кодировка источника CP866, затем выбирается другой целевой формат. Подстановка имени UTF-8 в старый convert_cyr_string невозможна; современный конвертер имеет иной договор.
Не применяйте перекодирование к уже корректному UTF-8: получится повреждение. Для неизвестного источника запросите спецификацию или сохраните исходные байты для диагностики; автоматическое определение не доказывает правильность. iconv — другая допустимая альтернатива при проверенной поддержке конкретного имени.
Сопоставьте: utf8_encode, utf8_decode, bin2hex. Байты, символы и выбор операции.
hebrevВ старых системах с однобайтовым ивритом буквы могли переставлять перед показом, потому что устройство ожидало визуальный порядок. Для такого наследованного формата существует hebrev. Современный браузер сам обрабатывает направление Unicode-текста: ниже показаны оба случая и их разные результаты.
hebrev(string $string, int $max_chars_per_line = 0): string| Часть договора | Что передать и что получить |
|---|---|
| string | Исторический однобайтовый текст иврита; реализация обрабатывает байты, а не полноценный Unicode BiDi. |
| max_chars_per_line | 0 — без ограничения; положительная длина задаёт перенос с попыткой не разрывать слова. Это не ширина в пикселях и не размер графем. |
| Результат | Строка визуального порядка. Для современного UTF-8-HTML обычно нужно сохранить логический порядок и задать dir, а не вызывать эту функцию. |
| Ошибки и границы | Передача UTF-8 не превращает её в Unicode-алгоритм. Неверный тип в строгом вызове — TypeError; не используйте отрицательные пределы как осмысленное правило переноса. |
| Версия / расширение | PHP 4+; standard |
- E0 E1 E2 — три исторических байта букв иврита в выбранной однобайтовой кодировке.
- hebrev меняет их порядок на E2 E1 E0.
- Для новой UTF-8-страницы байты не переставляют: HTML dir="rtl" задаёт направление отображения.
<?php
$logical = "\xE0\xE1\xE2";
$visual = hebrev($logical);
echo 'До: ', bin2hex($logical), "\n";
echo 'После: ', bin2hex($visual), "\n";
echo 'Порядок букв после преобразования: ', mb_convert_encoding($visual, 'UTF-8', 'ISO-8859-8'), "\n";
Ожидаемый результат
До: e0e1e2
После: e2e1e0
Порядок букв после преобразования: גבא
HEX показывает порядок хранения независимо от двунаправленного отображения терминала. Перекодирование последней строки нужно только для просмотра букв в UTF-8. Передача самой UTF-8-строки в hebrev не делает функцию Unicode-алгоритмом.
<?php
$text = 'שלום';
echo '<p lang="he" dir="rtl">', htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), "</p>\n";
Ожидаемый результат
<p lang="he" dir="rtl">שלום</p>
Видимый результат ниже — приветствие на иврите. lang указывает язык, dir задаёт направление, htmlspecialchars защищает текстовый контекст. Строка хранится в логическом порядке и не проходит hebrev. Для сложных смешанных подписей отдельно рассматривают bdi и изоляцию направления.
שלום
<?php
echo "hebrev:\n", hebrev('ABC DEF GHI', 5), "\n";
echo "wordwrap:\n", wordwrap('ABC DEF GHI', 5), "\n";
Ожидаемый результат
hebrev:
GHI
DEF
ABC
wordwrap:
ABC
DEF
GHI
Даже английские слова показывают отличие: hebrev меняет порядок получившихся строк. Аргумент max_chars_per_line не означает пиксельную ширину HTML-блока. Для обычного переноса текста не выбирайте функцию перестановки иврита.
hebrev не заменяет Unicode Bidirectional Algorithm. Поддержка старого формата требует знания его кодировки; новый веб-текст храните в логическом порядке. https://github.com/php/php-src/blob/PHP-8.5/ext/standard/string.c; правила dir описаны W3C Internationalization.
Сопоставьте: hebrevc, wordwrap, htmlspecialchars. Байты, символы и выбор операции.
hebrevcПосле перехода старого сайта на PHP 8 вызов hebrevc перестал существовать. Исторически он сочетал перестановку иврита с HTML-переносами. Для нового UTF-8-текста нужно сохранить логический порядок, экранировать данные, добавить переносы и задать направление в HTML.
Исторически: hebrevc(string $hebrew_text, int $max_chars_per_line = 0): string| Часть договора | Что передать и что получить |
|---|---|
| hebrew_text | Текст для исторического hebrev. |
| max_chars_per_line | Как у hebrev: 0 без предела, положительное значение ограничивает строки с попыткой сохранить слова. |
| Результат | Исторически визуальный текст с заменой LF на <br> и LF. В PHP 8 функция отсутствует. |
| Ошибки и границы | Deprecated PHP 7.4, removed PHP 8.0. Механическая замена на nl2br(hebrev(...)) не решает ошибочный выбор старого алгоритма для современного UTF-8. |
| Версия / расширение | PHP 4…7; deprecated 7.4, removed 8.0; standard |
- Две исходные строки שלום и עולם остаются в UTF-8.
- htmlspecialchars подготавливает текст, nl2br добавляет <br> на месте перевода строки.
- Браузер показывает две строки справа налево благодаря dir="rtl".
<?php
$text = "שלום\nעולם";
$safe = htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
echo '<div lang="he" dir="rtl">', nl2br($safe, false), "</div>\n";
Ожидаемый результат
<div lang="he" dir="rtl">שלום<br>
עולם</div>
Две строки означают «привет» и «мир». false просит nl2br создать HTML-тег <br>. Исходный перевод строки тоже остаётся. Сначала защищается текст, затем добавляются авторские теги; обратный порядок превратил бы <br> в буквальные символы.
עולם
<?php
if (!function_exists('hebrevc')) {
echo "В PHP 8 hebrevc удалена. Нужно обновить старый шаблон.\n";
}
Ожидаемый результат
В PHP 8 hebrevc удалена. Нужно обновить старый шаблон.
Проверка объясняет состояние среды, но не заменяет миграцию. Не создавайте автоматически функцию-заглушку с тем же именем: для старого однобайтового визуального формата и нового логического Unicode нужны разные решения. Исторические параметры сохранены в таблице карточки.
Удалённое имя остаётся в справочнике для миграции. Для нового сайта обычно достаточно логического Unicode, dir и экранирования; для сложного смешанного текста отдельно изучают bdi/изоляцию направления. Нельзя объявлять визуальный и логический порядок взаимозаменяемыми данными.
Сопоставьте: hebrev, nl2br, htmlspecialchars. Байты, символы и выбор операции.
money_formatПеренести старое валютное оформление на доступный PHP 8 инструмент с явно указанной локалью и валютой.
Исторически: money_format(string $format, float $number): string| Часть договора | Что передать и что получить |
|---|---|
| format | Старый формат strfmon, не printf; подробная грамматика приведена после примеров. |
| number | Одно числовое значение. Настройка LC_MONETARY задавала валютное оформление. |
| Результат | Исторически строка с валютой; недопустимое нечисловое значение могло дать null и E_WARNING. В PHP 8 функция удалена. |
| Ошибки и границы | Deprecated PHP 7.4, removed PHP 8.0. Даже до удаления отсутствовала на системах без strfmon, в частности Windows. Замена — NumberFormatter::formatCurrency из intl. |
| Версия / расширение | PHP 4.3…7; deprecated 7.4, removed 8.0; standard |
- 12.5 → сумма; en_US → правила отображения.
- NumberFormatter::CURRENCY создаёт форматтер валюты.
- formatCurrency с USD возвращает $12.50; код валюты не угадывается по локали.
<?php
$formatter = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
$label = $formatter->formatCurrency(12.5, 'USD');
if ($label === false) { throw new RuntimeException($formatter->getErrorMessage()); }
echo $label, "\n";
var_dump(function_exists('money_format'));
Ожидаемый результат
$12.50
bool(false)
new создаёт объект с собственной локалью; -> вызывает его метод. Нужен intl. Точный образец проверен в проектном ICU/PHP; языковые данные ICU могут меняться, а отсутствующая функция подтверждена на PHP 8.5.
<?php
$cents = 1250;
echo number_format($cents / 100, 2, '.', ''), " USD\n";
echo sprintf('%.2F USD', $cents / 100), "\n";
Ожидаемый результат
12.50 USD
12.50 USD
Это два способа выполнить конкретный договор выгрузки 12.50 USD, а не повторить все валютные правила страны. Для локализованного интерфейса нужен форматтер, а точные расчёты лучше хранить отдельно от вывода.
| Историческая часть money_format | Смысл для чтения старого кода |
|---|---|
| % + флаги + width + #n + .p + i/n | Одна конверсия числа; обычный текст до/после сохраняется. |
| =f / ^ | Заполнять байтом f / отключить группировку. |
| + или ( | Знаки по локали (по умолчанию +) либо скобки вокруг отрицательной суммы. |
| ! / - | Скрыть валютный символ / выровнять поле влево. |
| width | Минимальная ширина, по умолчанию 0. |
| #n | Ожидаемое число цифр слева от дробного разделителя: меньше — заполнение, больше — ограничение игнорируется. Группировка выполняется до заполнения и не группирует добавленные байты. |
| .p | Знаки после разделителя; .0 убирает дробную часть и разделитель. Без него правило берётся из локали. Значение округляется для вывода. |
| i / n / % | Международная валюта / национальная валюта / буквальный %. |
Не переносите шаблоны %i/%n в sprintf: там другой язык формата. Исторические width/#n также выравнивали знаки и символы валюты пробелами, поэтому миграцию табличных отчётов проверяют по ожидаемым строкам отдельно. Никакая функция оформления не выбирает валюту бизнес-операции за приложение.
Сопоставьте: number_format, sprintf, localeconv. Байты, символы и выбор операции.
cryptПри переносе старого сайта нужно проверить пароль по уже сохранённой crypt-записи, затем заменить её современным password_hash. Сама crypt лишь вычисляет хеш по заданным параметрам: она не хранит пользователей, не создаёт сессию и не «расшифровывает» пароль.
crypt(#[\SensitiveParameter] string $string, string $salt): string| Часть договора | Что передать и что получить |
|---|---|
| string | Проверяемая строка; не binary-safe. Алгоритм может ограничить значимую длину, например bcrypt — 72 байта. SensitiveParameter скрывает аргумент из поддерживаемого отображения stack trace, но не защищает ваши собственные логи. |
| salt | Обязательна с PHP 8.0. Определяет алгоритм/стоимость; при проверке старой записи можно передать сохранённый хеш с его параметрами. |
| Результат | Строка хеша. При отказе — строка короче 13 символов и отличная от salt, а не bool false. Обратного decrypt не существует. |
| Ошибки и границы | Нельзя считать любую строку успешным хешем. Старые DES/MD5 слишком быстры; SHA-crypt на неограниченном длинном вводе может расходовать CPU квадратично. Для новых паролей PHP рекомендует password_hash, а для проверки — password_verify. |
| Версия / расширение | PHP 4+; standard |
- В старой записи уже есть алгоритм, стоимость, соль и результат.
- crypt повторно вычисляет результат для введённого кандидата и параметров записи.
- hash_equals сравнивает две записи. Совпадение позволяет перейти к дальнейшим проверкам входа.
<?php
$stored = '$2y$04$abcdefghijklmnopqrstuuXw4/nL3XGUEWc/Gd51kTAXgEzpvX6xK';
$candidate = crypt('demo-stock-41', $stored);
if (strlen($candidate) >= 13 && hash_equals($stored, $candidate)) {
echo "Учебный пароль совпал со старой записью\n";
} else {
echo "Пароль не совпал или вычисление завершилось ошибкой\n";
}
Ожидаемый результат
Учебный пароль совпал со старой записью
Открытая учебная строка и фиксированная соль нужны только для воспроизводимого примера. Стоимость 04 намеренно мала для демонстрации и не является настройкой реального сайта. В salt передаётся сохранённая запись целиком: crypt берёт из неё параметры. При отказе crypt возвращает короткую строку, поэтому сначала проверена длина.
<?php
$old = '$2y$04$abcdefghijklmnopqrstuuXw4/nL3XGUEWc/Gd51kTAXgEzpvX6xK';
$entered = 'demo-stock-41';
if (password_verify($entered, $old)) {
$newHash = password_hash($entered, PASSWORD_DEFAULT);
echo "Пароль подтверждён. Новый хеш готов к сохранению.\n";
echo password_verify($entered, $newHash) ? "Новая запись проходит проверку\n" : "Ошибка миграции\n";
}
echo password_verify('wrong-demo', $old) ? "Совпадение\n" : "Неверный пароль отклонён\n";
Ожидаемый результат
Пароль подтверждён. Новый хеш готов к сохранению.
Новая запись проходит проверку
Неверный пароль отклонён
password_verify совместима с поддерживаемыми crypt-хешами, поэтому ручной crypt для такой проверки часто уже не нужен. Новый хеш каждый раз отличается благодаря соли; точная строка здесь не фиксируется. Для реальной миграции запись сохраняют в базе после успеха, учитывают конкурентный вход и применяют password_needs_rehash. Пример не является полной системой авторизации.
<?php
$result = crypt('demo', '*0');
if (strlen($result) < 13) {
echo "Хеш не получен: параметры crypt недопустимы\n";
}
Ожидаемый результат
Хеш не получен: параметры crypt недопустимы
Неверная соль здесь задана намеренно. Проверка $result !== false была бы ошибочной: crypt возвращает строку даже при таком отказе. Не позволяйте внешнему запросу выбирать алгоритм и вычислительную стоимость.
| Исторический вариант | Как его распознать и ограничить |
|---|---|
| CRYPT_STD_DES | Два знака соли из ./0-9A-Za-z; учитываются только первые 8 байтов входа. Неверные знаки соли означают отказ. |
| CRYPT_EXT_DES | _ + 4 знака числа итераций + 4 знака соли. Каждая четвёрка кодирует 24 бита, младший знак первым; алфавит ./0-9A-Za-z задаёт значения 0…63. |
| CRYPT_MD5 | Префикс $1$; историческая соль до 12 знаков вместе с ним. Не путать с обычным md5($text). |
| CRYPT_BLOWFISH | Префикс $2a$/$2x$/$2y$, двузначная стоимость 04…31, $, 22 знака соли из ./0-9A-Za-z. Стоимость — log2 числа итераций. $2x$ связан с исторической слабостью; корректный современный префикс bcrypt — $2y$. Вход обрезается до 72 байтов. |
| CRYPT_SHA256 / CRYPT_SHA512 | Префиксы $5$ / $6$, до 16 знаков соли. rounds=N$ задаёт число раундов: по умолчанию 5000, диапазон 1000…999999999 с ограничением ближайшей границей. Не допускайте произвольную стоимость из внешнего запроса. |
| CRYPT_SALT_LENGTH | Максимальная поддерживаемая длина соли; это не требование самостоятельно генерировать соль именно такой длины. |
Применение hash_equals не делает устаревший алгоритм стойким. Ограничьте допустимый размер кандидата по договору приложения; не разрешайте внешнему вводу выбирать алгоритм/стоимость. Старые описания автоматической соли относятся к PHP до 8.0: в текущем вызове salt обязательна. После успешной миграционной проверки можно сохранять новый password_hash по согласованной процедуре приложения.
Сопоставьте: md5, sha1, crc32. Байты, символы и выбор операции.
md5В старом обмене рядом с текстовым пакетом уже хранится MD5-сумма. Нужно вычислить такую же запись и понять, совпали ли байты. Этот исторический формат бывает нужен для совместимости; для защиты от подделки или хранения паролей он не подходит.
md5(string $string, bool $binary = false): string| Часть договора | Что передать и что получить |
|---|---|
| string | Точные байты сообщения; регистр, кодировка, NUL и перевод строки влияют на результат. |
| binary | false — 32 шестнадцатеричных знаков; true — 16 сырых байтов. |
| Результат | Дайджест фиксированного размера; одинаковые байты дают одинаковый результат. Разные данные могут иметь одинаковый хеш. |
| Ошибки и границы | Не подходит для паролей и защиты от намеренных коллизий. Не сравнивайте текстовые хеши слабым ==: строки, похожие на научную запись числа, могут сравниваться как числа. |
| Версия / расширение | PHP 4+; standard |
- BR41 — четыре конкретных ASCII-байта, без пробела и переноса.
- md5 вычисляет дайджест, который обычно показывается шестнадцатеричной строкой.
- Повтор тех же байтов даёт ту же запись; изменение пакета проверяется до импорта.
<?php
$text = 'BR41';
$checksum = md5($text);
echo 'Текст: ', $text, "
";
echo 'Сумма: ', $checksum, "
";
echo 'Длина записи: ', strlen($checksum), "
";
Ожидаемый результат
Текст: BR41
Сумма: 41264ab83d64ea83d4cc054475134e84
Длина записи: 32
Хеш не является зашифрованным текстом: функции обратного декодирования нет. Регистр, кодировка и перенос строки меняют входные байты. Возможны коллизии — разные данные с одной суммой; фиксированная длина не означает уникальность.
<?php
$expected = '8a63466971fd05efe7d83b0b796d7e78';
$packets = ["BR41;12
", "BR41;13
", 'BR41;12'];
foreach ($packets as $packet) {
if (md5($packet) === $expected) {
echo "Байты соответствуют старому манифесту
";
} else {
echo "Сумма отличается: остановить импорт и проверить источник
";
}
}
Ожидаемый результат
Байты соответствуют старому манифесту
Сумма отличается: остановить импорт и проверить источник
Сумма отличается: остановить импорт и проверить источник
Второй пакет содержит количество 13 вместо 12. Третий лишён завершающего LF, поэтому тоже отличается. Нельзя произвольно trim-ить пакет перед проверкой. Сравнение строгое ===; слабое == опасно для строк, похожих на научную запись числа. Открытая сумма не подтверждает отправителя: злоумышленник может заменить и данные, и сумму.
<?php
$binary = md5('BR41', true);
echo 'Бинарных байтов: ', strlen($binary), "
";
echo 'Для просмотра: ', bin2hex($binary), "
";
Ожидаемый результат
Бинарных байтов: 16
Для просмотра: 41264ab83d64ea83d4cc054475134e84
true во втором аргументе меняет представление результата, а не алгоритм. Бинарную строку не выводят непосредственно в терминал или HTML. Для нового формата выбирают, например, hash("sha256", ...); проверка подлинности требует HMAC или подписи, а пароли — password_hash/password_verify.
Для новых задач целостности без атак используйте подходящий современный hash, например SHA-256. Если противник может менять данные, нужна проверка подлинности (HMAC или подпись с надёжно хранимым ключом), а не просто более длинный открытый хеш. Для паролей — password_hash/password_verify; кодировка должна быть определена до хеширования.
Сопоставьте: md5_file, sha1, crc32. Байты, символы и выбор операции.
md5_fileСтарый поставщик передал файл остатков и MD5-сумму его содержимого. Нужно прочитать локальный файл, вычислить сумму и решить, можно ли продолжать разбор. Файл из примера входит в скачиваемый набор; ниже показаны и его текст, и результат проверки.
md5_file(string $filename, bool $binary = false): string|false| Часть договора | Что передать и что получить |
|---|---|
| filename | Путь к читаемому файлу. В учебных примерах только локальный относительный путь из изолированного набора. Не принимайте произвольный пользовательский URL/путь. |
| binary | false — 32 hex-знаков; true — 16 сырых байтов. |
| Результат | Строка дайджеста при успехе, false при ошибке чтения/открытия. Имя и метаданные файла не входят в хеш содержимого. |
| Ошибки и границы | Неудачное открытие обычно также выдаёт E_WARNING. Сначала строго проверяйте false; не считайте пустой/отсутствующий файл тем же, что успешный дайджест. |
| Версия / расширение | PHP 4.2+; standard |
- stock.txt содержит строку BR41;12 и завершающий LF.
- md5_file читает файл и возвращает контрольную запись или false при ошибке.
- Сначала проверяется успех чтения, затем совпадение со значением манифеста.
Перед запуском сохраните рядом с PHP-файлом файл stock.txt с содержимым ниже. Кодировка UTF-8 без BOM; окончание показанной строки — LF. В скачиваемом наборе примера этот файл уже подготовлен.
BR41;12
<?php
$path = __DIR__ . '/stock.txt';
$expected = '8a63466971fd05efe7d83b0b796d7e78';
$actual = md5_file($path);
if ($actual === false) {
echo "Не удалось прочитать файл остатков
";
} elseif ($actual !== $expected) {
echo "Сумма отличается: файл не импортируем
";
} else {
echo "Файл соответствует старому манифесту
";
echo 'Сумма: ', $actual, "
";
}
Ожидаемый результат
Файл соответствует старому манифесту
Сумма: 8a63466971fd05efe7d83b0b796d7e78
__DIR__ задаёт папку PHP-файла, поэтому запуск из другой рабочей папки не меняет путь. Сначала отличаем false от успешной строки. Если заменить в stock.txt количество 12 на 13, появится сообщение об отличии. CRLF вместо LF также меняет байты. Сам импорт этот пример ещё не выполняет.
Перед запуском сохраните рядом с PHP-файлом файл first.txt с содержимым ниже. Кодировка UTF-8 без BOM; окончание показанной строки — LF. В скачиваемом наборе примера этот файл уже подготовлен.
BR41;12
Перед запуском сохраните рядом с PHP-файлом файл second.txt с содержимым ниже. Кодировка UTF-8 без BOM; окончание показанной строки — LF. В скачиваемом наборе примера этот файл уже подготовлен.
BR41;12
<?php
$first = md5_file(__DIR__ . '/first.txt', true);
$second = md5_file(__DIR__ . '/second.txt', true);
if ($first === false || $second === false) {
throw new RuntimeException('Не удалось прочитать обе копии');
}
echo $first === $second ? "Контрольные суммы копий совпали
" : "Копии отличаются
";
echo 'Размер бинарной суммы: ', strlen($first), "
";
Ожидаемый результат
Контрольные суммы копий совпали
Размер бинарной суммы: 16
Имена first.txt и second.txt в сумму не входят — учитывается содержимое. Сырые дайджесты можно сравнить строго, не печатая управляющие байты. Совпадение старого алгоритма не исключает намеренную коллизию. После проверки файл ещё может измениться: эта операция его не блокирует.
<?php
set_error_handler(function (int $level): bool {
return $level === E_WARNING;
});
try {
$checksum = md5_file(__DIR__ . '/missing-stock.txt');
} finally {
restore_error_handler();
}
if ($checksum === false) {
echo "Файл остатков недоступен. Импорт не начат.
";
}
Ожидаемый результат
Файл остатков недоступен. Импорт не начат.
Здесь файл намеренно отсутствует. Временный обработчик только вокруг демонстрационного вызова позволяет показать понятный результат вместо системного пути в предупреждении. В приложении техническую причину сохраняют в закрытый журнал, а пользователю показывают безопасное сообщение. Отключать все ошибки или считать false контрольной суммой нельзя.
Манифест из того же недоверенного места, что и файл, не подтверждает подлинность. Для новых манифестов используйте современный алгоритм и доверенный канал/подпись. Файл может измениться после чтения: проверка не блокирует его и не делает дальнейшее использование атомарным.
Сопоставьте: md5, sha1_file. Байты, символы и выбор операции.
sha1В старом обмене рядом с текстовым пакетом уже хранится SHA-1-сумма. Нужно вычислить такую же запись и понять, совпали ли байты. Этот исторический формат бывает нужен для совместимости; для защиты от подделки или хранения паролей он не подходит.
sha1(string $string, bool $binary = false): string| Часть договора | Что передать и что получить |
|---|---|
| string | Точные байты сообщения; регистр, кодировка, NUL и перевод строки влияют на результат. |
| binary | false — 40 шестнадцатеричных знаков; true — 20 сырых байтов. |
| Результат | Дайджест фиксированного размера; одинаковые байты дают одинаковый результат. Разные данные могут иметь одинаковый хеш. |
| Ошибки и границы | Не подходит для паролей и защиты от намеренных коллизий. Не сравнивайте текстовые хеши слабым ==: строки, похожие на научную запись числа, могут сравниваться как числа. |
| Версия / расширение | PHP 4.3+; standard |
- BR41 — четыре конкретных ASCII-байта, без пробела и переноса.
- sha1 вычисляет дайджест, который обычно показывается шестнадцатеричной строкой.
- Повтор тех же байтов даёт ту же запись; изменение пакета проверяется до импорта.
<?php
$text = 'BR41';
$checksum = sha1($text);
echo 'Текст: ', $text, "
";
echo 'Сумма: ', $checksum, "
";
echo 'Длина записи: ', strlen($checksum), "
";
Ожидаемый результат
Текст: BR41
Сумма: a75892726a630e4e1ff9a3227331718cded15696
Длина записи: 40
Хеш не является зашифрованным текстом: функции обратного декодирования нет. Регистр, кодировка и перенос строки меняют входные байты. Возможны коллизии — разные данные с одной суммой; фиксированная длина не означает уникальность.
<?php
$expected = '72986c20946285f315532d162b7eed6bd0597870';
$packets = ["BR41;12
", "BR41;13
", 'BR41;12'];
foreach ($packets as $packet) {
if (sha1($packet) === $expected) {
echo "Байты соответствуют старому манифесту
";
} else {
echo "Сумма отличается: остановить импорт и проверить источник
";
}
}
Ожидаемый результат
Байты соответствуют старому манифесту
Сумма отличается: остановить импорт и проверить источник
Сумма отличается: остановить импорт и проверить источник
Второй пакет содержит количество 13 вместо 12. Третий лишён завершающего LF, поэтому тоже отличается. Нельзя произвольно trim-ить пакет перед проверкой. Сравнение строгое ===; слабое == опасно для строк, похожих на научную запись числа. Открытая сумма не подтверждает отправителя: злоумышленник может заменить и данные, и сумму.
<?php
$binary = sha1('BR41', true);
echo 'Бинарных байтов: ', strlen($binary), "
";
echo 'Для просмотра: ', bin2hex($binary), "
";
Ожидаемый результат
Бинарных байтов: 20
Для просмотра: a75892726a630e4e1ff9a3227331718cded15696
true во втором аргументе меняет представление результата, а не алгоритм. Бинарную строку не выводят непосредственно в терминал или HTML. Для нового формата выбирают, например, hash("sha256", ...); проверка подлинности требует HMAC или подписи, а пароли — password_hash/password_verify.
Для новых задач целостности без атак используйте подходящий современный hash, например SHA-256. Если противник может менять данные, нужна проверка подлинности (HMAC или подпись с надёжно хранимым ключом), а не просто более длинный открытый хеш. Для паролей — password_hash/password_verify; кодировка должна быть определена до хеширования.
Сопоставьте: sha1_file, md5, crc32. Байты, символы и выбор операции.
sha1_fileСтарый поставщик передал файл остатков и SHA-1-сумму его содержимого. Нужно прочитать локальный файл, вычислить сумму и решить, можно ли продолжать разбор. Файл из примера входит в скачиваемый набор; ниже показаны и его текст, и результат проверки.
sha1_file(string $filename, bool $binary = false): string|false| Часть договора | Что передать и что получить |
|---|---|
| filename | Путь к читаемому файлу. В учебных примерах только локальный относительный путь из изолированного набора. Не принимайте произвольный пользовательский URL/путь. |
| binary | false — 40 hex-знаков; true — 20 сырых байтов. |
| Результат | Строка дайджеста при успехе, false при ошибке чтения/открытия. Имя и метаданные файла не входят в хеш содержимого. |
| Ошибки и границы | Неудачное открытие обычно также выдаёт E_WARNING. Сначала строго проверяйте false; не считайте пустой/отсутствующий файл тем же, что успешный дайджест. |
| Версия / расширение | PHP 4.3+; standard |
- stock.txt содержит строку BR41;12 и завершающий LF.
- sha1_file читает файл и возвращает контрольную запись или false при ошибке.
- Сначала проверяется успех чтения, затем совпадение со значением манифеста.
Перед запуском сохраните рядом с PHP-файлом файл stock.txt с содержимым ниже. Кодировка UTF-8 без BOM; окончание показанной строки — LF. В скачиваемом наборе примера этот файл уже подготовлен.
BR41;12
<?php
$path = __DIR__ . '/stock.txt';
$expected = '72986c20946285f315532d162b7eed6bd0597870';
$actual = sha1_file($path);
if ($actual === false) {
echo "Не удалось прочитать файл остатков
";
} elseif ($actual !== $expected) {
echo "Сумма отличается: файл не импортируем
";
} else {
echo "Файл соответствует старому манифесту
";
echo 'Сумма: ', $actual, "
";
}
Ожидаемый результат
Файл соответствует старому манифесту
Сумма: 72986c20946285f315532d162b7eed6bd0597870
__DIR__ задаёт папку PHP-файла, поэтому запуск из другой рабочей папки не меняет путь. Сначала отличаем false от успешной строки. Если заменить в stock.txt количество 12 на 13, появится сообщение об отличии. CRLF вместо LF также меняет байты. Сам импорт этот пример ещё не выполняет.
Перед запуском сохраните рядом с PHP-файлом файл first.txt с содержимым ниже. Кодировка UTF-8 без BOM; окончание показанной строки — LF. В скачиваемом наборе примера этот файл уже подготовлен.
BR41;12
Перед запуском сохраните рядом с PHP-файлом файл second.txt с содержимым ниже. Кодировка UTF-8 без BOM; окончание показанной строки — LF. В скачиваемом наборе примера этот файл уже подготовлен.
BR41;12
<?php
$first = sha1_file(__DIR__ . '/first.txt', true);
$second = sha1_file(__DIR__ . '/second.txt', true);
if ($first === false || $second === false) {
throw new RuntimeException('Не удалось прочитать обе копии');
}
echo $first === $second ? "Контрольные суммы копий совпали
" : "Копии отличаются
";
echo 'Размер бинарной суммы: ', strlen($first), "
";
Ожидаемый результат
Контрольные суммы копий совпали
Размер бинарной суммы: 20
Имена first.txt и second.txt в сумму не входят — учитывается содержимое. Сырые дайджесты можно сравнить строго, не печатая управляющие байты. Совпадение старого алгоритма не исключает намеренную коллизию. После проверки файл ещё может измениться: эта операция его не блокирует.
<?php
set_error_handler(function (int $level): bool {
return $level === E_WARNING;
});
try {
$checksum = sha1_file(__DIR__ . '/missing-stock.txt');
} finally {
restore_error_handler();
}
if ($checksum === false) {
echo "Файл остатков недоступен. Импорт не начат.
";
}
Ожидаемый результат
Файл остатков недоступен. Импорт не начат.
Здесь файл намеренно отсутствует. Временный обработчик только вокруг демонстрационного вызова позволяет показать понятный результат вместо системного пути в предупреждении. В приложении техническую причину сохраняют в закрытый журнал, а пользователю показывают безопасное сообщение. Отключать все ошибки или считать false контрольной суммой нельзя.
Манифест из того же недоверенного места, что и файл, не подтверждает подлинность. Для новых манифестов используйте современный алгоритм и доверенный канал/подпись. Файл может измениться после чтения: проверка не блокирует его и не делает дальнейшее использование атомарным.
Сопоставьте: sha1, md5_file. Байты, символы и выбор операции.
crc32Устройство присылает короткий пакет с контрольной суммой CRC32. Она помогает обнаруживать случайное повреждение байтов. Чтобы сравнивать её между платформами, заранее выбирают представление: например, восемь шестнадцатеричных знаков.
crc32(string $string): int| Часть договора | Что передать и что получить |
|---|---|
| string | Точные байты пакета; функция не знает его кодировки или структуры. |
| Результат | 32-битная контрольная сумма в PHP int. На 32-битной платформе высокие значения могут быть отрицательными; на 64-битной они неотрицательны. |
| Ошибки и границы | CRC не является криптографической защитой. Для переносимой десятичной строки используйте sprintf("%u", ...), для hex — %08x либо hash("crc32b", ...). |
| Версия / расширение | PHP 4.0.1+; standard |
- Пакет BR41 состоит из четырёх байтов.
- crc32 возвращает 32-битное число; sprintf оформляет его как 959546b9.
- Пакет BR42 не соответствует этой сумме и должен быть разобран как повреждённый либо иной.
<?php
$packet = 'BR41';
$crc = crc32($packet);
echo 'Пакет: ', $packet, "\n";
echo 'CRC32 в HEX: ', sprintf('%08x', $crc), "\n";
echo 'CRC32 десятичная: ', sprintf('%u', $crc), "\n";
Ожидаемый результат
Пакет: BR41
CRC32 в HEX: 959546b9
CRC32 десятичная: 2509588153
%08x означает шестнадцатеричную запись шириной восемь знаков с нулями слева. %u показывает беззнаковое десятичное представление: на 32-битной платформе сам PHP int может быть отрицательным. На 64-битной платформе это число помещается как положительное.
<?php
$expectedHex = '959546B9';
foreach (['BR41', 'BR42'] as $packet) {
$actualHex = sprintf('%08x', crc32($packet));
if ($actualHex === strtolower($expectedHex)) {
echo $packet, ": контрольная сумма совпала\n";
} else {
echo $packet, ": сумма отличается, запросить пакет повторно\n";
}
}
echo 'Через hash: ', hash('crc32b', 'BR41'), "\n";
Ожидаемый результат
BR41: контрольная сумма совпала
BR42: сумма отличается, запросить пакет повторно
Через hash: 959546b9
Повторная передача здесь показана решением программы, сеть не вызывается. Поле суммы в настоящем протоколе сначала проверяют: ровно восемь ASCII hex-знаков. У hash нужен именно алгоритм crc32b для этого представления. CRC легко пересчитать после намеренного изменения данных; для подлинности отправителя она не подходит.
Совпадение CRC не удостоверяет отправителя и легко подделывается намеренно. Не применяйте для паролей, токенов, цифровых подписей или уникальных ключей. Для бинарной упаковки отдельно согласуйте порядок байтов, а не записывайте PHP int как попало.
Сопоставьте: md5, sha1, sprintf. Байты, символы и выбор операции.
soundexПредложить кандидатов по похожему английскому звучанию, когда пользователь не уверен в написании имени.
soundex(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Английское имя/слово для грубого фонетического ключа; это не универсальная Unicode-транскрипция. |
| Результат | Четыре знака ключа: первая буква при наличии букв, иначе "0000". Пустая строка с PHP 8 тоже даёт "0000", а не прежний false. |
| Ошибки и границы | Не доказывает тождество людей/слов. Цифровая/пустая строка даёт общий 0000, поэтому такой ключ нельзя считать подходящим найденным именем. |
| Версия / расширение | PHP 4+; standard |
- Smith и Smyth → два варианта написания.
- soundex выдаёт для обоих S530.
- Совпадение ключа позволяет показать варианты, но не объединять записи автоматически.
<?php
echo soundex('Smith'), "\n";
echo soundex('Smyth'), "\n";
var_dump(soundex('Smith') === soundex('Smyth'));
Ожидаемый результат
S530
S530
bool(true)
Ключ можно индексировать для предварительного отбора. Список кандидатов затем проверяет человек или более точное правило; это не поиск по точной орфографии.
<?php
$queryKey = soundex('Smith');
$names = ['Smith', 'Smyth', 'Jones'];
foreach ($names as $candidate) {
if (soundex($candidate) === $queryKey) { echo $candidate, "\n"; }
}
Ожидаемый результат
Smith
Smyth
Ввод в этом примере заранее непустой и английский. Реальный поиск ограничивает длину, проверяет алфавит/язык и показывает кандидатов вместо скрытого исправления имени.
Soundex использует вариант алгоритма, описанный Кнутом; одинаковый ключ встречается у разных имён. Для русских имён не обещайте качество этого алгоритма. Доступ к учётной записи, слияние клиентов и финансовые операции требуют точного идентификатора, а не фонетического сходства.
Сопоставьте: metaphone, levenshtein, similar_text. Байты, символы и выбор операции.
metaphoneПредложить кандидатов по похожему английскому звучанию, когда пользователь не уверен в написании имени.
metaphone(string $string, int $max_phonemes = 0): string| Часть договора | Что передать и что получить |
|---|---|
| string | Английское имя/слово для грубого фонетического ключа; это не универсальная Unicode-транскрипция. |
| max_phonemes | 0 — без ограничения. Положительное значение ограничивает ключ, но целая фонема может сделать результат немного длиннее. |
| Результат | Ключ переменной длины по правилам английского произношения; одинаковый ключ означает только кандидата на сходство. |
| Ошибки и границы | Не доказывает тождество людей/слов. Отрицательный max_phonemes в PHP 8 вызывает ValueError; прежнее поведение ошибок возвращало false. |
| Версия / расширение | PHP 4+; standard |
- Steven и Stephen → два варианта написания.
- metaphone выдаёт для обоих STFN.
- Совпадение ключа позволяет показать варианты, но не объединять записи автоматически.
<?php
echo metaphone('Steven'), "\n";
echo metaphone('Stephen'), "\n";
var_dump(metaphone('Steven') === metaphone('Stephen'));
Ожидаемый результат
STFN
STFN
bool(true)
Ключ можно индексировать для предварительного отбора. Список кандидатов затем проверяет человек или более точное правило; это не поиск по точной орфографии.
<?php
$queryKey = metaphone('Steven');
$names = ['Steven', 'Stephen', 'Jones'];
foreach ($names as $candidate) {
if (metaphone($candidate) === $queryKey) { echo $candidate, "\n"; }
}
Ожидаемый результат
Steven
Stephen
Ввод в этом примере заранее непустой и английский. Реальный поиск ограничивает длину, проверяет алфавит/язык и показывает кандидатов вместо скрытого исправления имени.
Metaphone учитывает больше английских правил и имеет переменный размер, в отличие от фиксированных четырёх знаков soundex. Для русских имён не обещайте качество этого алгоритма. Доступ к учётной записи, слияние клиентов и финансовые операции требуют точного идентификатора, а не фонетического сходства.
Сопоставьте: soundex, levenshtein, similar_text. Байты, символы и выбор операции.
levenshteinПредложить близкий ASCII-артикул после небольшой опечатки, измерив число правок.
levenshtein(string $string1, string $string2, int $insertion_cost = 1, int $replacement_cost = 1, int $deletion_cost = 1): int| Часть договора | Что передать и что получить |
|---|---|
| string1 / string2 | Исходная и целевая последовательности байтов; цена относится к преобразованию первой во вторую. |
| insertion_cost | Цена вставки, по умолчанию 1. |
| replacement_cost | Цена замены, по умолчанию 1. |
| deletion_cost | Цена удаления, по умолчанию 1. Для понятной метрики задавайте неотрицательные согласованные цены. |
| Результат | Минимальная суммарная стоимость вставок, замен и удалений. При разных ценах вставки/удаления направление имеет значение. |
| Ошибки и границы | В PHP 8 снято прежнее ограничение 255 байтов с ответом -1, разрешены промежуточные 3/4 аргумента вместо только 2/5. Снятие ограничения не делает длинный ввод дешёвым: время O(m×n). |
| Версия / расширение | PHP 4.0.1+; standard |
- BR41 → BR42 отличается последним байтом.
- Одна замена с ценой 1 даёт расстояние 1.
- BR41 → BR4 требует одного удаления, тоже цена 1 при обычных параметрах.
<?php
echo levenshtein('BR41', 'BR41'), "\n";
echo levenshtein('BR41', 'BR42'), "\n";
echo levenshtein('BR41', 'BR4'), "\n";
echo levenshtein('A', 'B', 1, 5, 1), "\n";
Ожидаемый результат
0
1
1
2
В последнем вызове замена стоит 5, поэтому дешевле удалить A и вставить B за 2. Функция выбирает стоимость пути, а не просто считает несовпавшие позиции.
<?php
$typed = 'BR42';
$catalog = ['BR41', 'BR420', 'NUT9'];
if (strlen($typed) > 32) { throw new InvalidArgumentException('Слишком длинный артикул'); }
foreach ($catalog as $sku) {
$distance = levenshtein($typed, $sku);
if ($distance <= 1) { echo $sku, ':', $distance, "\n"; }
}
Ожидаемый результат
BR41:1
BR420:1
Каталог здесь маленький и известный. В большом поиске сначала отбирают кандидатов индексом/префиксом, ограничивают размер каждого артикула, затем считают расстояние; иначе число сравнений растёт с каталогом.
Это побайтовые правки, не графемы Unicode: русская буква может требовать несколько правок. Не применяйте без явного Unicode-алгоритма к редакторским словам. Расстояние не знает цены ошибочного заказа: похожий артикул — предложение, а не автоматическая подмена.
Сопоставьте: similar_text, strcmp, soundex. Байты, символы и выбор операции.
similar_textПоказать грубую оценку похожести двух коротких технических подписей при ручной сверке.
similar_text(string $string1, string $string2, float &$percent = null): int| Часть договора | Что передать и что получить |
|---|---|
| string1 / string2 | Сравниваемые байтовые строки; перестановка аргументов может изменить результат. |
| percent | Необязательная переменная по ссылке: процент равен числу совпавших байтов, делённому на среднюю длину строк, ×100. |
| Результат | Число совпавших байтов по алгоритму: найти первую наибольшую общую подстроку, затем рекурсивно обработать префиксы и суффиксы и сложить длины. |
| Ошибки и границы | Оценка не является симметричной метрикой и не равна расстоянию Левенштейна. Сложность O(N³), N — максимальная длина; длинный внешний текст опасен для CPU. |
| Версия / расширение | PHP 4+; standard |
- BR41 и BR42 имеют общий префикс BR4.
- Совпало 3 из средней длины 4.
- Ответ функции 3, а отдельный percent равен 75.
<?php
$percent = 0.0;
$matched = similar_text('BR41', 'BR42', $percent);
echo $matched, "\n", number_format($percent, 1, '.', ''), "%\n";
Ожидаемый результат
3
75.0%
Вызов возвращает целое число совпадений; процент записывается третьим аргументом в переменную. Отображение округлено только для человека.
<?php
$sourceCode = 'ABCA';
$candidateCode = 'CABAA';
echo similar_text($sourceCode, $candidateCode), "\n";
echo similar_text($candidateCode, $sourceCode), "\n";
echo levenshtein($sourceCode, $candidateCode), "\n";
Ожидаемый результат
3
2
2
Короткие коды выбраны, чтобы показать неоднозначные общие подстроки. Разный ответ при перестановке не ошибка теста: таков алгоритм выбора. Левенштейн даёт 2: вставить C перед ABCA → CABCA, затем заменить второй C на A → CABAA. Для симметричного расстояния с равными ценами подходит именно эта метрика.
Не обозначайте процент как вероятность одинакового товара или правильности текста. Ограничивайте длину и число кандидатов; алгоритм считает байты, не смысл, язык или видимые Unicode-символы. Для точного равенства используйте ===, для пригодного поиска выбирайте модель задачи.
Сопоставьте: levenshtein, strcmp, substr_count. Байты, символы и выбор операции.
quoted_printable_encodeВ сыром письме вместо é видны последовательности =C3=A9. Это может быть quoted-printable: способ транспортной записи тела MIME-части. Сначала посмотрим исходный текст и его представление, затем — служебный перенос длинной строки.
quoted_printable_encode(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Исходные байты тела; выбор charset должен быть известен отдельно. |
| Результат | Quoted-printable по RFC 2045 §6.7: непечатаемые байты/знак = получают =HH, длинные строки получают мягкие переносы. IMAP-расширение не требуется. |
| Ошибки и границы | Это transfer encoding, а не смена кодировки и не шифрование. Не добавляет MIME-заголовки, границы частей или адреса отправителя. |
| Версия / расширение | PHP 5.3+; standard |
- Café=12 — текст в UTF-8.
- Байты é становятся =C3=A9, а знак равенства — =3D.
- Caf=C3=A9=3D12 передаёт те же данные; это не ошибка текста.
<?php
$text = 'Café=12';
$body = quoted_printable_encode($text);
echo 'Исходный текст: ', $text, "\n";
echo 'Тело MIME-части: ', $body, "\n";
echo 'После декодирования: ', quoted_printable_decode($body), "\n";
Ожидаемый результат
Исходный текст: Café=12
Тело MIME-части: Caf=C3=A9=3D12
После декодирования: Café=12
Функция работает с байтами уже выбранного UTF-8. Она не добавляет Content-Type, charset, адреса или MIME-границы. Для отправки реального письма используют почтовую библиотеку, которая согласует эти части; это пример формата тела, а не полное письмо.
<?php
$text = str_repeat('A', 80);
$body = quoted_printable_encode($text);
echo "Передаваемые строки:\n", $body, "\n";
$decoded = quoted_printable_decode($body);
echo 'После восстановления: ', strlen($decoded), " букв A без переноса\n";
Ожидаемый результат
Передаваемые строки:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAA
После восстановления: 80 букв A без переноса
В конце первой физической строки = вместе с CRLF означает мягкий перенос. Получатель его удаляет, поэтому исходная строка остаётся непрерывной. Настоящий перевод строки тела и служебный мягкий перенос имеют разный смысл. Длинная запись показана целиком, чтобы можно было увидеть границу.
Для реальных писем используйте MIME-библиотеку, которая согласует charset, Content-Transfer-Encoding и окончания CRLF. Не применяйте этот формат вместо кодирования заголовка Subject (у него другой договор). Изменение пробелов в конце закодированной строки может повредить данные.
Сопоставьте: quoted_printable_decode, bin2hex. Байты, символы и выбор операции.
quoted_printable_decodeПри чтении сохранённого письма нужно превратить quoted-printable тело обратно в текстовые байты. Функция восстанавливает =HH и убирает мягкие переносы. Само письмо с заголовками сначала разбирает MIME-парсер, который определяет формат именно этой части.
quoted_printable_decode(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Тело части в quoted-printable, а не весь документ письма с заголовками. |
| Результат | Декодированная бинарная строка по RFC 2045 §6.7. IMAP-расширение не требуется. |
| Ошибки и границы | Не разбирает MIME и не проверяет все ошибки письма. В частности это не SMTP dot-unstuffing: дополнительные точки в начале строки не снимаются. |
| Версия / расширение | PHP 4+; standard |
- Caf=C3=A9=3D12 — тело, помеченное quoted-printable и charset=UTF-8.
- Декодер восстанавливает байты, затем UTF-8 позволяет прочитать é.
- Пользователь видит Café=12 вместо транспортной записи.
<?php
$body = 'Caf=C3=A9=3D12';
$text = quoted_printable_decode($body);
echo 'Текст письма: ', $text, "\n";
Ожидаемый результат
Текст письма: Café=12
Здесь кодировка UTF-8 известна заранее. Для другой кодировки после декодирования нужен отдельный конвертер, например mb_convert_encoding. Декодер не определяет язык и не превращает все байты автоматически в корректный UTF-8.
<?php
$body = "Заказ BR=\r\n41 готов";
echo quoted_printable_decode($body), "\n";
echo quoted_printable_decode("..stock\r\n"), "Конец\n";
Ожидаемый результат
Заказ BR41 готов
..stock
Конец
=CRLF внутри BR41 убирается целиком. Обычный CRLF после ..stock остаётся, как и обе точки: снятие SMTP dot-stuffing — отдельный транспортный шаг. Первая строка демонстрирует именно мягкий перенос; для совместимой отправки исходные русские байты закодирует quoted_printable_encode.
Не декодируйте повторно уже декодированный текст: буквальные последовательности =HH могут измениться. После получения байтов проверяют кодировку/размер/договор полей. Для HTML-вывода отдельно применяют htmlspecialchars, даже если письмо имело текстовый MIME-тип.
Сопоставьте: quoted_printable_encode, htmlspecialchars, bin2hex. Байты, символы и выбор операции.
convert_uuencodeПри поддержке старого обмена встретилось требование передать содержимое в uuencode. Эта функция превращает исходные байты в печатный блок. Это исторический транспортный формат, а не шифрование: получатель свободно восстановит исходное содержимое.
convert_uuencode(string $string): string| Часть договора | Что передать и что получить |
|---|---|
| string | Любые исходные байты, включая NUL. |
| Результат | Печатная uuencode-строка примерно на 35% длиннее исходной; строки begin/end не добавляются. С PHP 8 пустой вход тоже даёт строку, а не прежний false. |
| Ошибки и границы | Не добавляет имя файла, права или заголовок контейнера; не обеспечивает конфиденциальность/целостность. |
| Версия / расширение | PHP 5+; standard |
- Исходная строка BR41;12 и перевод строки — запись остатков.
- convert_uuencode создаёт печатные строки с информацией о длине блока.
- Получатель применит convert_uudecode и снова прочитает BR41;12.
<?php
$record = "BR41;12\n";
$encoded = convert_uuencode($record);
echo "Транспортный блок:\n", $encoded;
echo "Восстановленный текст:\n", convert_uudecode($encoded);
Ожидаемый результат
Транспортный блок:
(0E(T,3LQ,@H`
`
Восстановленный текст:
BR41;12
Строки с обратными кавычками и переводами строки являются частью формата, а не PHP-кодом. Не удаляйте их через trim. Функция возвращает только блок данных: строки begin/end с именем и правами файла она не добавляет. В новом протоколе следует использовать оговорённый им формат, часто Base64, а не выбирать uuencode случайно.
Перед запуском сохраните рядом с PHP-файлом файл stock.txt с содержимым ниже. Кодировка UTF-8 без BOM; окончание показанной строки — LF. В скачиваемом наборе примера этот файл уже подготовлен.
BR41;12
<?php
$contents = file_get_contents(__DIR__ . '/stock.txt');
if ($contents === false) {
throw new RuntimeException('Выгрузка не прочитана');
}
$encoded = convert_uuencode($contents);
echo "Блок для старого получателя:\n", $encoded;
echo 'Исходных байтов: ', strlen($contents), "\n";
echo 'Байтов в блоке: ', strlen($encoded), "\n";
Ожидаемый результат
Блок для старого получателя:
(0E(T,3LQ,@H`
`
Исходных байтов: 8
Байтов в блоке: 16
Файл stock.txt приложен к примеру. В коротком блоке доля служебных символов велика; на длинных данных увеличение составляет примерно треть. Чтение целиком подходит только для ограниченного небольшого файла. Ни сетевой отправки, ни защиты от подделки здесь нет.
Для нового протокола выбирайте его согласованную сериализацию/кодирование, например Base64 для бинарного поля JSON, а не uuencode по привычке. Имя/путь из полного старого контейнера проверяют отдельно; не позволяйте импортируемому имени выбирать место записи на сервере.
Сопоставьте: convert_uudecode, bin2hex. Байты, символы и выбор операции.
convert_uudecodeВ старом экспорте находится печатный uuencode-блок. Нужно получить исходные байты, а затем уже разбирать запись остатков. Декодирование снимает транспортное представление; оно не проверяет структуру товара и не выбирает, куда сохранять файл.
convert_uudecode(string $string): string|false| Часть договора | Что передать и что получить |
|---|---|
| string | Закодированный блок без строк begin/end полного uuencoded-файла. |
| Результат | Исходные байты либо false при ошибке; пустая строка является допустимым декодированным содержимым. |
| Ошибки и границы | Некорректный блок может вызвать E_WARNING и false. Функция не принимает begin/end и не является строгим парсером полного контейнера. |
| Версия / расширение | PHP 5+; standard |
- (0E(T,3LQ,@H` и строка завершения — транспортный блок.
- convert_uudecode возвращает BR41;12 с переводом строки.
- После проверки false запись можно разобрать: товар BR41, остаток 12.
<?php
$encoded = "(0E(T,3LQ,@H`\n`\n";
$decoded = convert_uudecode($encoded);
if ($decoded === false) {
throw new RuntimeException('Блок uuencode не прочитан');
}
echo "Восстановленная запись:\n", $decoded;
Ожидаемый результат
Восстановленная запись:
BR41;12
Сначала проверяется именно === false. Пустая строка может быть корректным декодированным содержимым и не равна ошибке. Строк begin/end полного контейнера здесь нет: их нельзя передавать этой функции как часть полезного блока.
<?php
$encoded = "(0E(T,3LQ,@H`\n`\n";
$record = convert_uudecode($encoded);
if ($record === false) {
throw new RuntimeException('Повреждён транспортный блок');
}
$fields = explode(';', rtrim($record, "\r\n"));
if (count($fields) !== 2 || $fields[0] === '' || !ctype_digit($fields[1])) {
throw new RuntimeException('Нужны код товара и неотрицательный остаток');
}
echo 'Товар: ', $fields[0], "\n";
echo 'Остаток: ', $fields[1], " шт.\n";
Ожидаемый результат
Товар: BR41
Остаток: 12 шт.
Это две разные задачи: uudecode снимает представление, explode разделяет уже известный простой формат. Мы проверили число полей и десятичные цифры; в настоящем импорте дополнительно ограничивают длину/диапазон и проверяют товар по каталогу. Формат с кавычками и разделителями внутри значений требует CSV-парсера.
<?php
set_error_handler(function (int $level): bool {
return $level === E_WARNING;
});
try {
$broken = convert_uudecode('~');
} finally {
restore_error_handler();
}
echo $broken === false ? "Повреждённый блок отклонён\n" : "Блок прочитан\n";
$empty = convert_uudecode("`\n");
echo $empty === '' ? "Пустой блок прочитан: данных нет\n" : "Другой результат\n";
Ожидаемый результат
Повреждённый блок отклонён
Пустой блок прочитан: данных нет
Повреждённый вызов намеренно изолирован временным обработчиком E_WARNING, чтобы в учебном результате было понятное сообщение. В рабочем импорте ошибка также попадает в закрытый журнал. Полученные имена файлов и пути никогда не должны автоматически определять место записи на сервере.
Для нового протокола выбирайте его согласованную сериализацию/кодирование, например Base64 для бинарного поля JSON, а не uuencode по привычке. Имя/путь из полного старого контейнера проверяют отдельно; не позволяйте импортируемому имени выбирать место записи на сервере.
Сопоставьте: convert_uuencode, bin2hex. Байты, символы и выбор операции.