Глава 8 · С нуля → практика
Типы данных: значения, преобразования и проверка
Как отличить количество от строки формы, отсутствие значения от нуля и деньги от приблизительного числа.
Вид значения определяет, что с ним можно сделать
В карточке товара нужны артикул 002, количество 2 и измеренная масса 0.5 кг. Внешне это похожие цифры, но смысл разный: артикул нельзя потерять как 2, количество можно складывать, массу измеряют дробным числом. Тип описывает вид значения и правила операций. Если присваивание пока незнакомо, начните с одной переменной и её чтения.
- Артикул «002» → string: сохраняем ведущие нули.
- Количество 2 → int: считаем целые упаковки.
- Масса 0.5 → float: измеряем дробную величину.
- echo собирает из них три понятные строки.
<?php
$sku = '002';
$quantity = 2;
$weightKg = 0.5;
echo 'Артикул: ', $sku, "\n";
echo 'Упаковок: ', $quantity, "\n";
echo 'Масса упаковки: ', $weightKg, ' кг', "\n";
Ожидаемый результат
Артикул: 002
Упаковок: 2
Масса упаковки: 0.5 кг
Кавычки делают 002 текстом. У 0.5 десятичный разделитель в PHP — точка. Мы не выводим массу как 0,5: форматирование для человека рассматривается отдельно. Такой вывод в консоль не требует HTML.
| Тип | Запись и смысл |
|---|---|
| string | '002' — текст; даже если выглядит как число. |
| int | 2 — целое количество. Диапазон зависит от платформы; на границах нужны отдельные проверки. |
| float | 0.5 — число с плавающей точкой. Не все десятичные доли хранятся точно. |
| bool | true или false — ответ да/нет, без кавычек. |
| null | Явное отсутствие значения по договору; не равно числу 0 или пустой строке. |
Обычная переменная может позже получить значение другого типа. Это разрешение языка не обязывает смешивать смыслы: количество храните в quantity, а подпись для человека — в quantityLabel. Массив, объект и ресурс будут отдельными видами значений; типы параметров и возвращаемого результата появятся после объяснения функций.
Проверить тип и увидеть, почему одинаковый вывод обманывает
Строка "2" и число 2 могут одинаково выглядеть на экране. Встроенная диагностическая функция var_dump($value) получает значение в круглых скобках и показывает его тип вместе с содержимым. Это готовое средство наблюдения, а не объявление собственной функции: имя var_dump уже известно PHP. Команда echo предназначена для сообщения пользователю.
<?php
$quantity = 2;
$quantityText = '2';
echo 'Для человека: ', $quantity, ' и ', $quantityText, "\n";
var_dump($quantity);
var_dump($quantityText);
Ожидаемый результат
Для человека: 2 и 2
int(2)
string(1) "2"
int(2) — целое 2. string(1) "2" — строка длиной один байт. Цифра 1 здесь длина строки, а не её значение. var_dump не форматирует готовую карточку товара, зато помогает понять неожиданную операцию.
Ответы true/false и отсутствие null тоже имеют практический смысл. Покажем их в минимальном выборе сообщения. Новая запись if ($isAvailable) { ... } else { ... } выполняет первый блок при true, второй при false. === null проверяет именно null. Подробный разбор скобок и ветвей находится в следующей главе.
<?php
$isAvailable = false;
$customerNote = null;
if ($isAvailable) {
echo "Можно заказать\n";
} else {
echo "Сейчас нет в наличии\n";
}
if ($customerNote === null) {
echo "Комментарий покупателя не указан\n";
}
Ожидаемый результат
Сейчас нет в наличии
Комментарий покупателя не указан
false — известный ответ о доступности. null — отсутствие комментария по принятому договору. Строка "false" была бы непустым текстом и дала бы другое поведение условия: тип нельзя угадывать по слову в кавычках.
Дальше последовательно уточняем каждый тип: его запись, значения, преобразования, пределы и поведение при ошибках. Приведение типа не проверяет предметный смысл: превращение строки в число не доказывает, что это допустимое количество.
У значения есть тип: сначала научимся его наблюдать
Цена 150, артикул "150" и признак оплаты true отвечают на разные вопросы. Тип показывает, как PHP представляет значение и какие действия к нему применимы. 150 без кавычек — целое число. '150' в кавычках — текст из трёх символов. Внешне echo может показать их одинаково, поэтому для учебной проверки нужен инструмент, который показывает ещё и тип.
var_dump — встроенная диагностическая функция. В скобки передайте исследуемое значение; функция напечатает тип и содержимое. Это временное наблюдение разработчика, а не оформление для посетителя сайта. Перед выводом личных данных в журнал также нужно учитывать, кому доступен журнал.
<?php
var_dump(150);
var_dump('150');
var_dump(true);
var_dump(null);
Ожидаемый результат
int(150)
string(3) "150"
bool(true)
NULL
int — целое число; string(3) — строка длиной 3 байта; bool — логическое значение; NULL — отсутствие значения. У кириллицы число байтов не обязано совпадать с количеством букв. var_dump добавляет диагностическую запись, а не меняет переданное значение.
<?php
$value = 150;
echo 'Цена: ', $value, ' руб.; тип ', get_debug_type($value), "\n";
$value = 'Цена пока не указана';
echo 'Подпись: ', $value, '; тип ', get_debug_type($value), "\n";
Ожидаемый результат
Цена: 150 руб.; тип int
Подпись: Цена пока не указана; тип string
Первое значение — число 150, второе — текст. Присваивание заменило значение под тем же именем; get_debug_type возвращает название текущего типа как строку для echo. Оно не изменяет value. Так можно наблюдать динамическую типизацию, но для расчётов яснее отдельные price и label: фразу «Цена пока не указана» складывать с доставкой нельзя.
Карта типов: данные, составные значения и договоры
Одна надпись «0» — разные состояния данных
Интеграция передаёт остаток товара. Внешне похожие значения требуют разных действий: нулевой остаток нельзя принять за отсутствие поля.
- stock = 0
Целое число: товар известен, на складе ноль штук.
- stock = «0»
Строка из формы. Проверяем формат и только затем преобразуем в число.
- Ключ stock отсутствует
Поставщик не сообщил остаток. Нужна отдельная политика обработки.
Переменная связывает имя со значением. Тип определяет, какие операции осмысленны. Проверка типа, допустимого диапазона и наличия поля отвечает на три разных вопроса.
$orderId→150→ int: номер в нашей системе$externalId→'00150'→ string: внешний код$isPaid→false→ bool: оплата не подтверждена$comment→null→ комментария пока нет
| Группа | Названия | Пример смысла |
|---|---|---|
| Скалярные значения | bool, int, float, string | Один переключатель, счётчик, измерение, текст. |
| Набор данных | array | Позиции заказа или значения по именованным ключам. |
| Объект | object; конкретный класс или enum | Корзина с состоянием и действиями либо один из разрешённых статусов. |
| Специальный доступ | resource | Открытый поток; это доступ к чтению, а не сам текст файла. |
| Отсутствие | null | Адрес доставки ещё не выбран. |
| Договор входа или выхода | mixed, iterable, callable, void, never, self, parent, static, true, false | Описывает, какие значения или способы завершения разрешены в конкретном месте. |
Последняя строка не означает, что можно записать в переменную «значение void». Часть названий относится к объявлениям функций и классов. Сначала изучим обычные значения, потом — проверяемые договоры. PHP также позволяет сочетать типы: A|B — один из вариантов, A&B — одновременное выполнение двух объектных договоров.
PHP не даёт объявить произвольный псевдоним типа вроде type ProductRow = .... Формы массивов в PHPDoc помогают редактору и анализатору, но не становятся встроенной проверкой PHP. mixed и iterable — встроенные объединения, не образец создания собственных псевдонимов.
null: значение ещё не задано по смыслу задачи
Нужен понятный текст о комментарии к доставке. Сначала договоримся: null — покупатель ещё не отвечал, пустая строка — сознательно оставил без комментария, непустая строка — указание курьеру. Для выбора текста используем небольшую конструкцию if (проверка) { действия }: действия в фигурных скобках выполняются при true. elseif проверяет следующий случай, а else обрабатывает оставшийся. Полный разбор выбора — в уроке условий.
Покупатель не выбрал дату доставки. Ставить число 0 или текст «нет» неудобно: это другие виды данных. Значение null явно обозначает предусмотренное отсутствие. У этого типа ровно одно значение. Запись без кавычек имеет смысл для PHP; 'null' в кавычках — просто четыре буквы.
=== спрашивает о строгом равенстве значения и типа. Поэтому пустая строка и ноль не равны null. В следующем примере логический результат сравнения выбирает понятное сообщение.
<?php
$comment = null;
if ($comment === null) {
echo "Комментарий ещё не заполнен\n";
}
$comment = '';
if ($comment === '') {
echo "Покупатель выбрал доставку без комментария\n";
}
$comment = 'Позвонить у подъезда';
if ($comment !== null && $comment !== '') {
echo 'Курьеру: ', $comment, "\n";
}
Ожидаемый результат
Комментарий ещё не заполнен
Покупатель выбрал доставку без комментария
Курьеру: Позвонить у подъезда
Каждое присваивание задаёт отдельную учебную ситуацию. !== означает строгое неравенство, && требует обе проверки одновременно. Последняя строка выходит только для уже заданного непустого текста. Ноль и пустая строка не равны null при ===; не заменяйте это условие общей проверкой истинности.
isset($note) отвечает «переменная существует и не равна null?». Поэтому false получится и у явно присвоенного null, и после удаления. unset($note) удаляет привязку имени в текущей области, а не присваивает ему новый полезный текст. Обе конструкции получают имя исследуемой переменной в круглых скобках.
<?php
$note = null;
if (!isset($note)) {
echo "Указания для курьера нет\n";
}
$note = 'Позвонить заранее';
if (isset($note)) {
echo 'Подпись на заказе: ', $note, "\n";
}
unset($note);
if (!isset($note)) {
echo "Временная переменная удалена\n";
}
Ожидаемый результат
Указания для курьера нет
Подпись на заказе: Позвонить заранее
Временная переменная удалена
isset проверяет наличие имени с ненулевым значением, ! меняет ответ на противоположный. Поэтому первая и последняя проверки одинаково успешны, хотя сначала имя содержит null, а в конце имени уже нет. Вторая ветка использует реальный текст до удаления. unset не удаляет заказ или сохранённую запись из базы.
Приведение (unset) — старый синтаксис, удалённый в PHP 8.0. Для явного отсутствия присваивайте null; для удаления переменной используйте unset. Различие отсутствующего ключа и ключа со значением null подробно видно в массиве.
bool: ответ на вопрос «да или нет»
Переменная $isPaid должна отвечать на один вопрос: оплата подтверждена? Значения true и false пишутся без кавычек. Имена в PHP для них регистронезависимы, но единый нижний регистр проще читать. Не путайте логическое false со строкой "false" из конфигурации.
Запись условие ? первый_вариант : второй_вариант выбирает значение: при true — первое, при false — второе. Здесь это две строки для посетителя. После выбора echo получает и выводит одну из них.
<?php
$isPaid = false;
echo $isPaid ? "Можно комплектовать\n" : "Ждём оплату\n";
$isPaid = true;
echo $isPaid ? "Можно комплектовать\n" : "Ждём оплату\n";
Ожидаемый результат
Ждём оплату
Можно комплектовать
Первый выбор получает false и возвращает вторую строку. После присваивания true второй выбор возвращает первую строку. Echo выводит готовую строку, а не само логическое значение. Внешняя платёжная система в этом примере не вызывается: присваивания позволяют безопасно воспроизвести два состояния.
| При переводе в bool получится false | Примеры |
|---|---|
| Логическое ложное значение | false |
| Нули | 0, 0.0, -0.0 |
| Особые строки | Пустая строка и ровно строка "0" |
| Пустой набор и отсутствие | [], null |
Остальные обычные значения истинны: строка "false", пробел, "00", "0.0", отрицательное число, непустой массив и даже NAN. У отдельных внутренних объектов, например SimpleXML без содержимого, есть специальные правила. Поэтому проверка существования товара через одну истинность ID может потерять законный нулевой ID. Ниже сравним bool-приведение с разбором настройки.
int: посчитать целые предметы
Для количества коробок удобно целое число. У него нет дробной части. Знак минус задаёт отрицательное значение; плюс можно написать явно, но обычно опускают. Цифры без кавычек — числовая запись. Для сохранения ведущих нулей внешнего артикула нужна строка, потому что номер как текст и количество — разные понятия.
<?php
$stock = 12;
$ordered = 3;
$remaining = $stock - $ordered;
echo 'Заказано: ', $ordered, ' шт.', "\n";
echo 'Осталось для продажи: ', $remaining, ' шт.', "\n";
$annualTarget = 1_000;
echo 'Годовой план: ', $annualTarget, ' шт.', "\n";
Ожидаемый результат
Заказано: 3 шт.
Осталось для продажи: 9 шт.
Годовой план: 1000 шт.
12 минус 3 даёт int 9. Он сохранён в remaining, поэтому доступен дальнейшему расчёту. Подчёркивание в литерале 1_000 помогает читать код и не входит в число или вывод; доступно с PHP 7.4. Его ставят только между цифрами, без удвоения. Отрицательное количество возврата можно записать как -3, но правила склада должны определить, где такой знак разрешён.
intdiv(int $num1, int $num2): int принимает делимое и делитель и возвращает целое частное с отсечением к нулю. Это обычная функция: оба числа входят в скобки, возвращённое число занимает место вызова в правой части присваивания. % возвращает остаток от целочисленного деления.
<?php
$items = 7;
$capacity = 3;
$fullBoxes = intdiv($items, $capacity);
$looseItems = $items % $capacity;
echo 'Полных коробок: ', $fullBoxes, "\n";
echo 'Банок отдельно: ', $looseItems, "\n";
echo 'Проверка комплектации: ', $fullBoxes * $capacity + $looseItems, "\n";
$exactShare = 6 / 3;
echo 'Точный результат 6 / 3: ', $exactShare, ' (', get_debug_type($exactShare), ')', "\n";
$average = 7 / 2;
echo 'Среднее на двух получателей: ', $average, ' (', get_debug_type($average), ')', "\n";
Ожидаемый результат
Полных коробок: 2
Банок отдельно: 1
Проверка комплектации: 7
Точный результат 6 / 3: 2 (int)
Среднее на двух получателей: 3.5 (float)
Возврат intdiv 2 используется как число полных коробок; остаток 1 нельзя потерять. У / результат int, если оба операнда целые и деление точное, иначе float. Дробное среднее не означает возможность отправить половину банки. Делитель должен быть ненулевым; intdiv(PHP_INT_MIN, -1) не помещается в int и вызывает ArithmeticError.
Размер int зависит от сборки платформы, часто это 64 бита, а на некоторых платформах 32. Границы сообщают PHP_INT_MIN, PHP_INT_MAX, размер в байтах — PHP_INT_SIZE. PHP не предоставляет отдельный обычный unsigned int. При выходе результата арифметики за предел int он может стать float; точные огромные идентификаторы так обрабатывать нельзя. Проверку переполнения и системы счисления разберём ниже.
float: измерения и дробные величины
Вес посылки и температура допускают дробную часть. В PHP она отделяется точкой: 1.25. Запятая не является десятичным разделителем в исходном коде. Экспоненциальная запись обозначает степень десяти: 2.5e3 — 2500, 2.5e-3 — 0.0025. Даже целое по математическому значению число может иметь тип float.
<?php
$unitWeightKg = 1.25;
$quantity = 2;
$packageWeightKg = 0.2;
$totalWeightKg = $unitWeightKg * $quantity + $packageWeightKg;
echo 'Товар: ', $unitWeightKg * $quantity, ' кг', "\n";
echo 'С упаковкой: ', $totalWeightKg, ' кг', "\n";
$gramsInBatch = 2.5e3;
echo 'Та же масса товара в граммах: ', $gramsInBatch, "\n";
echo 'Тип граммов: ', get_debug_type($gramsInBatch), "\n";
Ожидаемый результат
Товар: 2.5 кг
С упаковкой: 2.7 кг
Та же масса товара в граммах: 2500
Тип граммов: float
1.25 умножается на 2, затем к 2.5 прибавляется 0.2: в totalWeightKg лежит float для следующего выбора тарифа. 2.5e3 означает 2.5 × 1000 и остаётся float, хотя echo показывает 2500. Литерал 2500 без точки был бы int. Экранное представление 2.7 не означает точную десятичную арифметику внутри.
is_nan(float $num): bool распознаёт специальное значение NAN. is_finite(float $num): bool разрешает конечные числа и отклоняет INF, -INF, NAN. Функции получают исследуемое число в скобках; их логический результат сразу становится проверкой if. В четырёх отдельных ситуациях ниже меняется только поступившее измерение.
<?php
$temperature = 12.5;
if (is_finite($temperature)) {
echo 'Температура: ', $temperature, ' °C', "\n";
}
$temperature = INF;
if (!is_finite($temperature)) {
echo "Измерение отклонено: бесконечность\n";
}
$temperature = NAN;
if (is_nan($temperature)) {
echo "Измерение отклонено: число не определено\n";
}
$temperature = -INF;
if (!is_finite($temperature)) {
echo "Измерение отклонено: бесконечность\n";
}
Ожидаемый результат
Температура: 12.5 °C
Измерение отклонено: бесконечность
Измерение отклонено: число не определено
Измерение отклонено: бесконечность
Первое значение проходит проверку is_finite. NAN распознаётся отдельной функцией, потому что оно не равно даже самому себе, в том числе при ===. INF и -INF не проходят is_finite. Это проверка представления; допустимый диапазон температуры конкретного датчика нужно проверять дополнительно. Здесь нет JSON и внешнего устройства.
В дробной арифметике проверяется допустимая погрешность, а не количество красивых цифр на экране. Точность зависит от представления числа; преобразование в форматированную строку меняет только вывод. Для денег выбор int в копейках или десятичной библиотеки должен соответствовать валюте, правилам округления и пределам суммы.
Приведение создаёт другое представление; оно не проверяет смысл
Приведение пишется перед значением: (int) $text, (float) $text, (string) $value, (bool) $value. Получится новое значение выбранного типа. Сама исходная переменная от одного такого выражения не изменится; её можно заменить отдельным присваиванием.
<?php
$raw = '012';
$quantity = (int) $raw;
echo 'Исходный текст: [', $raw, ']', "\n";
echo 'Количество для расчёта: ', $quantity, "\n";
echo 'За три одинаковые партии: ', $quantity * 3, "\n";
echo 'Опасный хвост 12items превращается в: ', (int) '12items', "\n";
echo 'Слово items превращается в: ', (int) 'items', "\n";
echo 'Дроби отсекаются к нулю: ', (int) 3.9, ', ', (int) -3.9, "\n";
Ожидаемый результат
Исходный текст: [012]
Количество для расчёта: 12
За три одинаковые партии: 36
Опасный хвост 12items превращается в: 12
Слово items превращается в: 0
Дроби отсекаются к нулю: 3, -3
В скобках (int) указано требуемое представление. Raw остаётся строкой 012, quantity получает int 12, который используется в умножении. Последние строки показывают предел преобразования: принятие числа PHP не подтверждает допустимость пользовательского текста. Для идентификатора 012 следует оставить строку, если ведущий ноль значим.
settype(mixed &$var, string $type): bool получает изменяемую переменную и название типа. Знак & в справочной сигнатуре предупреждает о записи в эту переменную. В нашем вызове он не пишется. Возврат сообщает об успехе; преобразованное значение нужно читать из самой переменной.
<?php
$quantity = '12';
$converted = settype($quantity, 'int');
if ($converted) {
echo 'Тип количества после преобразования: ', get_debug_type($quantity), "\n";
echo 'В двух партиях: ', $quantity * 2, ' шт.', "\n";
}
Ожидаемый результат
Тип количества после преобразования: int
В двух партиях: 24 шт.
Converted получил true, quantity стал int 12. Умножение использует quantity, а не converted: true не является преобразованным количеством. Когда не нужно менять входную переменную, отдельное присваивание $count = (int) $raw яснее сохраняет исходник. Допустимые type: boolean/bool, integer/int, float/double, string, array, object, null; неизвестное имя вызывает ValueError в PHP 8.
Используйте современные краткие имена кастов. Псевдонимы (integer), (boolean), (double), (real) нужно оценивать по конкретной версии; в PHP 8.5 integer/boolean/double/binary устарели, а real удалён ещё в PHP 8.0. Скалярное преобразование целиком не зависит от strict_types: явный каст остаётся вашим явным решением.
Значение не меняется само, но операция выбирает контекст
Одна строка "12" может участвовать в сложении и в соединении текста. В первом случае PHP интерпретирует её как число для конкретной операции; во втором — как строку. Это называется неявным преобразованием. Оно не означает, что исходная переменная стала числом навсегда.
<?php
$raw = '12';
$stockAfterDelivery = $raw + 3;
$combinedCode = $raw . '3';
echo 'После поступления трёх штук: ', $stockAfterDelivery, "\n";
echo 'Код из двух текстовых частей: ', $combinedCode, "\n";
echo 'Исходное значение: ', $raw, ' (', get_debug_type($raw), ')', "\n";
if (12 == $raw) {
echo "Значения равны после допустимого сравнения с преобразованием\n";
}
if (12 !== $raw) {
echo "При строгом сравнении типы различаются\n";
}
Ожидаемый результат
После поступления трёх штук: 15
Код из двух текстовых частей: 123
Исходное значение: 12 (string)
Значения равны после допустимого сравнения с преобразованием
При строгом сравнении типы различаются
Плюс выбрал числовое действие и дал int 15; точка соединяет строки и дала string 123. Ни то, ни другое не переписало raw. == допускает преобразование по правилам сравнения, === требует одинаковый тип и значение, !== — отрицание строгого равенства. Не используйте арифметическое преобразование как проверку формата внешнего ввода.
| Контекст | Где появляется | Что помнить |
|---|---|---|
| Числовой | +, -, *, / и другие арифметические действия | Не все значения интерпретируются как числа; нечисловой текст может дать TypeError. У + над двумя массивами другой смысл: объединение по ключам. |
| Строковый | echo, print, сцепление и интерполяция | Число представляется текстом; массив нельзя превратить таким образом в содержательный список. |
| Логический | if, логические операторы, условный выбор | Применяются правила bool, а не перевод слов true/false. |
| Целочисленный/строковый | Побитовые &, |, ^ и ~; сдвиги << и >> | Для &, | и ^ две строки обрабатываются по байтам; ~ также работает по байтам у строки. В остальных случаях эти операции рассматривают значения как int. У сдвигов << и >> операнды и результат всегда рассматриваются как int. |
| Сравнение | ==, <, > и похожие операции | Преобразование зависит от пары типов и выбранного оператора. |
| Вызов и возврат | Объявленные типы функций | Зависит от договора и режима скалярных вызовов; рассмотрено ниже. |
Проверьте понимание. Можно ли заменить строковый артикул "0012" на int только потому, что он складывается с 0?
Разбор
Нет. Возможность арифметического преобразования не задаёт смысл поля. Для артикула ведущие нули и текстовый формат могут быть частью договора с поставщиком.
Следующая ступень: типы в функциях, объектах и внешних данных
Теперь можно объяснять случаи, где тип проверяется на границе вызова, а данные приходят из формы или API. Далее встречаются собственные функции, условия, перебор и исключения. Если вы идёте с нуля, сначала пройдите устройство функции и return, условия и цикл и путь ошибки, затем вернитесь к этим задачам.
Смысл объявленного типа — обнаружить несовместимое значение в конкретной точке. Смысл проверки данных — установить, что значение допустимо для нашей задачи. Положительная сумма, существующий номер заказа и доступный пользователю заказ не становятся правильными только от объявления int.
bool: почему строка "false" может включить настройку
Здесь начинается применение уже известных значений. Для следующей функции нужны параметр и return, а match, foreach и catch кратко разобраны непосредственно перед их использованием. Первый опыт ниже обходится без собственной функции.
Представьте настройку уведомлений. Нам нужны два состояния: включено и выключено. Значения true и false относятся к типу bool. Но из файла конфигурации или формы часто приходит текст. Приведение (bool) отвечает на вопрос о логическом значении текста по правилам PHP; оно не переводит английские слова.
- Исходная строка "false"
- Приведение к bool: непустой текст
- true — неожиданное включение
Проверим последствия преобразования в отдельных состояниях. (bool) перед выражением создаёт логическое значение; запись условие ? строка_да : строка_нет выбирает понятную подпись. Никаких уведомлений этот код не отправляет — он показывает, какое решение получилось бы.
<?php
$raw = 'false';
$enabled = (bool) $raw;
echo 'Текст [', $raw, '] после (bool): ', $enabled ? 'включено' : 'выключено', "\n";
$raw = '0';
echo 'Текст [0] после (bool): ', (bool) $raw ? 'включено' : 'выключено', "\n";
$raw = '';
echo 'Пустой текст после (bool): ', (bool) $raw ? 'включено' : 'выключено', "\n";
$selected = [];
echo 'Пустой выбор: ', (bool) $selected ? 'позиции выбраны' : 'позиций нет', "\n";
$selected = [0];
echo 'Выбор с кодом 0: ', (bool) $selected ? 'позиции выбраны' : 'позиций нет', "\n";
Ожидаемый результат
Текст [false] после (bool): включено
Текст [0] после (bool): выключено
Пустой текст после (bool): выключено
Пустой выбор: позиций нет
Выбор с кодом 0: позиции выбраны
Строка false содержит обычные буквы и даёт true. Исключения среди строк — пустая строка и ровно 0. У массива проверяется наличие элементов: [0] непустой, хотя единственное значение само по себе ложно. Исходный raw остаётся строкой; enabled — отдельный bool. Нулевой код может быть допустимым, поэтому значение элемента и наличие элемента — разные вопросы.
Теперь функция notificationSetting получает один текст raw и должна вернуть bool. Trim возвращает строку без крайних пробелов; strtolower возвращает её вариант с нижним регистром ASCII. Match сравнивает полученную строку строго с вариантами: две строки слева от => разделены запятой и ведут к одному результату. Default обрабатывает остальные входы; throw передаёт управление в catch вместо return. Foreach последовательно даёт функции три независимых входа.
| Место вызова | Местные действия | Возврат и дальнейшее действие |
|---|---|---|
| notificationSetting('false') | trim → false, strtolower → false, ветка false/0 | return false попадает в ?: → echo «Выключено» |
| notificationSetting(' TRUE ') | trim → TRUE, strtolower → true, ветка true/1 | return true попадает в ?: → echo «Включено» |
| notificationSetting('maybe') | После нормализации maybe, подходящей ветки нет | throw → catch; печатается причина, bool не возвращён |
<?php
declare(strict_types=1);
function notificationSetting(string $raw): bool
{
return match (strtolower(trim($raw))) {
'true', '1' => true,
'false', '0' => false,
default => throw new InvalidArgumentException('Неизвестная настройка'),
};
}
foreach (['false', ' TRUE ', 'maybe'] as $raw) {
try {
echo notificationSetting($raw) ? "Включено\n" : "Выключено\n";
} catch (InvalidArgumentException $error) {
echo $error->getMessage(), "\n";
}
}
Ожидаемый результат
Выключено
Включено
Неизвестная настройка
Функция сначала нормализует пробелы и регистр, затем принимает только согласованные варианты. Альтернатива — FILTER_VALIDATE_BOOLEAN с FILTER_NULL_ON_FAILURE, если его набор допустимых слов подходит вашему протоколу. Неизвестное значение не превращается молча в false.
В реальном вопросе о строках true/false ожидалось, что обычное условие распознает слова. Отсюда полезное разделение: истина по правилам языка и разбор выбранного текстового протокола. Наша функция оставляет неизвестное значение ошибкой, вместо молчаливого изменения настройки.
int: целые числа, системы счисления и предел размера
Количество коробок и цена в копейках — целые числа. В коде число можно записать в десятичной, двоичной, восьмеричной или шестнадцатеричной системе. Это разные записи одного значения. Они пригодятся для флагов, прав доступа и двоичных протоколов; обычное количество товара записывайте десятичным числом.
<?php
echo 26, ' ', 0b11010, ' ', 0o32, ' ', 0x1A, "\n";
echo 1_000_000, "\n";
echo intdiv(17, 5), ' коробки, остаток ', 17 % 5, "\n";
Ожидаемый результат
26 26 26 26
1000000
3 коробки, остаток 2
0b — двоичная запись, 0o — восьмеричная (PHP 8.1+), 0x — шестнадцатеричная; подчёркивания помогают читать литерал и не входят в число. В старой восьмеричной записи 032 — тоже26: не добавляйте ведущий ноль к десятичным литералам. intdiv возвращает целую часть частного, % — остаток.
Размер int зависит от платформы: используйте PHP_INT_SIZE, PHP_INT_MIN и PHP_INT_MAX, а не предположение о числе разрядов. При выходе за диапазон результат арифметики может стать float. Это особенно опасно для длинных идентификаторов из внешней системы: идентификатор лучше хранить строкой, если над ним не требуется арифметика.
В следующем договоре числа неотрицательны. Функция принимает имеющийся остаток current и поступление incoming. Проверка диапазона идёт до сложения: PHP_INT_MAX - incoming показывает максимально допустимый current. Исключение означает, что новый остаток не получен и записывать его нельзя.
<?php
function addNonNegative(int $current, int $incoming): int {
if ($current < 0 || $incoming < 0 || $current > PHP_INT_MAX - $incoming) {
throw new OverflowException('Сумма выходит за допустимый диапазон');
}
return $current + $incoming;
}
$stock = 120;
$stock = addNonNegative($stock, 30);
echo 'После поставки: ', $stock, ' шт.', "\n";
try {
$stock = addNonNegative(PHP_INT_MAX, 1);
} catch (OverflowException $error) {
echo 'Поставка отклонена: ', $error->getMessage(), "\n";
}
echo 'Последний допустимый остаток: ', $stock, ' шт.', "\n";
Ожидаемый результат
После поставки: 150 шт.
Поставка отклонена: Сумма выходит за допустимый диапазон
Последний допустимый остаток: 150 шт.
Первый вызов получает current120/incoming30, возвращает int150 в правую часть присваивания, stock становится150. Во втором опытном вызове проверяется предельная пара PHP_INT_MAX/1. До return дело не доходит: catch получает отказ, присваивание stock не завершилось, поэтому в нём остаётся150. Эта граница работает на поддерживаемой 32- или 64-битной платформе; конкретное максимальное число в ожидаемый вывод не зашито.
float: измерения, погрешность и сравнение
Температура датчика или результат деления часто не являются целыми. Float хранит приближённое двоичное число. Даже короткая десятичная дробь может не иметь точного двоичного представления. Отсюда разница между визуально одинаковой строкой и точным сравнением значений.
<?php
$measuredKg = 0.1 + 0.2;
$expectedKg = 0.3;
$toleranceKg = 0.000001;
echo 'Измерено: ', number_format($measuredKg, 3, '.', ''), ' кг', "\n";
echo 'Ожидается: ', number_format($expectedKg, 3, '.', ''), ' кг', "\n";
if ($measuredKg !== $expectedKg) { echo "Точное двоичное значение отличается\n"; }
$differenceKg = abs($measuredKg - $expectedKg);
if ($differenceKg <= $toleranceKg) {
echo "Масса принята: отклонение укладывается в допуск\n";
}
Ожидаемый результат
Измерено: 0.300 кг
Ожидается: 0.300 кг
Точное двоичное значение отличается
Масса принята: отклонение укладывается в допуск
Оба значения выглядят как0.300 после округления для экрана, но === сравнивает хранимые числа. Abs возвращает положительный модуль разницы; сравнение с допуском даёт true и выбирает приёмку. Допуск задан в килограммах и выбран только для опыта. Number_format возвращает строку с тремя десятичными знаками и не исправляет внутреннее float. Измерения реального прибора требуют его диапазона, единиц и точности.
<?php
declare(strict_types=1);
function sensorValue(float $value): string
{
if (!is_finite($value)) {
throw new InvalidArgumentException('Показание не является конечным числом');
}
return number_format($value, 1, '.', '');
}
foreach ([21.25, INF, NAN] as $value) {
try {
echo sensorValue($value), "\n";
} catch (InvalidArgumentException $error) {
echo $error->getMessage(), "\n";
}
}
Ожидаемый результат
21.3
Показание не является конечным числом
Показание не является конечным числом
INF и NAN — специальные float-значения. NAN нельзя надёжно искать обычным сравнением с самим собой; используйте is_nan, а для допустимого конечного числа — is_finite. Перед записью в JSON отдельно проверяйте ограничения формата. Деньги с фиксированными младшими единицами храните int или используйте согласованную десятичную арифметику.
<?php
$expected = 1000000.0;
$measured = 1000000.2;
$absoluteTolerance = 0.01;
$relativeTolerance = 0.000001;
$allowedDifference = max($absoluteTolerance, abs($expected) * $relativeTolerance);
echo 'Разрешённое отклонение: ', number_format($allowedDifference, 2, '.', ''), "\n";
echo 'Полученное отклонение: ', number_format(abs($measured - $expected), 2, '.', ''), "\n";
if (abs($measured - $expected) <= $allowedDifference) {
echo "Показание принято по согласованному допуску\n";
}
Ожидаемый результат
Разрешённое отклонение: 1.00
Полученное отклонение: 0.20
Показание принято по согласованному допуску
Относительная часть равна1000000×0.000001=1. Max выбирает большее из1 и абсолютного минимума0.01. Разница около0.2 укладывается в1. У ожидаемого нуля останется абсолютный допуск0.01. Это конкретный договор сравнения с опорным expected, а не универсальная замена ===: для разных задач выбирают другую формулу и проверяют конечность входов.
| Вызов sensorValue | Путь значения | Использование |
|---|---|---|
| sensorValue(21.25) | Местный value конечен; number_format(value,1,'.','') даёт строку21.3 | return21.3 → место вызова → echo21.3 |
| sensorValue(INF) / sensorValue(NAN) | is_finite даёт false, ! превращает в true | throw → catch → сообщение; строки измерения нет |
Числовая строка: можно посчитать, но сначала нужно понять смысл
Из HTML-поля приходит "12", а из CRM может прийти артикул "0012". Обе строки похожи на числа, но только первая обязана стать количеством. is_numeric проверяет числовую форму, включая экспоненту, а не бизнес-условие «целое количество от 1 до 99».
<?php
$raw = '1e2';
if (is_numeric($raw)) {
echo 'Числовая форма [', $raw, '] даёт в расчёте ', $raw + 0, "\n";
}
$raw = '12';
echo 'После поступления трёх штук: ', $raw + 3, ' шт.', "\n";
$damaged = '12units';
echo 'Явный (int) отбросил хвост и получил: ', (int) $damaged, "\n";
try {
$total = 'units' + 3;
} catch (TypeError $error) {
echo "Расчёт не выполнен: слово нельзя сложить с числом\n";
}
Ожидаемый результат
Числовая форма [1e2] даёт в расчёте 100
После поступления трёх штук: 15 шт.
Явный (int) отбросил хвост и получил: 12
Расчёт не выполнен: слово нельзя сложить с числом
1e2 означает1×10² и подходит is_numeric, но наш магазин не обязан принимать такой ввод количества. Строка12 в сложении даёт15. Явное приведение12units даёт12 без подтверждения допустимого формата; исходный damaged при этом не исправлен. Нечисловое слово вызывает TypeError, поэтому значение total в этом пути не получено.
Для количества договор уже, чем «число PHP»: от1 до99, только обычные ASCII-цифры, без нуля в начале. Ниже выражение /\A[1-9][0-9]?\z/ используется после изучения обычной строки; это отдельная проверка формата, а не определение типа int. Слеши ограничивают шаблон; \A — начало, [1-9] — одна первая цифра, [0-9]? — необязательная вторая, \z — конец. Preg_match возвращает1 при полном совпадении,0 при несовпадении или false при ошибке шаблона. Проверка !==1 отклоняет оба неуспеха; шаблон здесь постоянный и корректный.
<?php
function quantityFromText(string $text): int {
if (preg_match('/\A[1-9][0-9]?\z/', $text) !== 1) {
throw new InvalidArgumentException('Нужно целое от 1 до 99');
}
return (int) $text;
}
$unitPrice = 150;
foreach (['12', '1e2', '0012', '12units'] as $text) {
try {
$quantity = quantityFromText($text);
echo '[', $text, ']: ', $quantity, ' шт., к оплате ', $quantity * $unitPrice, ' руб.', "\n";
} catch (InvalidArgumentException $error) {
echo '[', $text, ']: заказ не рассчитан — ', $error->getMessage(), "\n";
}
}
Ожидаемый результат
[12]: 12 шт., к оплате 1800 руб.
[1e2]: заказ не рассчитан — Нужно целое от 1 до 99
[0012]: заказ не рассчитан — Нужно целое от 1 до 99
[12units]: заказ не рассчитан — Нужно целое от 1 до 99
Foreach по очереди даёт четыре текста. Только12 целиком совпадает: функция возвращает int12 в quantity, вызывающий код умножает12×150 и печатает1800. На остальных входах throw прерывает расчёт этой строки, catch показывает конкретный исходный текст и причину; следующая итерация продолжается. Внешнее поле до вызова ещё проверяют на is_string: этот пример уже задаёт четыре строки сам. Разрешение пробелов или ведущих нулей потребует явного изменения договора.
| Правило PHP для числовой строки | Граница |
|---|---|
| Целая форма | Необязательные крайние пробелы, знак +/− и цифры; в PHP8 крайние пробелы допустимы с обеих сторон. |
| Дробная форма | Десятичная точка и цифры; экспонента e/E с необязательным знаком степени. Десятичная запятая сюда не входит. |
| Числовой префикс с хвостом | В допускающем его числовом контексте берётся префикс и возникает E_WARNING в PHP8; это не корректная полная числовая строка. |
| Вообще нет числового начала | Арифметика PHP8 даёт TypeError. До8.0 поведение включало предупреждение и0; старый совет нельзя переносить механически. |
| Очень большое целое в тексте | Когда оно не помещается в int, числовой результат может стать float и потерять точность. Внешний идентификатор храните строкой. |
До PHP8 хвостовые пробелы относили строку к числовому префиксу. До PHP8 для префикса выдавался E_NOTICE, а до7.1 соответствующей диагностики не было. Проверка нового ввода должна задавать собственную политику; историческое поведение не заменяет её.
Тип переменной и объявление типа: где PHP проверяет договор
- Аргумент int3 → параметр int boxes
- Тело: boxes3 × 6 → int18
- return18 проходит выходной договор : int
- units=18 → подпись о количестве банок
Присваивание $count = 3 создаёт переменную со значением int, но не запрещает позднее присвоить ей строку. Объявление int $count в параметре функции устанавливает договор на её входе. После имени функции : int описывает выход. Сначала прочитайте устройство функции, если эти позиции пока незнакомы.
- Вызов с аргументом
- Проверка типа параметра
- Тело функции
- Проверка типа результата
<?php
declare(strict_types=1);
function boxTotal(int $boxes): int {
return $boxes * 6;
}
$units = boxTotal(3);
echo 'Три коробки содержат ', $units, ' банок', "\n";
try {
$units = boxTotal('3');
} catch (TypeError $error) {
echo "Текстовое количество отклонено до расчёта\n";
}
echo 'Последний рассчитанный итог: ', $units, ' банок', "\n";
Ожидаемый результат
Три коробки содержат 18 банок
Текстовое количество отклонено до расчёта
Последний рассчитанный итог: 18 банок
Вызов с int3 создаёт местный boxes3; тело получает18; return после проверки int отдаёт18 в units. Строка3 во втором вызове отвергается на входе из строгого файла: тело вообще не выполняется, новое присваивание units не завершается. Старое18 сохранено. Тип int ещё допускает отрицательные числа: проверка диапазона количества — отдельное правило задачи.
| Договор | Что разрешает | Когда пригодится |
|---|---|---|
?string или string|null | Строку либо отсутствие | Поиск необязательного комментария |
int|false | Один из двух типов | Числовой результат либо договорённый признак неудачи |
mixed | Любое обычное значение PHP | Вход проверки внешних данных; затем тип уточняется |
iterable | Массив либо Traversable | Обработка списка без привязки к способу загрузки |
callable | Вызываемую функцию или метод | Передать обработку в обход |
object / имя класса | Любой объект / нужный объект | Разница общего контейнера и конкретного договора |
void / never | Без полезного результата / нормального возврата вообще нет | Действие / прерывание исключением |
A&B, (A&B)|null | Одновременно два объектных договора / или null | Объект умеет и перебираться, и сообщать размер |
Ресурс имеет тип во время выполнения, но resource нельзя объявить как пользовательский тип параметра. Callable нельзя использовать как тип свойства. Пишите int и bool, а не integer и boolean. Объединения появились в 8.0, пересечения в 8.1, сочетание в DNF в 8.2. Повторяющиеся и заведомо избыточные типы, например bool|false, запрещены. Типы свойств и наследование разбираются в ООП.
null, mixed и специальные результаты без догадок
<?php
declare(strict_types=1);
function commentLabel(?string $comment): string
{
if ($comment === null) {
return 'Не запрашивали';
}
return $comment === '' ? 'Оставлено пустым' : $comment;
}
echo commentLabel(null), "\n";
echo commentLabel(''), "\n";
echo commentLabel('Позвонить'), "\n";
Ожидаемый результат
Не запрашивали
Оставлено пустым
Позвонить
?string разрешает null, но не делает аргумент необязательным. Для пропуска нужен default, например ?string $comment = null. В PHP 8.4 неявная nullable-запись string $comment = null устарела: отсутствие указывается в типе явно.
<?php
declare(strict_types=1);
function skuFromInput(mixed $raw): string
{
if (!is_string($raw) || $raw === '') {
throw new InvalidArgumentException('Нужен непустой артикул');
}
return $raw;
}
foreach (['0012', 12, null] as $raw) {
try {
echo skuFromInput($raw), "\n";
} catch (InvalidArgumentException $error) {
echo "Неверный артикул\n";
}
}
Ожидаемый результат
0012
Неверный артикул
Неверный артикул
Неизвестный тип принимается намеренно, затем сужается проверкой. mixed не объединяют с другими типами: они уже в него входят. Литеральные true, false и null можно объявлять самостоятельно начиная с PHP 8.2; они полезны для точного договора, но обычный переключатель чаще имеет тип bool.
Действие, прекращение работы и последовательность данных
Здесь сравниваются два результата выполнения: печать продолжает вызывающий код, а отказ передаёт управление обработчику. Null ниже показан намеренно как диагностическое наблюдение вызова void; он не является напечатанной строкой и не используется как статус заказа.
<?php
function showStatus(string $text): void {
echo $text, "\n";
}
$result = showStatus('Заказ 41: комплектуется');
echo 'Значение вызова для наблюдения: ';
var_dump($result);
echo "Можно напечатать следующую строку отчёта\n";
Ожидаемый результат
Заказ 41: комплектуется
Значение вызова для наблюдения: NULL
Можно напечатать следующую строку отчёта
Строка из аргумента попадает в местный text. Echo внутри сразу отправляет её в вывод. Завершение функции возвращает управление к месту вызова; наблюдаемое значение — null, а не текст статуса. Следующий echo выполняется. У void разрешён пустой return; для раннего выхода, но return с выражением запрещён.
<?php
function rejectOrder(): never {
throw new DomainException('Нет остатка');
}
try {
rejectOrder();
echo "Заказ можно выдать\n";
} catch (DomainException $error) {
echo 'Заказ отклонён: ', $error->getMessage(), "\n";
}
echo "Переходим к следующей проверке\n";
Ожидаемый результат
Заказ отклонён: Нет остатка
Переходим к следующей проверке
RejectOrder не возвращается обычным путём; выполнение перескакивает в ближайший подходящий catch. Строка о выдаче пропущена. После catch программа продолжается, что видно в последней строке. Never доступен с PHP8.1 и запрещает любой return; выход из процесса и бесконечное выполнение — другие способы не вернуться, но здесь использовано исключение.
<?php
declare(strict_types=1);
function totalUnits(iterable $counts): int
{
$total = 0;
foreach ($counts as $count) {
if (!is_int($count) || $count < 0) {
throw new InvalidArgumentException('Нужно целое количество');
}
$total += $count;
}
return $total;
}
echo totalUnits([2, 3]), "\n";
echo totalUnits(new ArrayIterator([4, 5])), "\n";
Ожидаемый результат
5
9
Iterable гарантирует возможность перебора, но не тип каждого элемента и не возможность повторного прохода. Например, генератор может быть одноразовым. Поэтому проверка count остаётся внутри расчёта. Объекты ArrayIterator и генераторы подробно объясняются в главе об итераторах.
| Вход totalUnits | Внутри функции | Возврат |
|---|---|---|
| Массив[2,3] | foreach берёт2, затем3; каждое целое и неотрицательное; total0→2→5 | int5 используется внешним echo как итог единиц |
| ArrayIterator с[4,5] | Тот же foreach получает4, затем5 из объекта-последовательности; total0→4→9 | int9 используется тем же вызывающим кодом |
Iterable — встроенное сокращение array|Traversable, а не обещание одинакового устройства этих значений. Это не автоматически countable, не тип каждого элемента и не гарантия повторного прохода. ArrayIterator в опыте хранит готовую последовательность; его устройство разбирается в связанном уроке итераторов.
Ресурс и составной объектный тип: два разных договора
<?php
declare(strict_types=1);
$stream = fopen('php://memory', 'w+b');
if ($stream === false) {
throw new RuntimeException('Не удалось открыть поток');
}
try {
var_dump(is_resource($stream));
fwrite($stream, 'SKU-17');
rewind($stream);
echo stream_get_contents($stream), "\n";
} finally {
fclose($stream);
}
var_dump(is_resource($stream));
Ожидаемый результат
bool(true)
SKU-17
bool(false)
Ресурс — выданный PHP доступ к внешнему или внутреннему механизму, например открытому потоку. Это не содержимое файла. После fclose такой доступ закрыт. Некоторые расширения в современных версиях используют специальные объекты вместо ресурсов; проверяйте контракт конкретной функции.
<?php
declare(strict_types=1);
function batchSize((Countable&Traversable)|null $batch): int
{
return $batch === null ? 0 : count($batch);
}
echo batchSize(new ArrayIterator(['A', 'B'])), "\n";
echo batchSize(null), "\n";
try {
batchSize(['A', 'B']);
} catch (TypeError $error) {
echo "Массив не является требуемым объектом\n";
}
Ожидаемый результат
2
0
Массив не является требуемым объектом
Countable и Traversable — имена интерфейсов. Знак & требует оба, |null допускает отсутствие, скобки отделяют пересечение. Это DNF-тип(PHP 8.2+). Массив можно пересчитать и обойти, но он не реализует интерфейсы объектов. Если реально нужно поддержать массив, добавьте его в договор или выберите iterable и иной способ подсчёта. Новичку достаточно вернуться к этому примеру после интерфейсов.
Переменная хранит значение с конкретным смыслом
У заказа есть идентификатор, сумма, признак оплаты и список позиций. Эти данные ведут себя по-разному. int удобен для количества и копеек, string — для артикула и телефона, bool — для решения «оплачен», null — для осмысленного отсутствия, array — для набора. Строка "00127" может быть артикулом, где нули значимы; превращать любой цифровой текст в число нельзя. Тип отвечает на вопрос «какие операции допустимы», а имя — «что означает значение в нашем сервисе».
| Значение | Смысл | Проверка |
|---|---|---|
| 0 | Нулевой остаток товара | Допустим, если контракт разрешает |
| null | Поставщик не сообщил остаток | Не подменять нулем автоматически |
| "0" | Текст из HTML-формы | Проверить и нормализовать |
| [] | Пустой список позиций | Решить, допустим ли пустой заказ |
PHP динамически типизирован: переменной можно присвоить значение другого типа. Типы аргументов и результата ограничивают границы функций. declare(strict_types=1) влияет на скалярные вызовы из данного файла; он не превращает массив $_POST в типизированные данные и не запрещает все преобразования языка. Проверка внешнего контракта остается отдельной обязанностью.
Текст формы не равен целому количеству
<?php
declare(strict_types=1);
function parseQuantity(mixed $raw): int
{
if (!is_string($raw) || preg_match('/\A[1-9][0-9]{0,2}\z/', $raw) !== 1) {
throw new InvalidArgumentException('Количество: целое от1 до999');
}
return (int) $raw;
}
foreach (['12', '0', '12товаров', ['12']] as $raw) {
try {
echo parseQuantity($raw), "\n";
} catch (InvalidArgumentException $exception) {
echo "Некорректно\n";
}
}
Ожидаемый результат
12
Некорректно
Некорректно
Некорректно
Сначала проверяется тип: пользователь может отправить quantity[]=12. Затем ограниченная грамматика и диапазон. Здесь ведущие нули намеренно запрещены контрактом. Числовое приведение происходит только после проверки, поэтому "12 товаров" не станет заказом из 12 штук.
Приблизительные числа и денежный расчет
<?php
declare(strict_types=1);
$subtotalKopecks = 19990 * 3;
$discountPercent = 10;
// Для этого магазина дробная копейка скидки отбрасывается.
$discountKopecks = intdiv($subtotalKopecks * $discountPercent, 100);
echo $subtotalKopecks - $discountKopecks, "\n";
echo ((0.1 + 0.2) === 0.3) ? "равно\n" : "не строго равно\n";
Ожидаемый результат
53973
не строго равно
Это демонстрация одной объявленной политики округления, не универсальное правило бухгалтерии. Проверьте ограничения суммы, чтобы произведение не вышло за PHP_INT_MAX. Для десятичных расчетов сложной финансовой области выбирают decimal/BCMath и согласованную точность. float уместен для измерений с допуском.
Не путать проверку присутствия и истинности
isset($row["stock"]) вернет false и при отсутствии ключа, и при значении null. array_key_exists различает эти случаи. empty объединяет еще больше состояний, включая 0 и "0". Это полезно для некоторых флагов, но опасно для остатков, кодов и платежных значений. Оператор ?? задает резервное значение при отсутствии/null; он не лечит неверную вложенную схему. Для каждого поля заранее сформулируйте: обязательное ли оно, может ли быть null, какой диапазон и что означает пустая строка.
Передача массива по значению использует механизмы PHP, позволяющие откладывать копирование до изменения; это не обещание нулевой стоимости для любой операции. Объектные переменные указывают на один объект после обычного присваивания. Эти две модели подробно сравниваются в главах об объектах и клонировании. Отладочный get_debug_type показывает фактический тип, но не заменяет бизнес-проверки.
Попробуйте сами. Поставщик прислал {"stock":0,"price":null}. Можно ли скрыть оба поля через empty?
Разбор решения
Нет. Ноль остатка означает «нет в наличии», null цены — «цена неизвестна». Оба состояния важны пользователю. Проверяйте ключ, затем тип и допустимость null, отображайте разные объяснения.
Объект и массив: похожие данные ещё не означают один тип
У массива значение читается по ключу, например $row['name']. У объекта свойство читается через стрелку: $object->name. Класс задаёт устройство объекта; new создаёт экземпляр. Объект может также выполнять методы. Для знакомства с полной моделью есть отдельная начальная глава. Здесь разбираем именно преобразование представления.
- Массив: ключ name → Чай
- Каст (object) создаёт stdClass
- Свойство name → Чай
- Вложенные значения сохраняют свои прежние типы
<?php
$row = ['name' => 'Чай', 'sizes' => ['small', 'large']];
$product = (object) $row;
echo $product->name, "\n";
var_dump(is_array($product->sizes));
$again = (object) $product;
var_dump($again === $product);
Ожидаемый результат
Чай
bool(true)
bool(true)
Получился объект встроенного класса stdClass. Ключи массива стали именами свойств, а sizes остался массивом. Каст уже существующего объекта в object не клонирует его. === подтверждает, что это тот же экземпляр, поэтому изменение его свойств видно через обе переменные.
<?php
$empty = (object) null;
var_dump(get_object_vars($empty));
$label = (object) 'SKU-12';
echo $label->scalar, "\n";
$numeric = (object) [12 => 'Двенадцать'];
echo $numeric->{'12'}, "\n";
Ожидаемый результат
array(0) {
}
SKU-12
Двенадцать
get_object_vars здесь получает публичные свойства простого stdClass. Числовое имя свойства читается через фигурные скобки и строку. Преобразование произвольного слова в объект со свойством scalar почти никогда не создаёт полезную предметную модель: лучше явно назвать необходимые данные.
Свойства внутреннего класса могут иметь особые правила, а приватные и защищённые поля требуют учёта видимости при обратном преобразовании. Не заменяйте проектирование модели цепочкой JSON encode/decode ради случайного приведения типов. Для API выбирайте заранее: массив с проверенной схемой или конкретный объект данных.
PHP проверяет имя договора, а не только похожесть методов
Два разных объекта могут иметь одинаковый метод send, но это не делает их экземплярами одного интерфейса. PHP использует номинальные типы: принадлежность задаётся объявленным классом, наследованием и implements. Интерфейс — названный договор доступных действий; класс явно сообщает, что выполняет его.
<?php
interface NoticeSender
{
public function send(string $message): void;
}
class ScreenSender implements NoticeSender
{
public function send(string $message): void
{
echo $message, "\n";
}
}
class UnrelatedSender
{
public function send(string $message): void
{
echo $message, "\n";
}
}
function notify(NoticeSender $sender): void
{
$sender->send('Заказ готов');
}
notify(new ScreenSender());
try {
notify(new UnrelatedSender());
} catch (TypeError $error) {
echo "Нет объявленного договора NoticeSender\n";
}
Ожидаемый результат
Заказ готов
Нет объявленного договора NoticeSender
public означает доступность метода вызывающему коду, : void — отсутствие полезного результата. Одинаковое тело send у двух классов не заменило implements. Это помогает связать части сервиса через явный договор, который проверяется PHP.
Соответствие объявлений методов при наследовании проверяется при загрузке объявления класса; значения аргументов и результатов проверяются при выполнении вызовов. Допустимое расширение входа и сужение результата называется контравариантностью и ковариантностью; практическая сторона разобрана в интерфейсах и в наследовании.
self, parent и static: тип относительно места в иерархии
Когда базовый класс расширяют, абсолютное имя типа иногда связывает код с конкретным названием сильнее, чем нужно. Относительные типы описывают связь с текущей иерархией. self означает класс, где написано объявление; parent — его прямого родителя. Эти два имени допустимы только внутри класса. static разрешён как тип результата и означает класс, через который метод фактически вызвали.
self→ значение должно быть экземпляром класса объявления или его потомкаparent→ значение должно быть экземпляром родительского класса или его потомкаstaticв результате → вернуть экземпляр фактически вызванного класса- PHP проверяет переданное или возвращённое значение и при нарушении выдаёт TypeError
<?php
class CatalogItem
{
public function __construct(public string $name) {}
}
class Product extends CatalogItem
{
public function productLabel(self $item): string
{
return 'self: ' . $item->name;
}
public function catalogLabel(parent $item): string
{
return 'parent: ' . $item->name;
}
}
$product = new Product('Чай');
$category = new CatalogItem('Категория напитков');
echo $product->productLabel($product), "\n";
echo $product->catalogLabel($category), "\n";
echo $product->catalogLabel($product), "\n";
try {
$product->productLabel($category);
} catch (TypeError $error) {
echo "Категория не является Product\n";
}
Ожидаемый результат
self: Чай
parent: Категория напитков
parent: Чай
Категория не является Product
Внутри Product имя self обозначает Product, поэтому обычный CatalogItem отклонён. parent обозначает CatalogItem; экземпляр Product тоже подходит, потому что наследуется от CatalogItem. Имена параметров item локальны для каждого вызова, стрелка читает публичное свойство name.
<?php
class ExportJob
{
public static function fromDefinition(): self
{
return new self();
}
public static function fromCalledClass(): static
{
return new static();
}
}
class CsvExportJob extends ExportJob {}
echo get_class(CsvExportJob::fromDefinition()), "\n";
echo get_class(CsvExportJob::fromCalledClass()), "\n";
Ожидаемый результат
ExportJob
CsvExportJob
Метод унаследован и оба раза вызван через CsvExportJob. new self создаёт класс места объявления — ExportJob. new static учитывает позднее статическое связывание и создаёт CsvExportJob; тип результата static обещает это вызывающему коду. Пример намеренно использует пустые конструкторы: если потомок потребует другие обязательные аргументы, универсальная фабрика new static() перестанет быть корректной.
Относительные типы не заменяют интерфейс для независимых реализаций. Если складской и платёжный клиенты должны выполнять общий договор, используйте интерфейс. Если нужна фабрика, возвращающая потомка, изучите позднее статическое связывание и закройте возможность несовместимого конструктора. Для обычной функции вне класса укажите конкретный класс или интерфейс.
Ресурс: две переменные могут держать один открытый поток
Нужен временный текст отчёта, который несколько участков кода читают через один открытый поток. Поток — последовательность байтов; ресурс здесь хранит доступ к нему. php://memory создаёт поток в памяти текущего процесса, а режим w+b разрешает запись и чтение байтов. Он не создаёт сохраняемый файл на диске.
Если открыть файл, PHP возвращает специальное значение — handle ресурса. Его передают операциям чтения и закрытия. Нельзя получить рабочий файл, приведя число к resource: нужен результат конкретной функции открытия. Некоторые современные расширения возвращают специальные объекты вместо старых ресурсов; это определяется API и версией.
<?php
$stream = fopen('php://memory', 'w+b');
if ($stream === false) {
throw new RuntimeException('Не удалось открыть поток');
}
fwrite($stream, "Заказ 12: упакован\n");
rewind($stream);
$sameStream = $stream;
$report = stream_get_contents($sameStream);
if ($report === false) {
throw new RuntimeException('Не удалось прочитать отчёт');
}
echo $report;
fclose($stream);
if (!is_resource($sameStream)) {
echo "Повторное чтение запрещено: доступ уже закрыт\n";
}
echo 'Оставшееся значение: ', get_debug_type($sameStream), "\n";
Ожидаемый результат
Заказ 12: упакован
Повторное чтение запрещено: доступ уже закрыт
Оставшееся значение: resource (closed)
Fwrite получает ресурс и строку и возвращает число записанных байтов или false; здесь маленький текст пишется в локальную память. Rewind возвращает указатель к началу; stream_get_contents возвращает оставшиеся байты как строку или false. Report получает текст, echo его выводит. SameStream не открыл второй поток: fclose закрывает общий доступ. На диске отчёт не сохраняется. В производственном потоке проверяют успех записи/rewind и обеспечивают закрытие через finally.
PHP освобождает обычный ресурс, когда больше нет удерживающих его ссылок, но это не заменяет своевременное закрытие нужного вам файла. Явный finally полезен, чтобы не держать файл до конца длинного задания. Постоянные соединения отдельных расширений имеют особый жизненный цикл. Тип параметра resource в пользовательской функции объявить нельзя: проверяют значение и договор функции внутри, либо используют собственный объект-обёртку.
callable: передать действие вместо немедленного результата
- Вызов передаёт строку с пробелами и значение trim(...), которое можно вызвать позже.
- Местный code хранит строку, местный normalize — действие. Callable не является уже готовым результатом.
- Выражение normalize(code) вызывает переданное действие с этим текстом и получает tea-12.
- Return отдаёт tea-12 в prepareCode(...); наружный echo использует именно эту строку.
Иногда одному обработчику нужно отдать правило преобразования: например, перед выгрузкой нормализовать артикул. Если записать strtoupper('tea'), функция выполнится сейчас и получится строка TEA. Если передать вызываемое значение, другой участок выполнит действие позже со своими данными. Такой параметр часто называют callback — переданный обработчик.
<?php
$normalize = strtoupper(...);
echo get_debug_type($normalize), "\n";
echo $normalize('tea-12'), "\n";
Ожидаемый результат
Closure
TEA-12
Многоточие внутри этих скобок — специальный синтаксис создания first-class callable в PHP 8.1+, а не пропущенный фрагмент учебника и не распаковка массива. Первая строка не вызывает strtoupper для строки; она создаёт Closure. Вторая форма скобок с tea-12 уже вызывает сохранённое действие.
<?php
function prepareCode(string $code, callable $normalize): string
{
return $normalize($code);
}
echo prepareCode(' tea-12 ', trim(...)), "\n";
echo prepareCode('tea-12', strtoupper(...)), "\n";
Ожидаемый результат
tea-12
TEA-12
callable гарантирует возможность вызова в данном контексте, но не описывает число, типы аргументов и смысл результата для каждого обработчика. Наш договор дополнительно требует действие string → string. Если нужно одновременно обрезать пробелы и изменить регистр, передайте функцию, которая явно делает оба шага.
Параметр callable не разрешён как тип свойства класса. Для хранения уже подготовленного обработчика подходит Closure; преобразование из обычного callable выполняет Closure::fromCallable. Проверка is_callable не выполняет действие и не доказывает его безопасность. Не берите имя исполняемой функции напрямую из параметра запроса.
Какие формы callable встречаются в библиотеках
| Форма | Что передано |
|---|---|
'trim' | Имя доступной функции; в пространстве имён полагаться на автоматическое добавление текущего namespace нельзя. |
[Formatter::class, 'clean'] | Имя класса и доступный статический метод. |
[$formatter, 'clean'] | Конкретный объект и его доступный метод. |
$object с __invoke | Объект, специально умеющий вызываться как функция. |
function (...) {...}, fn(...) => ... | Анонимная или стрелочная функция; результатом создания является Closure. |
$object->method(...) | Closure, привязанный к допустимому методу в момент получения; PHP 8.1+. |
<?php
class CodePrefix
{
public function add(string $code): string
{
return 'SHOP-' . $code;
}
public function __invoke(string $code): string
{
return $this->add($code);
}
}
$prefix = new CodePrefix();
$method = [$prefix, 'add'];
echo $method('12'), "\n";
echo $prefix('13'), "\n";
var_dump(is_callable([CodePrefix::class, 'add']));
Ожидаемый результат
SHOP-12
SHOP-13
bool(false)
add — нестатический метод: ему нужен экземпляр. Начиная с PHP 8 нельзя выдавать такую пару имя класса + нестатический метод за допустимый статический callback. __invoke делает сам объект вызываемым; $this обозначает текущий экземпляр.
Конструкции языка echo, isset, empty, unset, list и подобные не являются обычными функциями для такого вызова. Когда нужно обернуть их в обработчик, пишут собственную функцию с понятным договором. Контекстно-зависимые старые записи вроде 'self::method' как callable устарели с PHP 8.2; используйте конкретный класс либо Closure, полученный в разрешённой области. Видимость методов продолжает действовать.
true и false как точные типы результата
Точный тип true подходит, когда вызывающий код хочет явное подтверждение, а не произвольный bool. Ниже функция проверяет только учебный признак готовности: она не создаёт файл и не отправляет заказ. Return true — подтверждение её договора, исключение — другой путь завершения.
Обычный bool допускает два значения. Иногда контракт точнее: действие либо выполнено и возвращает true, либо прерывается исключением. Тогда тип true помогает не вернуть false случайно. Это тип с единственным значением, а не паттерн «Singleton-объект».
<?php
function confirmPrepared(bool $prepared): true
{
if (!$prepared) {
throw new LogicException('Сначала подготовьте данные');
}
return true;
}
$confirmed = confirmPrepared(true);
if ($confirmed) {
echo "Данные готовы: можно показывать кнопку печати\n";
}
try {
confirmPrepared(false);
} catch (LogicException $error) {
echo 'Кнопка недоступна: ', $error->getMessage(), "\n";
}
Ожидаемый результат
Данные готовы: можно показывать кнопку печати
Кнопка недоступна: Сначала подготовьте данные
Первый вызов возвращает true в confirmed, и условие выбирает дальнейшее сообщение. Второй вызов останавливается на throw и до return не доходит; catch выводит причину. Тип true разрешён с PHP 8.2; одиночный false тоже с 8.2, false в union — с 8.0. True|false записывают как bool. Если подтверждение не требуется, часто достаточно void.
Нельзя объявить произвольный литеральный тип 'paid' как собственный singleton-тип. Для конечного набора статусов есть перечисления enum. Отдельный тип null также допускает одно значение; его не следует путать с void, который описывает отсутствие полезного возвращаемого результата.
Неявное преобразование при union-типе не идёт слева направо
| Вход в старый API без strict_types | Выбранный тип | Почему это имеет практическое значение |
|---|---|---|
| Код "12" в int|string | string: уже разрешённый исходный тип | Можно сохранить внешний код текстом. |
| "12" в int|bool | int 12 | Допускается числовое преобразование. |
| "no" в int|bool | bool true | Непустая строка не является парсером слова «нет». |
| "12.5" в int|float | float 12.5 | Дробную часть сохраняет выбор float. |
В слабом режиме сначала сохраняется допустимый исходный тип. Если требуется скалярное преобразование, PHP выбирает из поддерживаемых типов по правилам, а не по порядку записи через |. Для подходящих скалярных вариантов общий приоритет: int, затем float, затем string, затем bool; у числовых строк при сочетании int и float учитывается форма числа. Это ещё одна причина разбирать внешние значения явно.
<?php
function orderIdLabel(int|string $value): string {
return 'Заказ ' . $value;
}
function legacyRowLimit(int|bool $value): int {
return is_int($value) ? $value : ($value ? 100 : 0);
}
function packedWeight(int|float $kg): float {
return $kg + 0.2;
}
echo orderIdLabel('0012'), "\n";
echo 'Лимит из текста 12: ', legacyRowLimit('12'), ' строк', "\n";
echo 'Лимит из текста no: ', legacyRowLimit('no'), ' строк', "\n";
echo 'Посылка: ', packedWeight('12.5'), ' кг', "\n";
Ожидаемый результат
Заказ 0012
Лимит из текста 12: 12 строк
Лимит из текста no: 100 строк
Посылка: 12.7 кг
Этот файл намеренно нестрогий. У orderIdLabel исходный string уже разрешён: ведущие нули остаются в подписи. У legacyRowLimit строка12 преобразуется в int12, и функция возвращает этот лимит. Слово no не годится для int, зато превращается в booltrue: старый договор трактует его как100 строк, хотя человек мог ожидать запрет. У packedWeight строка12.5 выбирает float, тело добавляет упаковку0.2 и возвращает12.7 в echo. Такие перегруженные договоры показаны для чтения старого API; новый текстовый ввод лучше разбирать явно.
Array, object и ресурс не превращаются в подходящий скаляр просто потому, что в union есть несколько вариантов. Возможность строкового представления отдельных объектов также имеет собственный договор. Значения null и числовые строки требуют проверки, особенно при устаревших вызовах внутренних функций. Точная сигнатура конкретной функции важнее привычки к автоматическим преобразованиям.
strict_types относится к месту вызова, а не к проекту целиком
Каждый из двух ZIP ниже — отдельный законченный проект из example.php и price.php. Распакуйте в разные папки и запустите php example.php. require загружает объявления второго файла; __DIR__ даёт папку текущего файла. PackagePrice получает число коробок, умножает его на 150 и возвращает цену, которую использует внешний echo. Критическая разница опытов — strict_types именно в файле с вызовом.
Объявление declare(strict_types=1); в библиотечном файле не делает строгими все сторонние вызовы её функций. Для проверки скалярного аргумента важен вызывающий файл. Для скалярного результата — файл объявления возвращающей функции. Это можно наблюдать без сервера и базы.
<?php
require __DIR__ . '/price.php';
echo packagePrice('2'), "\n";
Ожидаемый результат
300
price.php содержит strict_types=1 и объявление int $boxes, но место вызова в example.php работает в слабом режиме. Строка 2 допускает преобразование в int. В ZIP лежат оба файла.
<?php
declare(strict_types=1);
require __DIR__ . '/price.php';
try {
echo packagePrice('2'), "\n";
} catch (TypeError $error) {
echo "Строка вместо int отклонена\n";
}
Ожидаемый результат
Строка вместо int отклонена
Теперь strict_types=1 стоит именно перед вызовом. Библиотека при этом может не объявлять строгий режим. В обоих случаях правила самой арифметики и явных кастов не превратились в другую разновидность PHP.
Даже строгий скалярный договор float допускает int. Strict_types также не проверяет автоматически содержимое каждого элемента array и не превращает внутренние callback-вызовы расширений в обычные вызовы из вашего файла. Допустимость связанного результата и поведение конкретного callback отдельно исследуются на его API.
У возвращаемого значения место строгой проверки другое: файл объявления самой функции. Следующий ZIP содержит три файла; даже при строгом example.php функция из weak-return.php может допустимо преобразовать свой строковый результат. Такой эксперимент помогает обнаружить несоответствие библиотечного договора и её тела.
<?php
declare(strict_types=1);
require __DIR__ . '/weak-return.php';
require __DIR__ . '/strict-return.php';
$total = weakTotal();
echo 'Слабая библиотека: ', $total, ' руб., тип ', get_debug_type($total), "\n";
try {
$total = strictTotal();
} catch (TypeError $error) {
echo "Строгая библиотека: строка не прошла договор результата int\n";
}
echo 'Последний пригодный итог: ', $total, ' руб.', "\n";
Ожидаемый результат
Слабая библиотека: 300 руб., тип int
Строгая библиотека: строка не прошла договор результата int
Последний пригодный итог: 300 руб.
В обеих функциях литерал300 намеренно написан в кавычках. В weakTotal возвращаемая числовая строка допускает преобразование в int300; вызывающий код использует его как сумму. StrictTotal объявлена в строгом файле и выдаёт TypeError до возврата в присваивание. Тело нужно исправить на числовой расчёт или правильный договор; catch здесь позволяет увидеть оба пути. Ни один из файлов не получает случайное текущее состояние окружения.
<?php
declare(strict_types=1);
function orderTotal(int $goods): int {
$deliveryText = '50';
return $goods + $deliveryText;
}
$total = orderTotal(300);
echo 'Товары и доставка: ', $total, ' руб.', "\n";
Ожидаемый результат
Товары и доставка: 350 руб.
На входе int300 соответствует параметру. Затем + использует числовой контекст: строка50 участвует как число, получается int350. Выходной договор int тоже выполнен. Strict_types не запрещает это преобразование внутри выражения. Для внешнего deliveryText нужна явная проверка до расчёта; в опыте литерал заранее известен.
Похожее ожидание описано в вопросе о сложении строки внутри строгой функции: автор ожидал запрета любой конверсии. Следите отдельно за входом функции, её выражениями и выходом; у них разные правила.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
get_debug_typeПолучить понятное имя типа для сообщения об ошибке или локальной диагностики.
get_debug_type(mixed $value): stringПолучить понятное имя типа для сообщения об ошибке или локальной диагностики.
| Часть контракта | Что это означает |
|---|---|
| value | Значение любого типа. |
| Возврат | Строка: bool/int/float/string/array/null, класс объекта либо описание ресурса. Не возвращает само значение. |
<?php
echo get_debug_type(12), "
";
echo get_debug_type(12.0), "
";
echo get_debug_type(null);
Ожидаемый результат
int
float
nullФункция возвращает строку; echo отдельно показывает её. В отличие от gettype здесь имена int/float/null согласованы с привычной записью типов.
<?php
$payload = ["sku" => ["TEA"]];
$sku = $payload["sku"] ?? null;
if (!is_string($sku)) {
echo "sku: ожидалась string, получена ", get_debug_type($sku);
}
Ожидаемый результат
sku: ожидалась string, получена arrayСообщение не выводит значение поля и токены запроса. Для обработчика HTTP выберите корректный статус/формат ошибки; этот пример показывает только локальную диагностику.
<?php
$stream = fopen("php://memory", "r+");
echo get_debug_type($stream), "
";
fclose($stream);
echo get_debug_type($stream);
Ожидаемый результат
resource (stream)
resource (closed)Переменная с закрытым дескриптором ещё существует, но поток уже нельзя использовать. Для проверки работоспособного ресурса служит is_resource.
Для именованного объекта возвращается полное имя класса; для анонимного — обозначение с @anonymous, которое не следует принимать за стабильный идентификатор объекта. Строка "12" даст string. Для ветвления по типу применяйте соответствующую is_*-проверку или instanceof, не сравнение диагностических сообщений.
Рядом: gettype, var_dump, is_resource. К основам: переменная и присваивание; тип значения.
Ещё один прикладной разбор. После основных правил можно перейти к следующему примеру.
Возвращает понятное имя скалярного типа или класса объекта. Помогает объяснить ошибку контракта, не печатая само содержимое значения.
<?php
declare(strict_types=1);
$quantity = ['12'];
echo 'Ожидалась строка, получен ', get_debug_type($quantity), "\n";
Ожидаемый результат
Ожидалась строка, получен array
Массив может прийти из quantity[]=12. В журнале достаточно пути поля и типа; не печатайте целиком POST с паролями.
<?php
declare(strict_types=1);
$result = new DateTimeImmutable('2026-10-12');
echo get_debug_type($result), "\n";
Ожидаемый результат
DateTimeImmutable
Диагностика показывает конкретный класс. Для принятия значения используйте DateTimeInterface или нужный контракт, а не сравнение имени строкой.
Сравните. var_dump показывает также значения и может раскрыть секреты; instanceof проверяет объектный контракт.
is_arrayПроверить, что получено именно PHP-значение array.
is_array(mixed $value): boolПроверить, что получено именно PHP-значение array.
| Часть контракта | Что это означает |
|---|---|
| value: mixed | Уже полученное значение любого PHP-типа. Чтение неизвестной переменной происходит до вызова; функция не скрывает такое предупреждение. |
| Возврат: bool | [] и непустой массив дают true; объект ArrayAccess и JSON-текст — false. Схему элементов проверяют отдельно. |
| Изменение данных | Не преобразует и не заменяет value. true/false — ответ на конкретную проверку. |
<?php
$value = ["tea", "bread"];
var_dump(is_array($value));
var_dump(is_array("[1,2]"));
Ожидаемый результат
bool(true)
bool(false)
Квадратные скобки создают массив, кавычки — строку. Похожее написание внутри строки не создаёт массив автоматически.
Следующая ступень использует условия и циклы. Проверяем вход до действия; исключение сообщает о нарушенном договоре, а не исправляет данные автоматически.
<?php
$payload = ['items' => [['sku' => 'TEA-01', 'stock' => 0], ['sku' => 'COF-02', 'stock' => 5]]];
$items = $payload['items'] ?? null;
if (!is_array($items)) {
throw new UnexpectedValueException('Нет массива items');
}
foreach ($items as $item) {
if (!is_array($item) || !is_string($item['sku'] ?? null)
|| !is_int($item['stock'] ?? null) || $item['stock'] < 0) {
throw new UnexpectedValueException('Повреждённая позиция');
}
echo $item['sku'], ': доступно ', $item['stock'], ' шт.', "\n";
}
Ожидаемый результат
TEA-01: доступно 0 шт.
COF-02: доступно 5 шт.
Is_array(items) возвращает true, поэтому !true ложно и первый throw не выполняется. Каждая item проверяется отдельно: общий array не обещает форму детей. После короткого замыкания || читать stock безопасно только в подходящей ветви. В отчёт попадает и полезный ноль, и пять штук. Здесь payload уже является PHP-массивом; строка JSON ещё не прошла бы is_array.
Рядом: is_iterable, is_object. К основам: переменная и присваивание; тип значения.
Ещё один прикладной разбор. После основных правил можно перейти к следующему примеру.
Возвращает bool без преобразования входа. Первый шаг перед обращением к вложенному JSON или списку формы.
<?php
declare(strict_types=1);
$payload = ['items' => 'unexpected'];
echo is_array($payload['items'] ?? null) ? "Можно проверять элементы\n" : "Неверный типitems\n";
Ожидаемый результат
Неверный типitems
После этой границы проверяют ключи и типы каждой записи. Не приводите любую строку к(array): это скроет нарушение API.
<?php
declare(strict_types=1);
$value = json_decode('{"id":7}', false, 16, JSON_THROW_ON_ERROR);
echo is_array($value) ? "array\n" : "object\n";
Ожидаемый результат
object
Параметр associative определяет представление JSON-объектов. Выберите одно представление в адаптере и не смешивайте $x->id и $x["id"] произвольно.
Сравните. array_is_list проверяет форму ключей; is_iterable принимает array или Traversable.
is_stringПроверить строковый тип без приведения массивов и чисел.
is_string(mixed $value): boolКак использовать результат. Вызов is_string(raw) решает, разрешено ли передать raw строковым операциям. Он не возвращает артикул. После проверки trim возвращает новую строку sku; именно sku затем выводится. Пустота, длина и существование товара — самостоятельные проверки.
Проверить строковый тип без приведения массивов и чисел.
| Часть контракта | Что это означает |
|---|---|
| value: mixed | Уже полученное значение любого PHP-типа. Чтение неизвестной переменной происходит до вызова; функция не скрывает такое предупреждение. |
| Возврат: bool | Пустая строка, "0" и бинарная строка дают true. Кодировка UTF-8, длина и формат проверяются отдельно. Объект Stringable не становится строкой до явного преобразования. |
| Изменение данных | Не преобразует и не заменяет value. true/false — ответ на конкретную проверку. |
<?php
var_dump(is_string("0"));
var_dump(is_string(""));
var_dump(is_string(0));
Ожидаемый результат
bool(true)
bool(true)
bool(false)
Тип string не обещает наличие видимого текста. Даже пустая строка остаётся строкой.
Следующая ступень использует условия и циклы. Проверяем вход до действия; исключение сообщает о нарушенном договоре, а не исправляет данные автоматически.
<?php
$fields = ["sku" => " TEA-2 "];
$raw = $fields["sku"] ?? null;
if (!is_string($raw)) {
throw new InvalidArgumentException("Артикул должен быть строкой");
}
$sku = trim($raw);
if ($sku === "" || strlen($sku) > 40) {
throw new InvalidArgumentException("Пустой или слишком длинный артикул");
}
echo $sku;
Ожидаемый результат
TEA-2Сначала тип, затем trim и длина. strlen здесь измеряет байты; предел условного ASCII-артикула — 40. Проверка существования артикула и разрешённых символов является отдельным правилом магазина, а не свойством is_string.
Рядом: strval, is_scalar, empty. К основам: переменная и присваивание; тип значения.
Ещё один прикладной разбор. После основных правил можно перейти к следующему примеру.
Возвращает true только для строки. Полезно до trim, регулярного выражения и проверки длины данных формы.
<?php
declare(strict_types=1);
$input = ['email' => ['a@example.test']];
echo is_string($input['email'] ?? null) ? "Проверить адрес\n" : "Ожидается одно значение\n";
Ожидаемый результат
Ожидается одно значение
Массив нужно отклонить, а не взять случайно первый элемент. Затем проверяются длина, формат и бизнес-ограничения адреса.
<?php
declare(strict_types=1);
$sku = '00127';
if (!is_string($sku)) {
throw new UnexpectedValueException('SKU должен быть строкой');
}
echo $sku, "\n";
Ожидаемый результат
00127
Цифровой текст остается идентификатором. Приведение к int уничтожило бы нули и могло склеить разные товары.
Сравните. Для грамматики используйте preg_match после проверки типа; для вывода HTML — htmlspecialchars.
is_intПроверить фактический целочисленный тип.
is_int(mixed $value): boolПроверить фактический целочисленный тип.
| Часть контракта | Что это означает |
|---|---|
| value: mixed | Уже полученное значение любого PHP-типа. Чтение неизвестной переменной происходит до вызова; функция не скрывает такое предупреждение. |
| Возврат: bool | Целое 4 даёт true, строка "4" и float 4.0 — false. Входные поля HTTP обычно являются строками или массивами, а не int. |
| Изменение данных | Не преобразует и не заменяет value. true/false — ответ на конкретную проверку. |
<?php
var_dump(is_int(4));
var_dump(is_int("4"));
var_dump(is_int(4.0));
Ожидаемый результат
bool(true)
bool(false)
bool(false)
Числовой вид строки ещё не меняет тип. Проверка ничего не присваивает обратно переменной.
Следующая ступень использует условия и циклы. Проверяем вход до действия; исключение сообщает о нарушенном договоре, а не исправляет данные автоматически.
<?php
$order = ['sku' => 'TEA-01', 'quantity' => 3];
$quantity = $order['quantity'] ?? null;
if (!is_int($quantity) || $quantity < 1 || $quantity > 20) {
throw new InvalidArgumentException('Количество должно быть целым от 1 до 20');
}
$unitPriceKopecks = 12000;
$lineTotalKopecks = $quantity * $unitPriceKopecks;
echo 'Товар: ', $order['sku'], "\n";
echo 'Количество: ', $quantity, ' уп.', "\n";
echo 'Сумма строки: ', $lineTotalKopecks, ' коп.', "\n";
Ожидаемый результат
Товар: TEA-01
Количество: 3 уп.
Сумма строки: 36000 коп.
Is_int получает 3 и возвращает true без преобразования. Затем проверяется отдельное правило диапазона. Получившееся количество участвует в произведении 3 × 12000; сумма 36000 сохраняется для следующего расчёта. Строка "3" и float 3.0 нарушили бы этот внутренний договор; для текста формы нужен предварительный этап разбора.
Рядом: intval, is_numeric, is_float. К основам: переменная и присваивание; тип значения.
Ещё один прикладной разбор. После основных правил можно перейти к следующему примеру.
Возвращает true для int. Подходит для уже декодированного JSON-контракта с целым значением или внутренних данных после нормализации.
<?php
declare(strict_types=1);
$data = json_decode('{"stock":0}', true, 16, JSON_THROW_ON_ERROR);
echo is_int($data['stock']) && $data['stock'] >= 0 ? "Остаток принят\n" : "Ошибка\n";
Ожидаемый результат
Остаток принят
Ноль считается корректным. Поставщик со строковыми числами требует явного адаптера, а не случайного ослабления всех проверок.
<?php
declare(strict_types=1);
$value = 5 / 2;
echo is_int($value) ? "int\n" : "неint\n";
Ожидаемый результат
неint
Если нужна целая часть, используйте intdiv с объявленной политикой остатка. Приведение не должно незаметно отбрасывать важную дробную часть.
Сравните. Для формы — валидатор целого с диапазоном; is_numeric шире и принимает экспоненциальную запись.
is_numericПроверить число или строку, соответствующую числовому синтаксису PHP.
is_numeric(mixed $value): boolПроверить число или строку, соответствующую числовому синтаксису PHP.
| Часть контракта | Что это означает |
|---|---|
| value: mixed | Уже полученное значение любого PHP-типа. Чтение неизвестной переменной происходит до вызова; функция не скрывает такое предупреждение. |
| Возврат: bool | Принимает int, float и числовые строки, включая экспоненту. Не гарантирует целое, конечность, положительность или допустимый денежный формат. В PHP 8 конечный обычный пробел допускается. |
| Изменение данных | Не преобразует и не заменяет value. true/false — ответ на конкретную проверку. |
<?php
var_dump(is_numeric("12"));
var_dump(is_numeric("1e2"));
var_dump(is_numeric("12items"));
var_dump(is_numeric("12 "));
Ожидаемый результат
bool(true)
bool(true)
bool(false)
bool(true)
1e2 — числовая запись 100. Если договор поля разрешает только десятичные цифры без пробелов, is_numeric слишком широк.
Следующая ступень использует условия и циклы. Проверяем вход до действия; исключение сообщает о нарушенном договоре, а не исправляет данные автоматически.
<?php
$raw = '1.25';
if (!is_string($raw) || !is_numeric($raw)) {
throw new InvalidArgumentException('Нужна числовая строка');
}
$weightKg = (float) $raw;
if (!is_finite($weightKg) || $weightKg <= 0 || $weightKg > 50) {
throw new InvalidArgumentException('Вес вне диапазона');
}
$deliveryRubles = $weightKg <= 2 ? 200 : 350;
echo 'Масса посылки: ', $weightKg, ' кг', "\n";
echo 'Стоимость доставки: ', $deliveryRubles, ' руб.', "\n";
Ожидаемый результат
Масса посылки: 1.25 кг
Стоимость доставки: 200 руб.
Is_numeric возвращает bool о числовой грамматике PHP; само значение raw не меняется. После true явный float-каст создаёт weightKg. Конечность и диапазон проверяются отдельно, потом 1.25 <= 2 выбирает тариф 200. Договор допускает экспоненту и пробелы, разрешённые числовой грамматикой PHP; запятая или единицы «кг» не принимаются. Для более строгой формы нужен другой разбор.
Рядом: is_int, is_float, floatval, intval. К основам: переменная и присваивание; тип значения.
Ещё один прикладной разбор. После основных правил можно перейти к следующему примеру.
Принимает int, float и числовые строки в синтаксисе PHP, включая некоторые формы, неприемлемые для номера или денежного поля.
<?php
declare(strict_types=1);
$raw = '1.25e2';
if (!is_numeric($raw)) {
throw new InvalidArgumentException('Не число');
}
$reading = (float) $raw;
echo $reading, "\n";
Ожидаемый результат
125
Экспоненциальная запись уместна для измерений. После преобразования добавьте is_finite и физический диапазон, чтобы не принять бесконечность или невозможное значение.
<?php
declare(strict_types=1);
$raw = '1e3';
echo is_numeric($raw) ? "Числовая запись\n" : "Нет\n";
echo preg_match('/\A[0-9]+\z/', $raw) === 1 ? "Целые цифры\n" : "Не формат формы\n";
Ожидаемый результат
Числовая запись
Не формат формы
Одна проверка отвечает на синтаксис PHP, другая — на конкретный контракт поля. Сначала назовите допустимый формат, потом выбирайте функцию.
Сравните. Для количества задайте грамматику целого и диапазон; для десятичных сумм — согласованный формат и точность.
intvalПолучить целое значение или разобрать строку в заданной системе счисления.
intval(mixed $value, int $base = 10): intПолучить целое значение или разобрать строку в заданной системе счисления.
| Часть контракта | Что это означает |
|---|---|
| value | Исходное число/проверенная строка; не изменяется. |
| base = 10 | Основание для строки; 0 означает распознавание префикса. |
| Возврат | int. Преобразование с потерей части входа возможно, отдельного однозначного маркера неверного текста нет. |
<?php
var_dump(intval(3.9));
var_dump(intval(-3.9));
var_dump(intval("12items"));
var_dump(intval("items12"));
Ожидаемый результат
int(3)
int(-3)
int(12)
int(0)
Дробная часть отсекается к нулю. Строка с допустимым началом может стать числом; ноль может быть как полезным результатом, так и следствием неподходящего входа.
<?php
echo intval("101", 2), "
";
echo intval("0x20", 0), "
";
echo intval("010", 0), "
";
echo intval("010", 10), "
";
echo intval(101, 2), "
";
echo intval("1e2", 10), "
";
echo intval("1e2", 0);
Ожидаемый результат
5
32
8
10
101
100
1base=0 включает распознавание префиксов: 0x/0X — 16, 0b/0B — 2, начальный 0 — 8, иначе 10. Для числового аргумента base не переинтерпретирует его цифры. Артикулы с ведущими нулями обычно вообще не следует превращать в числа.
<?php
$rawBits = '101';
$allowedCharacters = strspn($rawBits, '01');
if ($rawBits === '' || strlen($rawBits) > 8 || $allowedCharacters !== strlen($rawBits)) {
throw new InvalidArgumentException('Нужны от 1 до 8 двоичных цифр');
}
$flags = intval($rawBits, 2);
echo 'Двоичный текст: ', $rawBits, "\n";
echo 'Числовая маска: ', $flags, "\n";
echo 'Флаг 1: ', ($flags & 1) !== 0 ? 'включён' : 'выключен', "\n";
echo 'Флаг 2: ', ($flags & 2) !== 0 ? 'включён' : 'выключен', "\n";
echo 'Флаг 4: ', ($flags & 4) !== 0 ? 'включён' : 'выключен', "\n";
Ожидаемый результат
Двоичный текст: 101
Числовая маска: 5
Флаг 1: включён
Флаг 2: выключен
Флаг 4: включён
Strspn возвращает длину начального участка только из 0 и 1; равенство с байтовой strlen подтверждает весь ASCII-текст. Ограничение восьмью цифрами даёт максимум 255 на всех поддерживаемых int-платформах. Intval(rawBits, 2) получает основание 2, возвращает 5; побитовое & проверяет отдельные биты. При аргументе int основание не переинтерпретировало бы цифры, поэтому строковый договор здесь важен.
Основание задавайте как 0 или 2–36. При base=10 экспонента вроде "1e2" разбирается как число 100. Не переносите это правило на другие основания: base=0 в этом случае даёт 1, а base=16 воспринимает e как шестнадцатеричную цифру и даёт 482. Диапазон int зависит от платформы: ориентируйтесь на PHP_INT_MIN/MAX, храните большие идентификаторы строками. Массивы превращаются в 0/1, обычный объект вызывает E_WARNING; это не надёжная ошибка валидации. Не используйте int для округления денег после вычислений float.
Рядом: is_int, is_numeric, floatval. К основам: переменная и присваивание; тип значения.
Ещё один прикладной разбор. После основных правил можно перейти к следующему примеру.
Возвращает целое представление. Для строк можно указать основание системы счисления. Преобразование способно потерять часть исходного текста.
<?php
declare(strict_types=1);
$raw = '12';
if (preg_match('/\A[1-9][0-9]{0,2}\z/', $raw) !== 1) {
throw new InvalidArgumentException('Неверное количество');
}
echo intval($raw), "\n";
Ожидаемый результат
12
Проверка гарантирует диапазон 1..999, поэтому преобразование не скрывает посторонний хвост или переполнение.
<?php
declare(strict_types=1);
$hex = 'ff';
echo intval($hex, 16), "\n";
Ожидаемый результат
255
Основание 16 задается явно для известного технического формата. Внешний код сначала ограничьте допустимыми символами и длиной.
Сравните. Для проверенного значения обычно достаточно(int); FILTER_VALIDATE_INT или собственная ограниченная грамматика выполняют валидацию.
roundОкруглите показание до десятых либо расчёт до сотен, заранее определив, куда отправлять точную половину.
round(int|float $num, int $precision = 0, int|RoundingMode $mode = RoundingMode::HalfAwayFromZero): floatОкруглите показание до десятых либо расчёт до сотен, заранее определив, куда отправлять точную половину.
Перед первым опытом: запись round(...) вызывает функцию; в круглых скобках передаются значения через запятую, возвращённое значение можно сохранить через =. echo печатает, var_dump показывает ещё и тип, "\n" переносит строку. Параметр, вызов и возврат; Как читать числовые типы и результаты.
| Вход или результат | Точный договор |
|---|---|
| num | Число до округления. |
| precision | По умолчанию 0: целое; 2: сотые; −2: сотни. Это позиция разряда, а не количество значащих цифр всего числа. |
| mode | До PHP 8.4 — одна из четырёх PHP_ROUND_HALF_*; с 8.4 также RoundingMode с восемью вариантами. По умолчанию ближайшее, половина от нуля. |
| Возврат | Всегда float; функция не дописывает в тексте нули до precision. |
| Ошибки и границы | С PHP 8.4 неизвестный int-режим даёт ValueError; раньше молча использовался HALF_UP. С PHP 8 не принимаются внутренние численно преобразуемые объекты. |
Числа float хранят двоичное приближение. Входной int разрешён и для параметра float. Массив и неподходящая строка не являются числом: возможен TypeError; правила нестрогого преобразования строк и режима strict_types разобраны в главе о типах. Данные формы сначала проверяют, а не исправляют математической функцией.
В примерах printf("%.3f\n", $value) печатает число с тремя знаками после точки; это формат вывода, само значение в памяти не становится точным десятичным числом. if выбирает ветку, function объявляет повторяемое действие, return возвращает его результат: начальные объяснения. В опытах с отказом try выполняет попытку, catch ловит указанный тип ошибки; как работает обработка исключений.
Обозначения последующих шагов: foreach ($list as $value) последовательно берёт элементы списка; [a, b] создаёт список. Условия и обход списка.
- Показание 18.26 °C
- round(18.26, 1): выбираем десятые
- Получаем 18.3; затем отдельно печатаем
<?php
echo round(18.26, 1), "\n";
echo round(1260, -2), "\n";
var_dump(round(7.0, 2));
Ожидаемый результат
18.3
1300
float(7)
precision = −2 выбирает ближайшую сотню. Тип float остаётся даже у числа без дробной части.
<?php
foreach ([PHP_ROUND_HALF_UP, PHP_ROUND_HALF_DOWN, PHP_ROUND_HALF_EVEN, PHP_ROUND_HALF_ODD] as $mode) {
echo round(2.5, 0, $mode), ',', round(-2.5, 0, $mode), "\n";
}
Ожидаемый результат
3,-3
2,-2
2,-2
3,-3
UP означает от нуля именно на половине; это не ceil для всех дробей. EVEN выбирает чётного соседа, ODD — нечётного.
<?php
try { round(2.5, 0, 99); }
catch (ValueError $error) { echo "unknown rounding mode\n"; }
echo round(2.9, 0, RoundingMode::TowardsZero), "\n";
Ожидаемый результат
unknown rounding mode
2
Последняя строка намеренно усекает все дроби к нулю. Она требует PHP 8.4; режимы enum разобраны рядом.
Когда подходит и где остановиться. Сначала задайте правило предметной области: округлять строку заказа или общий итог — разные задачи. round не делает float точным десятичным числом. number_format нужен для текста интерфейса, не для следующего вычисления. RoundingMode показывает все восемь правил.
Сравните: ceil, floor, RoundingMode. Выбор по задаче.
Продолжение: ранее разобранная практика.
Возвращает float. Precision задает десятичную позицию, mode — правило половин. Перечисление RoundingMode доступно с PHP 8.4; примеры используют совместимые константы.
<?php
declare(strict_types=1);
$temperature = 21.26;
echo round($temperature, 1), "\n";
Ожидаемый результат
21.3
Округленный результат — представление измерения. Если исходная точность нужна для статистики, храните исходное значение отдельно.
<?php
declare(strict_types=1);
echo round(2.5, 0, PHP_ROUND_HALF_UP), "\n";
echo round(2.5, 0, PHP_ROUND_HALF_EVEN), "\n";
Ожидаемый результат
3
2
Оба результата корректны для разных правил. Укажите выбранную политику в расчете, иначе два сервиса могут по-разному получить итог чека.
Сравните. floor/ceil направленно округляют; intdiv работает с целыми; BCMath нужен для согласованных десятичных вычислений.
intdivРазделите 29 задач на полные пакеты по 10 без промежуточного float: получатся два полных пакета.
intdiv(int $num1, int $num2): intРазделите 29 задач на полные пакеты по 10 без промежуточного float: получатся два полных пакета.
Перед первым опытом: запись intdiv(...) вызывает функцию; в круглых скобках передаются значения через запятую, возвращённое значение можно сохранить через =. echo печатает, var_dump показывает ещё и тип, "\n" переносит строку. Параметр, вызов и возврат; Как читать числовые типы и результаты.
| Вход или результат | Точный договор |
|---|---|
| num1 | Целое делимое. |
| num2 | Целый ненулевой делитель. |
| Возврат | Целое частное с отбрасыванием дробной части в сторону нуля. |
| Ошибки и границы | Делитель 0: DivisionByZeroError. PHP_INT_MIN / −1 не помещается в int: ArithmeticError. Дробный float не входит в контракт int. |
Оператор % в первом примере возвращает остаток целочисленного деления. 29 % 10 равно 9; это не процент от числа.
В опытах с отказом try выполняет попытку, catch ловит указанный тип ошибки; как работает обработка исключений.
- 29 задач, размер пакета 10
- intdiv(29, 10) убирает неполный пакет
- Возвращает int 2; 9 задач остаются для следующего пакета
<?php
echo intdiv(29, 10), ' full; ', 29 % 10, " remain\n";
echo intdiv(-29, 10), "\n";
Ожидаемый результат
2 full; 9 remain
-2
% даёт целочисленный остаток. Для отрицательного делимого частное −2, тогда как floor(−2.9) даёт −3.
<?php
try { intdiv(29, 0); }
catch (DivisionByZeroError $error) { echo "zero divisor\n"; }
try { intdiv(PHP_INT_MIN, -1); }
catch (ArithmeticError $error) { echo "integer overflow\n"; }
Ожидаемый результат
zero divisor
integer overflow
Ловим конкретные ошибки; не подменяем ошибку исходных данных результатом 0.
Когда подходит и где остановиться. Для числа страниц нужна ещё страница при ненулевом остатке: intdiv($count, $size) + (int) ($count % $size !== 0), при count ≥ 0 и size > 0. Не заменяйте intdiv на (int)($a/$b) для больших целых: float может потерять младшие разряды.
Сравните: fdiv, floor, ceil. Выбор по задаче.
Продолжение: ранее разобранная практика.
Возвращает целое частное, округляя к нулю. Остаток вычисляется отдельно через%. Нулевой делитель вызывает DivisionByZeroError.
<?php
declare(strict_types=1);
$total = 100;
$parts = 3;
echo intdiv($total, $parts), ' + остаток ', $total % $parts, "\n";
Ожидаемый результат
33 + остаток 1
Три части по 33 оставляют 1 копейку. Политика распределения остатка должна быть явной, иначе сумма частей не совпадет с итогом.
<?php
declare(strict_types=1);
$processed = 250;
$batchSize = 100;
echo intdiv($processed, $batchSize), "\n";
Ожидаемый результат
2
Завершены две полные партии, но остается 50 обработанных элементов. Для прогресса храните также остаток или точное число, а не только номер партии.
Сравните. Для дробного результата нужен/; для страниц с округлением вверх учтите остаток, а не отбрасывайте его.
Источники и обсуждения
- PHP Manual: language.types.integer
- PHP Manual: language.types.float
- PHP Manual: language.types.declarations
- isset() — официальный справочник
- empty() — официальный справочник
- PHP Manual: language.variables.basics
- PHP Manual: language.types.string
- PHP Manual: language.types.boolean
- PHP Manual: language.types.numeric-strings
- PHP Manual: language.types.type-system
- PHP Manual: language.types.type-juggling
- PHP Manual: language.types.null
- PHP Manual: language.types.mixed
- PHP Manual: language.types.void
- PHP Manual: language.types.never
- PHP Manual: language.types.iterable
- PHP Manual: language.types.resource
- PHP Manual: language.types.intro
- PHP Manual: language.types.array
- PHP Manual: language.types.object
- PHP Manual: language.types.enumerations
- PHP Manual: language.types.callable
- PHP Manual: language.types.relative-class-types
- PHP Manual: language.types.singleton
- PHP Manual: function.var-dump
- PHP Manual: function.get-debug-type
- PHP Manual: function.settype
- PHP Manual: function.get-object-vars
- PHP Manual: class.stdclass
- PHP Manual: function.is-callable
- PHP Manual: functions.first_class_callable_syntax
- PHP Manual: migration85.deprecated
- PHP Manual: language.variables.scope
- PHP Manual: language.operators.arithmetic
- PHP Manual: language.operators.comparison
- PHP Manual: control-structures.if
- PHP Manual: control-structures.foreach
- PHP Manual: functions.user-defined
- PHP Manual: functions.arguments
- PHP Manual: functions.returning-values
- PHP Manual: function.echo
- PHP Manual: function.return
- PHP Manual: function.is-array
- PHP Manual: function.array-fill
- PHP Manual: function.array-replace-recursive
- PHP Manual: function.array-multisort
- PHP Manual: function.array-udiff