Учебник по PHP с запускаемыми примерами
Бесплатный учебник и справочник по PHP 8: массивы, foreach, строковые функции, даты, формы, классы и ошибки. Каждый пример запускается прямо на странице, а примеры, которые выводят HTML, показывают страницу с формами, которые можно отправить.
Начать пошаговый курс по PHPНачало работы
- Что такое PHPPHP это серверный скриптовый язык: PHP-файл выполняется на веб-сервере и отправляет в браузер собранный им HTML. На PHP работают WordPress, Laravel и значительная часть веба, а ещё он запускает скрипты из командной строки.
- Установка PHPPHP ставится одной командой: winget в Windows, Homebrew в macOS, apt или dnf в Linux. Потом запустите php -v в терминале, чтобы проверить, что всё работает, и узнать версию.
- Запуск PHPЗапустите PHP-файл из терминала командой php index.php или откройте папку в браузере через встроенный сервер PHP: php -S localhost:8000. Ни Apache, ни XAMPP для этого не нужны.
- echo и printecho выводит одну или несколько строк: echo "Hello, $name"; или echo $a, $b;. Это обычный способ отправить текст или HTML на страницу. print делает то же с одним значением и возвращает 1; <?= $x ?> это короткая форма внутри HTML.
- Вывод ошибокЧтобы показать все ошибки PHP, поставьте ini_set('display_errors', '1'); и error_reporting(E_ALL); в начало скрипта или задайте display_errors = On и error_reporting = E_ALL в php.ini. В продакшене ошибки пишут в лог, а не показывают.
- PHP или PythonPHP создан для веба: файл выполняется на каждый запрос и печатает страницу, на нём работают WordPress и Laravel. Python это язык общего назначения, который тоже умеет веб (Django, Flask) и лидирует в анализе данных, автоматизации и машинном обучении.
- PHP или JavaScriptPHP работает на сервере и отправляет в браузер готовый HTML. JavaScript работает в браузере, где меняет страницу после загрузки, а с Node.js и на сервере. Большинство сайтов используют оба: PHP для страниц и API, JavaScript для интерактивности.
Основы
- ПеременныеПеременная PHP начинается с $ и создаётся присваиванием: $name = "Ada";. Ключевого слова для объявления и фиксированного типа нет. Функции не видят внешние переменные, если их не передать, не объявить через global или не захватить; static сохраняет значение между вызовами.
- Типы данныхВ PHP десять типов: четыре скалярных (int, float, string, bool), четыре составных (array, object, callable, iterable) и два специальных (null, resource). var_dump($x) показывает тип и значение, gettype($x) возвращает имя типа.
- СтрокиСтроки в PHP склеивают оператором точка ("Hello, " . $name) или подставляют переменные прямо в двойные кавычки ("Hello, $name"). Одинарные кавычки выводят текст как есть. Конкатенация, .=, интерполяция массивов и объектов, смена регистра через strtoupper, strtolower, ucfirst и ucwords.
- Heredoc и NowdocHeredoc в PHP это многострочная строка, которая начинается с <<<EOT и заканчивается на EOT; переменные в ней подставляются, как в строке в двойных кавычках. Nowdoc, <<<'EOT', сохраняет текст точно как написано. Синтаксис, правила отступов, интерполяция массивов и объектов, HTML-шаблоны.
- ОператорыОператоры PHP работают со значениями: +, -, *, /, %, ** для арифметики, =, +=, .=, ??= для присваивания, &&, ||, ! и xor для логики, . для склейки строк, ==, ===, <=> для сравнения и ++, -- для инкремента. Каждая группа на живых примерах и ловушка приоритета and против &&.
- Операторы сравненияPHP сравнивает значения через == (равны после приведения типов), === (равны значение и тип), != и !==, < > <= >= и оператор spaceship <=>. Когда == приводит типы, что изменилось в PHP 8, и все нестрогие и строгие сравнения в одной таблице.
- Тернарный операторТернарный оператор PHP это if/else в одну строку: $result = $condition ? 'yes' : 'no';. Короткий тернарный ?: (оператор Элвиса), чем он отличается от ??, почему вложенным тернарным с PHP 8 нужны скобки и когда match понятнее.
- Оператор ??Оператор объединения с null в PHP возвращает левую часть, если она не null и задана: $name = $_GET['name'] ?? 'Guest';. Он никогда не предупреждает об отсутствующей переменной или ключе массива. Цепочки ??, присваивание ??=, ?? против ?: и nullsafe-оператор ?->.
- Приведение типовСтроку PHP превращают в целое число через (int) $str или intval($str); (int) "42" даёт 42, а (int) "12abc" даёт 12. Приведение к int, float, bool, string и array, числовые строки, intval с основанием, settype и проверка ввода, чтобы не получать молча 0.
- КонстантыКонстанта PHP это имя для значения, которое нельзя изменить: const MAX_USERS = 100; или define('MAX_USERS', 100);. Разница define и const, константы-массивы, константы класса через self::, проверка через defined() и магические константы __LINE__, __FILE__, __DIR__ и __CLASS__.
- isset()isset($var) возвращает true, когда переменная существует и не равна null, и false в остальных случаях, без всяких предупреждений. Она проверяет и ключи массивов, и свойства объектов: isset($user['email']). isset с несколькими переменными, вложенные ключи, isset и empty, isset и array_key_exists, проверка отправки формы.
- empty()empty($var) возвращает true, когда переменной нет или она ложна: null, false, 0, 0.0, "", "0" или []. Функция никогда не предупреждает о неопределённых переменных и ключах. Почему "0" считается пустым, как точно проверить пустую строку или массив, empty и isset, проверка полей формы.
- Математика и округлениеround($x, 2) округляет до двух знаков после запятой, floor() всегда округляет вниз, а ceil() всегда вверх. А ещё intdiv(), оператор %, ** для степеней, abs(), max(), min() и random_int() для случайных чисел.
Управление потоком
- if, elseif, elseif (condition) { ... } выполняет блок, только когда условие истинно, elseif проверяет следующее условие, а else срабатывает, когда ничего не подошло. elseif и else if, && и ||, истинные значения и синтаксис if: endif; для HTML-шаблонов.
- Оператор switchswitch ($value) сравнивает одно значение со списком меток case и выполняет код от первого совпадения до break. Несколько case, проваливание, default, switch(true) и почему switch использует нестрогое сравнение ==.
- Выражение matchmatch ($x) { 1, 2 => 'low', default => 'other' } сравнивает значение строго через ===, возвращает результат первой совпавшей ветки и выбрасывает UnhandledMatchError, когда ничего не совпало. match и switch, match(true) и match с перечислениями.
- Цикл forfor ($i = 0; $i < 10; $i++) { ... } выполняет блок заданное число раз: начальное значение, условие, которое проверяется перед каждым проходом, и шаг. Обход массива по индексу, обратный отсчёт, вложенные циклы, break и continue.
- Цикл whilewhile (condition) { ... } повторяет блок, пока условие истинно, и проверяет его перед каждым проходом. do { ... } while (condition); проверяет после, поэтому всегда выполняется хотя бы раз. while (true) с break, чтение файла построчно и как избежать бесконечного цикла.
- Цикл foreachforeach ($array as $value) проходит каждый элемент массива, а foreach ($array as $key => $value) даёт ещё и ключ. Как получить индекс, менять значения по ссылке и ловушка с unset(), break и continue, вложенные массивы, list() в foreach и сборка HTML.
Массивы
- МассивыМассив PHP это упорядоченный список значений, каждое из которых хранится под ключом: $fruits = ['apple', 'banana']; читается как $fruits[0]. Как создавать массивы, читать и менять элементы, обходить и выводить их и какую функцию для массивов выбрать.
- Ассоциативные массивыАссоциативный массив PHP хранит значения под ключами, которые вы называете сами: $user = ['name' => 'Ada', 'age' => 36]; читается как $user['name']. Как добавлять, менять и удалять пары, обходить ключи и значения, получать все ключи или значения, проверять ключ и сортировать по ключу или значению.
- Многомерные массивыМногомерный массив в PHP это массив, элементы которого тоже массивы; читается он по одной паре скобок на уровень: $grid[1][2] или $users[0]['name']. Как создавать вложенные массивы, читать и менять глубокие значения, обходить вложенным foreach, строить HTML-таблицу, извлекать столбец и сортировать строки.
- array_push()array_push($arr, $value) добавляет одно или несколько значений в конец массива; $arr[] = $value делает то же для одного значения и работает быстрее. Как добавить в конец, начало или середину массива, добавить ключ и значение и не попасть в классическую ошибку с возвращаемым значением.
- count()count($arr) возвращает число элементов массива PHP, поэтому count(['a', 'b', 'c']) равно 3. Как считать вложенные массивы через COUNT_RECURSIVE, как sizeof() связана с count(), как посчитать подходящие значения и почему count(null) с PHP 8 выбрасывает TypeError.
- in_array()in_array($needle, $haystack) возвращает true, когда значение есть в массиве. Передайте true третьим аргументом для строгой проверки, in_array('1', $ids, true), чтобы совпадал и тип. Флаг strict, поиск без учёта регистра, проверка нескольких значений, вложенные массивы и быстрая альтернатива через isset.
- array_search()array_search($value, $array) возвращает ключ первого элемента, равного $value, или false, если такого нет. Поскольку ключ может быть 0, всегда проверяйте результат через !== false. Строгий поиск, все совпадения, поиск в многомерном массиве через array_column и поиск без учёта регистра.
- array_key_exists()array_key_exists('key', $array) возвращает true, когда в массиве есть этот ключ, даже если его значение null. isset($array['key']) быстрее, но для значения null возвращает false. Разница между ними, key_exists(), вложенные ключи, проверка нескольких ключей и почему объектам нужна property_exists().
- array_merge()array_merge($a, $b) объединяет массивы в один: значения с числовыми ключами добавляются в конец и перенумеровываются, а для строковых ключей побеждает более поздний массив. Чем array_merge отличается от оператора + и распаковки [...$a, ...$b], как объединить список массивов и когда лучше подходят array_merge_recursive или array_replace.
- array_map()array_map($callback, $array) вызывает колбэк для каждого элемента и возвращает новый массив результатов. Стрелочные функции, ключи, обработка нескольких массивов сразу и когда лучше подходят array_walk или foreach.
- array_filter()array_filter($array, $callback) оставляет элементы, для которых колбэк возвращает true. Как удалить пустые значения, фильтровать по ключу через ARRAY_FILTER_USE_KEY, перенумеровать результат через array_values и подвести итог через array_reduce.
- Сортировка массивовsort($array) сортирует список на месте, asort и ksort сортируют ассоциативный массив по значению или ключу, а usort сортирует по вашему сравнению. Все функции, сортировка многомерного массива по столбцу и классическая ошибка $sorted = sort($arr).
- Удаление из массиваunset($array[$key]) удаляет элемент по ключу, array_splice удаляет по позиции и перенумеровывает, а array_diff удаляет по значению. Каждый способ, перенумерация после удаления и ловушка с array_search, из-за которой удаляется не тот элемент.
- array_slice()array_slice($array, $offset, $length) возвращает копию части массива, не меняя оригинал. Отрицательные смещения для последних элементов, preserve_keys, постраничный вывод списка и отличие от array_splice.
- array_unique()array_unique($array) удаляет повторяющиеся значения и оставляет первое вхождение каждого с его исходным ключом. Как перенумеровать результат, удалить дубликаты без учёта регистра, сделать уникальным многомерный массив и посчитать или найти дубликаты.
- array_column()array_column($rows, 'email') возвращает поле email каждой строки в виде плоского массива. Аргумент ключа индекса, переиндексация строк по id, работа с объектами и поиск строки через array_search.
- Деструктуризация и list()[$a, $b] = $array присваивает первые два элемента переменным $a и $b в одну строку, а ['name' => $name] = $user делает то же по ключу. list() это старое написание. Пропуск элементов, обмен значений переменных, несколько возвращаемых значений, деструктуризация в foreach и оператор распаковки ....
Строки
- explode()explode(',', $string) разбивает строку по каждой запятой и возвращает части в виде массива. Параметр limit, разбиение по переносу строки, обрезка пробелов у частей и когда лучше подходят str_split или preg_split.
- implode()implode(', ', $array) склеивает значения массива в одну строку с разделителем между ними. implode с ключами, ассоциативные массивы, сборка HTML-списков и разница между implode и join.
- str_replace()str_replace($search, $replace, $subject) заменяет каждое вхождение $search на $replace. Массивы искомых значений и замен, подсчёт замен, str_ireplace без учёта регистра и замена только первого вхождения.
- substr()substr($string, $offset, $length) возвращает часть строки. Отрицательные смещения, первые и последние символы, mb_substr для кириллицы, японского и другого текста в UTF-8, substr_count и обрезка текста с многоточием.
- strpos()strpos($haystack, $needle) возвращает позицию первого вхождения $needle или false, если оно не найдено. Проверка !== false, stripos без учёта регистра, последнее совпадение через strrpos и поиск всех вхождений.
- str_contains()str_contains($haystack, $needle) возвращает true, если $needle встречается где-либо в $haystack. Версия без учёта регистра, str_starts_with и str_ends_with, проверка нескольких слов и вариант через strpos для PHP 7.
- trim()trim($string) удаляет пробелы, табуляции и переносы строк с обоих концов строки; ltrim() и rtrim() чистят одну сторону. Как удалять определённые символы, японский полноширинный пробел и обрезать каждое значение массива.
- strlen()strlen($string) возвращает длину строки в байтах, а mb_strlen($string) в символах. Почему кириллица, японский текст и эмодзи дают разные ответы, как проверить пустую строку и как проверять длину ввода.
- sprintf()sprintf() собирает строку из формата и значений: sprintf('%05.2f', 3.14159) возвращает "03.14". %s, %d и %f, дополнение нулями, фиксированное число знаков после запятой, выравнивание, нумерация аргументов, printf и vsprintf.
- number_format()number_format(1234567.891, 2) возвращает "1,234,567.89": функция округляет до заданного числа знаков и добавляет разделитель тысяч. Четыре параметра, европейский и японский форматы, как она округляет и почему результат это строка.
- Перенос строкиЧтобы вывести новую строку в PHP, напишите "\n" внутри двойных кавычек или используйте PHP_EOL. На веб-странице перенос строки не виден, поэтому там нужен <br> или nl2br(). Разница между ними, почему '\n' в одинарных кавычках не работает и как разбить текст по строкам или убрать переносы.
- preg_match()preg_match($pattern, $subject, $matches) проверяет строку по регулярному выражению и возвращает 1 при совпадении и 0, если совпадения нет. Группы захвата, именованные группы, preg_match_all, якоря, шаблоны для проверки и модификатор u для UTF-8, кириллицы и японского текста.
- preg_replace()preg_replace($pattern, $replacement, $subject) заменяет каждое совпадение регулярного выражения: preg_replace('/\s+/', ' ', $text) схлопывает пробелы. Обратные ссылки вроде $1, preg_replace_callback, удаление всего, кроме букв и цифр, ограничение и счётчик замен.
Функции
- ФункцииФункция PHP объявляется так: function name($param) { return $value; }. Возвращаемые значения, параметры по умолчанию, именованные аргументы, передача по ссылке, объявления типов со strict_types, nullable и union-типы и вариативные ...$args.
- Стрелочные функцииСтрелочная функция это анонимная функция из одного выражения вида fn($x) => $x * 2. Она использует внешние переменные автоматически, по значению, без use. Синтаксис, как работает захват, типы, колбэки для array_map и usort и когда лучше взять полноценное замыкание.
- Анонимные функцииАнонимная функция (замыкание) это функция без имени, сохранённая в переменной: $greet = function ($name) { return "Hi $name"; };. Ключевое слово use, захват по ссылке, callable, синтаксис first-class callable strlen(...) и замыкания внутри классов.
- ГенераторыГенератор это функция, которая через yield отдаёт значения по одному, поэтому foreach может пройти миллион элементов, не создавая массив. yield, ключи, экономия памяти, бесконечные последовательности, yield from, send() и getReturn().
Классы и объекты
- Классы и объектыКласс PHP это шаблон, объявленный через `class Name { }`, а `new Name()` создаёт по нему объект. Свойства, методы, `$this`, public/private/protected, типизированные и readonly-свойства, `__toString` и почему присваивание объекта его не копирует.
- КонструкторыКонструктор PHP это метод `__construct()`, который выполняется автоматически, когда вы пишете `new ClassName(...)`. Продвижение свойств в конструкторе, значения по умолчанию и именованные аргументы, проверка аргументов, `parent::__construct()` и как заменить несколько конструкторов статическими фабричными методами.
- НаследованиеВ PHP класс наследует от другого через `class Dog extends Animal`: он получает публичные и защищённые свойства и методы родителя, может их переопределять и всё равно вызывать оригинал через `parent::method()`. Переопределение, protected, final, instanceof и ловушка с private-методами.
- ИнтерфейсыИнтерфейс PHP перечисляет методы, которые класс обещает иметь: `interface Shape { public function area(): float; }`, и тогда `class Circle implements Shape` обязан определить `area()`. Подсказки типов через интерфейсы, реализация нескольких интерфейсов, константы интерфейсов, наследование интерфейсов, встроенные интерфейсы вроде Countable и сравнение с абстрактным классом.
- Абстрактные классыАбстрактный класс PHP объявляется как `abstract class Name`, и создать его объект нельзя: он существует, чтобы его наследовали. Он может содержать свойства, конструктор и готовые методы, а также `abstract`-методы без тела, которые обязан реализовать каждый дочерний класс.
- ТрейтыТрейт PHP это переиспользуемый блок методов и свойств, который копируется в класс через `use TraitName;`. Несколько трейтов, разрешение конфликтов имён через `insteadof` и `as`, абстрактные и статические члены трейта, чей метод побеждает и сравнение трейта, интерфейса и абстрактного класса.
- Перечисления enumПеречисление PHP (8.1+) это тип с фиксированным списком значений: `enum Status: string { case Active = 'active'; }`. Чистые и типизированные перечисления, `->value` и `->name`, преобразование через `from()` и `tryFrom()`, список вариантов через `cases()`, методы, константы, интерфейсы и перечисления в `match`.
- Статические методыМетод или свойство `static` принадлежит классу, а не объекту: метод вызывается через `ClassName::method()`, свойство читается через `ClassName::$count`, и `new` не нужен. `self::` и `static::` (позднее статическое связывание), `new static`, статические переменные в функциях и когда static это неправильный инструмент.
- Пространства имёнПространство имён PHP группирует классы, функции и константы под общим префиксом: `namespace App\Models;` в начале файла, а в другом месте `use App\Models\User;`, чтобы обращаться к классу по короткому имени. Полные имена, псевдонимы `use ... as`, `use function`, обратный слеш для глобальных классов и автозагрузка PSR-4.
Ошибки и исключения
- try catchВ PHP код, который может упасть, помещают в блок `try { }`, а `catch (Exception $e) { }` выполняется, если он выбросил исключение, и причина лежит в `$e->getMessage()`. Перехват нескольких типов исключений, `finally`, почему `catch (Exception)` пропускает `TypeError` и `DivisionByZeroError` (перехватывайте `Throwable`), повторный выброс и глобальный обработчик.
- ИсключенияИсключение в PHP выбрасывается через `throw new Exception('message');`, а вызывающий код читает его через `$e->getMessage()` в блоке `catch`. Встроенные классы исключений, собственные исключения, `getCode()`, `getLine()` и `getPrevious()`, `throw` как выражение и ошибки, которые выбрасывает сам PHP, например `ValueError` и `TypeError`.
Данные, файлы и веб
- json_encode()json_encode($value) превращает массив, объект или скаляр PHP в строку JSON. Ассоциативные массивы становятся JSON-объектами, списки JSON-массивами, а флаги вроде JSON_PRETTY_PRINT и JSON_UNESCAPED_UNICODE управляют выводом.
- json_decode()json_decode($json, true) превращает строку JSON в ассоциативный массив PHP; без true вы получаете объекты stdClass. Вложенные значения, обработка ошибок через JSON_THROW_ON_ERROR, json_validate() и почему json_decode возвращает null.
- date()date('Y-m-d') возвращает сегодняшнюю дату строкой, а date('Y-m-d', $timestamp) форматирует любую метку времени Unix. time(), mktime(), strtotime(), DateTimeImmutable, прибавление дней, разница между двумя датами и часовые пояса на примерах, которые можно запустить.
- Формат датыФормат даты в PHP это строка из букв вроде 'Y-m-d H:i:s', где Y это год из 4 цифр, m месяц, d день, H час, i минуты, а s секунды. Здесь все символы формата с примерами, а также экранирование, ISO 8601, японские даты и преобразование одного формата в другой.
- file_get_contents()file_get_contents('notes.txt') читает весь файл и возвращает его одной строкой или false, если не может. Её пара file_put_contents() записывает строку в файл, а file() возвращает строки в виде массива.
- fopen()fopen('data.txt', 'r') открывает файл и возвращает дескриптор или false при сбое. Читайте из дескриптора через fgets() или fread(), пишите через fwrite(), закрывайте через fclose(). Режим ('r', 'w', 'a', 'x', 'c' и их формы с +) определяет, что можно делать.
- include и requireinclude 'header.php'; выполняет другой PHP-файл в этом месте, будто его код вставлен сюда. require делает то же, но останавливает скрипт фатальной ошибкой, если файла нет, а include лишь предупреждает и продолжает работу. include_once и require_once подключают файл только в первый раз.
- ФормыPHP-форма отправляется сама на себя через <form method="post">, а скрипт читает поля из $_POST['name'], когда $_SERVER['REQUEST_METHOD'] равен 'POST'. Проверка данных, сообщения об ошибках рядом с полями, сохранение введённых значений, чекбоксы и списки select и экранирование вывода.
- $_GET и $_POST$_GET хранит строку запроса URL (?page=2 даёт $_GET['page']), а $_POST хранит поля формы, отправленной с method="post". Как безопасно читать оба, строить ссылки через http_build_query, проверять $_SERVER['REQUEST_METHOD'] и выбирать между GET и POST.
- htmlspecialchars()htmlspecialchars($text) превращает пять символов, которые что-то значат в HTML (& < > " '), в сущности, поэтому пользовательский ввод показывается как текст, а не выполняется как разметка. ENT_QUOTES, экранирование атрибутов, двойное кодирование, htmlentities и strip_tags.
- Сессии и cookieВызовите session_start() в начале каждой страницы и храните значения в $_SESSION['key']; PHP держит их на сервере и узнаёт посетителя по cookie. setcookie('name', 'value', $options) сохраняет небольшое значение в браузере, а прочитать его можно из $_COOKIE. Вход, выход, flash-сообщения, параметры cookie и настройки безопасности.
- password_hash()Хешируйте пароль через password_hash($password, PASSWORD_DEFAULT), храните результат из 60 символов и проверяйте вход через password_verify($password, $hash). Почему хеш каждый раз разный, cost, password_needs_rehash, Argon2id и почему md5 и sha1 не подходят для паролей.
- filter_var()filter_var($value, FILTER_VALIDATE_EMAIL) возвращает значение, если оно корректно, и false, если нет; так же работают FILTER_VALIDATE_INT, FILTER_VALIDATE_URL, FILTER_VALIDATE_BOOL и FILTER_VALIDATE_IP. Опции (min_range, default), флаги, фильтры очистки и ловушка false и 0.
- PDOPDO это слой PHP для работы с базами данных: подключитесь через new PDO('mysql:host=localhost;dbname=shop;charset=utf8mb4', $user, $pass), а затем выполняйте каждый запрос со значениями через prepare() и execute([$value]). Подключение, подготовленные запросы, получение строк, вставка, транзакции и WHERE IN.
- ComposerComposer это менеджер пакетов PHP: composer require vendor/package скачивает библиотеку в vendor/, записывает её в composer.json, а require 'vendor/autoload.php' её подключает. Установка Composer, composer.json, ограничения версий, install и update и автозагрузка PSR-4 для своих классов.
Отладка и практика
- var_dump() и print_r()print_r($array) выводит массив в читаемом виде, var_dump($value) показывает ещё тип и длину каждого значения, а var_export выводит корректный PHP-код. Когда что использовать, как получить вывод print_r строкой и как красиво вывести массив в HTML через <pre>.
- Вопросы на собеседовании24 вопроса на собеседовании по PHP с короткими правильными ответами для PHP 8: от == и === и isset и empty до ссылок в foreach, self и static, генераторов, SQL-инъекций и хеширования паролей. У большинства ответов есть код, который можно запустить и изменить.