PHP: полный мануал с примерами, функциями и ООП
Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Дерево руководства
Глава 12 · С нуля → практика
Выражения PHP: значение, вычисление и использование результата
Чем выражение отличается от инструкции, почему присваивание даёт значение и как результат проходит через переменную, функцию, условие и вывод.
Выражение: кусочек кода, который даёт значение
От формулы к значению и выводу
Две единицы по 120 ₽ и одна доставка 50 ₽: в первом примере вычисляется 120 * 2 + 50.
- 1Часть формулы: 120 * 2↓
Умножение связывается раньше сложения. Стоимость двух единиц — 240.
- 2Вся формула: 240 + 50↓
Прибавляем одну доставку. Значение выражения — целое число 290.
- 3echo получает 290
Число становится текстом в выходном потоке. Отдельный echo добавляет перевод строки.
Схема показывает смысл чистой арифметики. Она не обещает порядок побочных действий в любой формуле; вычисление само не выводит результат.
Нужно узнать стоимость двух товаров по 120 ₽ с доставкой за 50 ₽. Для этого не нужна переменная или функция: 120 * 2 + 50 уже описывает вычисление. Знак * означает умножение, + — сложение. Получается число 290.
Выражение — запись, вычисление которой даёт значение. Значение можно вывести, сохранить, передать дальше или использовать как условие. 120 — тоже выражение: его значение уже записано буквально. Слово expr в справочной документации обычно означает «сюда можно подставить подходящее выражение», а не ключевое слово PHP.
<?php
echo 120 * 2 + 50;
echo "\n";
Ожидаемый результат
290
Сначала получается значение выражения 290; echo добавляет его текстовое представление в вывод. "\n" во второй строке — строковое значение перевода строки. Скобки вокруг всей формулы для echo не нужны.
Выражение и инструкция: почему 120 * 2 ничего не печатает
120 * 2 даёт 240. Если написать 120 * 2;, получится инструкция-выражение: она не приказывает выводить или сохранять значение. После выполнения результат никем не используется. Оптимизатор может вообще убрать такое бесполезное вычисление.
echo 120 * 2; — инструкция вывода. Внутри неё находится выражение, но сама конструкция echo значения для дальнейшей формулы не возвращает. Точка с запятой заканчивает инструкцию, а не означает «покажи ответ».
<?php
120 * 2;
echo 'После расчёта', "\n";
echo 120 * 2, "\n";
Ожидаемый результат
После расчёта
240
Первой видимой строкой оказывается «После расчёта». Формула в начале не выводит 240. Последняя инструкция явно передаёт результат echo.
Попробуйте сами. Что появится, если убрать последнюю инструкцию echo?
Разбор решения
Только «После расчёта» с переводом строки. Из самого наличия формулы не возникает вывода.
Не всякая синтаксическая конструкция является выражением. if выбирает инструкции, объявление именованной функции через function задаёт её, echo выводит. Их нельзя произвольно поставить туда, где ожидается значение. Позже увидим вызов функции и тернарный оператор — они как раз дают значения.
Значением бывает не только число
Числа 12 и 2.5, текст 'paid', логические true/false, отсутствие значения null — разные виды значений. Список [12, 18] — массив с двумя элементами. Квадратные скобки собирают массив, запятая разделяет элементы. Массивы и объекты тоже могут быть результатами выражений, но не предназначены для бездумного вывода через echo.
Для наблюдения используем готовую функцию var_dump(...): она показывает тип и содержимое каждого переданного значения. Круглые скобки задают вызов, запятые — отдельные входные значения. Это диагностический вывод для учебного опыта, а не оформление страницы магазина.
<?php
var_dump(12, 2.5, 'paid', true, null, [12, 18]);
Ожидаемый результат
int(12)
float(2.5)
string(4) "paid"
bool(true)
NULL
array(2) {
[0]=>
int(12)
[1]=>
int(18)
}
string(4) сообщает длину в байтах; у слова paid четыре ASCII-байта. У элементов массива здесь автоматически получились ключи 0 и 1. Не переносите правило «байт = буква» на кириллицу.
В PHP также есть ресурсы для открытых потоков и другие специальные случаи типов. Эта глава объясняет получение и использование значения; определения типов находятся в главе о типах, составные данные — в главах о массивах и объектах.
Переменная читается как значение; присваивание тоже даёт результат
$delivery — переменная, имя текущего значения. Знак $ отличает её от обычного слова; = сохраняет правое значение в левую переменную. $delivery = 50; записывает 50, а echo $delivery; читает и выводит его. Это начало темы переменных.
У присваивания есть две стороны: оно меняет переменную и само является выражением со значением, которое было записано. В $shown = ($delivery = 80) внутреннее присваивание даёт 80; внешнее получает именно это значение. Скобки делают вложенность заметной.
<?php
$delivery = 50;
$shown = ($delivery = 80);
echo 'Доставка: ', $delivery, "\n";
echo 'Сохранённый результат: ', $shown, "\n";
Ожидаемый результат
Доставка: 80
Сохранённый результат: 80
Результат присваивания — не true/false и не имя переменной. Это записанное значение 80. В обычном коде два отдельных присваивания часто легче прочесть, чем вложенная запись.
<?php
$pickupLimit = $courierLimit = 6;
$courierLimit = 8;
echo $pickupLimit, ' / ', $courierLimit, "\n";
Ожидаемый результат
6 / 8
Группировка справа: $pickupLimit = ($courierLimit = 6). Позже изменение courierLimit не изменило pickupLimit. Здесь обычные целые значения, а не ссылки. У объектов есть отдельные правила идентичности; равенство результата присваивания не означает глубокое копирование объекта.
Именованная константа тоже читается как выражение. const DELIVERY = 50; закрепляет за именем DELIVERY число 50; знак $ у константы не ставят. Имя помогает не повторять непонятное число в формулах. В отличие от переменной, константу нельзя затем обычным присваиванием заменить новым значением.
<?php
const DELIVERY = 50;
echo 120 * 2 + DELIVERY, "\n";
Ожидаемый результат
290
Выражение DELIVERY даёт 50, а вся формула — 290. Область применения и способы определения разобраны в главе о константах.
Скобки показывают, из каких частей собрана формула
В формуле доставки умножение связывается раньше сложения: 120 * 2 + 50 читается как (120 * 2) + 50. Если доставку 50 ₽ прибавить к каждой единице товара, нужна другая группа: (120 + 50) * 2.
Приоритет определяет группировку разных операторов. Ассоциативность определяет группировку операторов одного уровня: 20 - 5 - 3 означает (20 - 5) - 3. Присваивание группируется справа, как в предыдущем примере. Скобки не выводятся и не меняют число сами: они выбирают, какие значения участвуют в следующей операции.
<?php
echo 120 * 2 + 50, "\n";
echo (120 + 50) * 2, "\n";
echo 20 - 5 - 3, "\n";
echo 20 - (5 - 3), "\n";
Ожидаемый результат
290
340
12
18
Разные скобки меняют смысл расчёта, не только внешний вид. Для денег в настоящей системе договоритесь об единицах: далее в практическом примере считаем целые копейки.
Полная таблица операторов находится в разделе приоритетов. Сравнения одного неассоциативного уровня нельзя цеплять как школьное 1 < 2 < 3. Для диапазона нужны две проверки, соединённые && («и»). Точка для соединения строк в PHP 8.0+ имеет более низкий приоритет, чем + и −; скобки вокруг арифметики всё равно делают намерение яснее.
Группировка не обещает порядок побочных действий
Для простой арифметики результат ясен по скобкам. Сложнее, когда вычисление одновременно меняет данные: увеличивает счётчик, пишет файл, вызывает внешний сервис. Такое изменение помимо результата называют побочным эффектом. Оно не обязательно плохое — запись заказа нужна приложению; важно видеть, когда она происходит.
PHP в общем случае не обещает порядок вычисления соседних операндов. Нельзя опираться на выбранное одной версией поведение формулы, которая и читает, и меняет одну переменную в разных её частях. Ассоциативность описывает структуру, не расписание всех действий. Скобки не превращают произвольную формулу в последовательность команд.
<?php
$count = 3;
echo $count + $count++;
Синтаксически допустимый антипаттерн, не образец для копирования. У такого выражения нет переносимого ожидаемого результата, поэтому здесь намеренно нет фиксированного вывода и оно не включено в run-проверку. ++ увеличивает переменную; его отдельные формы разобраны далее. Вынесите изменение в отдельную инструкцию.
<?php
$count = 3;
$before = $count;
$count = $count + 1;
$sum = $before + $count;
echo $sum, "\n";
Ожидаемый результат
7
Порядок отдельных инструкций явный: запомнить 3, изменить count на 4, сложить 3 и 4. Так выражена именно выбранная бизнес-логика, а не попытка угадать порядок опасной формулы.
Есть конкретные гарантии для отдельных механизмов. Аргументы функции вычисляются до входа в её тело, слева направо; логические короткие проверки могут пропустить правую часть. Это специальные правила, не разрешение обобщить «всё всегда слева направо». См. вычисление аргументов.
Вызов функции занимает место её результата
Назовём расчёт доставки, чтобы использовать его повторно. function объявляет функцию; deliveryCost — выбранное понятное имя; ($kilometers) — один параметр, локальное имя входного числа; фигурные скобки ограничивают тело. return завершает вызов и отдаёт значение туда, откуда вызвали. Само объявление ещё не выполняет расчёт.
deliveryCost(3) — вызов. Число 3 передаётся параметру kilometers. Функция считает 100 + 3 * 20 и возвращает 160. В выражении вызова теперь есть значение 160, его и получает присваивание. Никаких аннотаций типов и исключений для понимания этого шага не требуется.
<?php
function deliveryCost($kilometers)
{
return 100 + $kilometers * 20;
}
$cost = deliveryCost(3);
echo 'Доставка: ', $cost, "\n";
echo 'Срочная: ', deliveryCost(3) + 50, "\n";
Ожидаемый результат
Доставка: 160
Срочная: 210
Во втором вызове те же 160 участвуют в сложении с доплатой 50. Возврат не напечатал 160 отдельной строкой: вывод выполняет echo снаружи. Функция здесь ожидает заранее выбранное неотрицательное число километров; проверка входных данных — отдельный этап.
- До присваивания нужно получить правое значение: начинается deliveryCost(3).
- Параметр $kilometers получает 3; тело вычисляет 100 + 3 * 20.
- return отдаёт 160 в исходное место вызова, выполнение тела заканчивается.
- Присваивание сохраняет 160 в $cost; следующая инструкция echo читает $cost.
Дальше можно передать другое расстояние, не повторяя формулу. Функция подходит, если этот расчёт имеет имя и повторяется; для одноразовых 120 * 2 отдельная функция не обязательна. Подробная механика параметров — в главе о функциях.
Вывести, вернуть и выполнить действие — разные вещи
Функция может только выполнить действие. Если в ней нет return со значением, обычный вызов возвращает null. Это не строка, которую функция вывела. Для наглядности следующая функция печатает объявление; присваивание получает отсутствие результата.
У print другая особенность: это конструкция вывода одного выражения, у которой есть значение 1. Оно не равно длине текста и не подтверждает успешную запись заказа. У echo возвращаемого значения нет, поэтому запись $result = echo ... запрещена.
<?php
function showPickupNotice()
{
echo 'Самовывоз сегодня', "\n";
}
$result = showPickupNotice();
var_dump($result);
$printResult = (print "Пункт открыт\n");
var_dump($printResult);
Ожидаемый результат
Самовывоз сегодня
NULL
Пункт открыт
int(1)
Первый вызов произвёл вывод, затем вернул null. Print вывел другую строку и дал 1. var_dump здесь нужен, чтобы отсутствие значения не выглядело как «ничего не произошло».
<?php
$result = echo 'Сохранено';
Намеренно запрещённый синтаксис. PHP 8.5.11 сообщает unexpected token "echo". Нужен либо самостоятельный echo, либо функция, которая возвращает строку через return.
Не заменяйте echo на print только ради вложенной формулы: отдельный вывод обычно понятнее. Если текст нужен для письма и для HTML, удобнее вернуть строку и решить снаружи, куда её отправить. Когда нужно получить строкой уже готовый шаблон с echo, применяется буферизация вывода.
++ и --: новое состояние и значение самого выражения
Оба варианта $ticket++ и ++$ticket увеличивают целую переменную на один. Но выражение послефикса $ticket++ даёт прежнее значение, а префикса ++$ticket — уже увеличенное. Это различие видно, когда результат сохраняется. Для уменьшения действуют формы $stock-- и --$stock.
Не нужно представлять, что постфикс откладывает изменение до произвольного будущего. После завершения отдельной инструкции переменная уже изменена. Различаются значение выражения и новое состояние переменной. Здесь используются только целые числа; у других типов есть ограничения и версионные нюансы.
<?php
$nextTicket = 40;
$issuedTicket = $nextTicket++;
echo 'Выдан: ', $issuedTicket, '; следующий: ', $nextTicket, "\n";
$counter = 40;
$newCounter = ++$counter;
echo 'Результат: ', $newCounter, '; счётчик: ', $counter, "\n";
Ожидаемый результат
Выдан: 40; следующий: 41
Результат: 41; счётчик: 41
Постфикс позволил сохранить старый номер 40 и подготовить 41. Префикс передал наружу уже 41. Для настоящей выдачи уникальных номеров нескольким запросам нужна согласованная запись в базе: обычная локальная переменная не защищает от гонки.
<?php
$stock = 5;
$before = $stock--;
echo $before, ' / ', $stock, "\n";
$after = --$stock;
echo $after, ' / ', $stock, "\n";
Ожидаемый результат
5 / 4
3 / 3
Сначала сохранено прежнее 5, stock стал 4. Затем префикс уменьшил stock до 3 и вернул 3. Не смешиваем чтение и изменение этого остатка в одной более длинной формуле.
Если значение операции не используется, $count++; и ++$count; меняют целый счётчик одинаково. Выбирайте читаемость; обещать выигрыш производительности по одному только расположению ++ неверно. Продолжение — изменение и присваивание.
+= и -=: берём текущее, меняем, сохраняем
$total += 50 для обычной числовой переменной означает прибавить 50 к текущему total и сохранить сумму обратно. Значение всего выражения — новая сумма, не прибавка 50. Аналогично работают -=, *= и другие предусмотренные языком составные присваивания.
Со строкой .= добавляет текст справа к уже имеющемуся. Это соединение, а не арифметика. Не изобретайте комбинированные операторы по любому знаку: существует определённый набор, например === — сравнение, а не вариант присваивания.
<?php
$total = 1000;
$afterDiscount = ($total -= 200);
$label = 'Заказ';
$label .= ' готов';
echo 'Итого: ', $total, '; результат: ', $afterDiscount, "\n";
echo $label, "\n";
Ожидаемый результат
Итого: 800; результат: 800
Заказ готов
Оба числа равны 800; выражение -= не вернуло размер скидки 200. Скобки подчёркивают, какой результат сохраняется. Для человека проще разнести шаги, если вложенность мешает.
Сокращённая форма уменьшает повторение имени, но не является доказательством ускорения программы. Время обычно определяется алгоритмом, объёмом данных и вводом-выводом. Для больших выражений с индексами и побочными эффектами не делайте механическую текстовую замену без разбора того, что вычисляется.
Сравнение даёт bool, а условие приводит значение к bool
8 > 3 даёт true, 8 < 3 — false. Операторы >= и <= включают равенство. ==/!= сравнивают с правилами преобразования типов, а ===/!== учитывают и тип. Ни один из них не присваивает значение: присваивание — одиночный =.
В if (...) { ... } выражение внутри скобок решает, выполнять ли блок. Условие может быть уже bool или приводиться к нему. К ложным относятся false, числовой ноль (включая 0.0 и −0.0), пустая строка, строка "0", пустой массив и null. Непустая строка "false" истинна, как и ненулевое отрицательное число. У отдельных внутренних объектов бывают специальные правила преобразования.
<?php
var_dump(8 > 3);
var_dump('8' == 8);
var_dump('8' === 8);
Ожидаемый результат
bool(true)
bool(true)
bool(false)
Строка и число могут совпасть при ==, но === требует одинакового типа. Это не совет сравнивать любую пользовательскую строку с числом: сначала решите, какое значение приложение принимает.
<?php
$stockText = '0';
if ($stockText) {
echo 'Условие истинно', "\n";
} else {
echo 'Условие ложно', "\n";
}
if ($stockText !== '') {
echo 'Поле не пустое', "\n";
}
Ожидаемый результат
Условие ложно
Поле не пустое
Ложность в логическом контексте не означает отсутствие данных. Для остатка строка 0 — осмысленный ввод. Проверка !== пустой строке отвечает другому вопросу; она сама по себе ещё не доказывает, что ввод является допустимым остатком.
Не проверяйте «успех» через один bool, если функция может вернуть полезный 0 и отдельный false для ошибки. Политика конкретного результата важнее краткости условия. См. сравнения и isset и empty.
Тернарный оператор выбирает значение, а не две инструкции
условие ? значение_если_да : значение_если_нет — одно выражение. Сначала проверяется условие, затем вычисляется только выбранная часть. Оно удобно, когда нужно выбрать короткую подпись или одну величину. Для нескольких действий в ветке понятнее if.
Следующий пример впервые соединяет выбор значения с присваиванием: $label = ... сохраняет выбранную строку. Целиком выражение выбора окружено скобками для чтения; без них в таком присваивании смысл тот же.
<?php
$stock = 0;
$label = ($stock > 0 ? 'В наличии' : 'Под заказ');
echo $label, "\n";
$itemCount = 0;
$average = ($itemCount > 0 ? 900 / $itemCount : 0);
echo 'Среднее: ', $average, "\n";
Ожидаемый результат
Под заказ
Среднее: 0
Вторая формула не делит на ноль: ветка 900 / itemCount не вычислялась. Ноль как результат для пустого набора — выбранное правило этого примера; в отчёте можно вместо него вернуть null и отдельно показать «нет данных».
Короткая форма $value ?: $fallback оставляет value, если оно истинно, иначе берёт fallback; value вычисляется один раз. $value ?? $fallback выбирает запасной вариант только при отсутствии переменной или null и сохраняет 0 и пустую строку. Это разные требования, а не два написания одного правила.
<?php
$delivery = 0;
echo $delivery ?: 300, "\n";
echo $delivery ?? 300, "\n";
Ожидаемый результат
300
0
?: заменил допустимый 0 на 300; ?? сохранил его. Не смешивайте операторы в длинной формуле без явных скобок. В PHP 8.0+ цепочка нескольких полных тернарных операторов без скобок запрещена.
Подробнее о выборе значений и о null — тернарный оператор и ??.
Короткое замыкание: правая часть может не выполниться
A && B истинно, когда истинны обе части. Если A ложно, проверять B уже не нужно. A || B истинно, когда истинна хотя бы одна часть: при истинном A правая часть пропускается. Результат этих операторов в PHP — bool, а не выбранная исходная строка или число.
Это полезно для безопасной последовательной проверки: сначала убедиться, что делитель не ноль, затем делить. В PHP похожие слова and/or имеют другой приоритет; при присваивании результата проще явно использовать скобки и &&/||.
<?php
$count = 0;
$total = 900;
$largeAverage = ($count > 0 && $total / $count > 300);
var_dump($largeAverage);
Ожидаемый результат
bool(false)
Левая часть false, поэтому деление не запускается. Если count заменить на 2, проверятся обе части и получится true.
<?php
function checkAvailability()
{
echo 'Проверили остаток', "\n";
return true;
}
$guestAllowed = false && checkAvailability();
$cachedAllowed = true || checkAvailability();
echo 'Первые два выражения завершены', "\n";
$allowed = true && checkAvailability();
var_dump($allowed);
Ожидаемый результат
Первые два выражения завершены
Проверили остаток
bool(true)
Объявление функции само не печатает метку. Первые два выражения пропустили вызов; третье выполнило. Так можно понять, почему спрятанная справа запись в журнал или запрос к API иногда не случается. Обязательное действие нужно вынести в отдельную инструкцию.
Такая защита работает только когда левая проверка действительно гарантирует безопасность правой. Оператор не валидирует данные сам. Битовые & и | не являются заменой логическим && и || и не обеспечивают ту же схему пропуска.
Практика: одна формула внутри функции и карточки заказа
В корзине уже проверены цена и количество. Нужно получить целую сумму в копейках, сохранить её для заказа и отдельно вывести. totalKopecks($price, $quantity, $delivery) получает три входных значения. Внутри формула такая же, как в начале главы; return отдаёт сумму. Имена переменных фиксируют смысл единиц.
Это расчёт, а не обработчик оплаты. Он не читает POST, не проверяет наличие товара и не списывает деньги. Перед применением к внешним данным нужны проверка целых чисел, допустимого диапазона и правила округления/скидок. Здесь вход задан непосредственно в программе.
<?php
function totalKopecks($price, $quantity, $delivery)
{
return $price * $quantity + $delivery;
}
$total = totalKopecks(12000, 2, 5000);
$message = 'К оплате: ' . $total . ' коп.';
echo $message, "\n";
echo 'Для записи заказа: ', $total, "\n";
Ожидаемый результат
К оплате: 29000 коп.
Для записи заказа: 29000
Вызов дал 29000. Присваивание сохранило число. Точки соединили три части строки, а echo вывел готовую подпись. Повторное чтение total не пересчитывает функцию.
| Шаг | Значение и действие |
|---|---|
| Вход | price = 12000, quantity = 2, delivery = 5000. |
| Тело | 12000 * 2 → 24000; 24000 + 5000 → 29000. |
| Возврат | 29000 подставляется в место вызова totalKopecks(...). |
| Сохранение | Переменная total получает 29000; функция закончила этот вызов. |
| Представление | Формируется отдельная строка; число остаётся пригодным для дальнейшей работы. |
Если завтра появится другая подпись для письма, расчёт не нужно переписывать. Если появится иной способ расчёта налога, меняется функция и её проверка. Не прячьте в такую формулу сетевую оплату или сохранение заказа: побочные действия должны быть видны. Продолжение: контракт функции, проверенный ввод формы, атомарное оформление.
Проверьте смысл формулы и исправьте запрещённые конструкции
<?php
$quantity = 3;
var_dump(1 < $quantity < 10);
Намеренно запрещённый синтаксис. PHP 8.5.11 сообщает unexpected token "<". Нужно 1 < $quantity && $quantity < 10, а если границы допустимы — операторы <=.
<?php
$stock = 2;
$label = $stock > 5 ? 'Много' : $stock > 0 ? 'Мало' : 'Нет';
Намеренно запрещённый синтаксис в PHP 8.0+. Диагностика сообщает, что unparenthesized ternary не поддерживается. Группируйте вложенный выбор скобками или используйте понятный if/elseif/else.
Попробуйте сами. Товар стоит 7000 копеек, нужно 3 штуки, доставка 2000 копеек. Получите total, затем подпись «Бесплатная доставка» при сумме товаров от 20000. Сначала посчитайте сумму товаров отдельно: порог не включает платную доставку.
Разбор решения
<?php
$itemsTotal = 7000 * 3;
$delivery = ($itemsTotal >= 20000 ? 0 : 2000);
$total = $itemsTotal + $delivery;
$label = ($delivery === 0 ? 'Бесплатная доставка' : 'Доставка платная');
echo $label, "\n";
echo 'К оплате: ', $total, ' коп.', "\n";
Ожидаемый результат
Бесплатная доставка
К оплате: 21000 коп.
Сумма товаров 21000 достигает порога, поэтому доставка равна 0. Мы не применили ?: к нулю и не подменили его платным вариантом.
Мысленная проверка любого выражения: какие значения входят; что получится; меняется ли что-то кроме результата; какая ветка может быть пропущена; кто использует результат дальше. Сложную строку полезно разложить на именованные шаги, если от этого легче проверить правило. Полный набор операций и версионные различия находятся в главе об операторах.
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


