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

Дерево руководства
Глава 93 · От основ к практике
URL: ссылки, query, Base64 и метаданные HTTP
Полные контракты URL-функций с постепенными примерами: что кодируется, что проверяется, где уже снят слой кодирования.
Поиск «C++ tea» не должен превращаться в другие параметры
Знак + остаётся частью поискового запроса
Пользователь ищет C++ tea. Название должно пройти через URL и восстановиться без изменения.
- 1q = C++ tea↓
Два плюса и пробел относятся к данным пользователя.
- 2http_build_query↓
Плюсы превращаются в %2B, пробел при RFC3986 — в %20.
- 3q=C%2B%2B%20tea↓
Query помещается после ? в известный маршрут /search.
- 4Приём PHP
$_GET['q'] уже содержит C++ tea. Лишний urldecode испортил бы плюсы.
Query-кодирование и HTML-экранирование — разные этапы. Перед вставкой готового адреса в href экранируют весь атрибут.
Посетитель вводит название товара, а сайт строит ссылку на результаты. Введённый плюс, амперсанд или знак вопроса должен остаться частью названия, а не стать разделителем URL. Эта категория помогает собрать адрес, разобрать старый формат, перенести бинарные данные текстом и получить определённые метаданные HTTP/HTML.
Имя функции со скобками означает вызов: данные внутри скобок — аргументы, а результат можно сохранить через = в переменную с $. Строки записываются в кавычках; echo выводит текст, "\n" — перевод строки. Точка . склеивает строки. Массив ["q" => "чай"] хранит значение «чай» под ключом q. var_dump дополнительно показывает тип.
В проверках === false означает именно булев отказ, а не пустую строку или ноль. if выбирает действие по условию; throw new RuntimeException прекращает обычный путь при неожиданном отказе. foreach берёт элементы массива по очереди. Переменные, массивы и вызов и возврат функции можно повторить отдельно.
- Значение q: C++ tea
- http_build_query кодирует именно значение
- /search?q=C%2B%2B%20tea&page=2
- PHP на принимающей стороне видит q как C++ tea
<?php
declare(strict_types=1);
$query = http_build_query(['q' => 'C++ tea', 'page' => 2], '', '&', PHP_QUERY_RFC3986);
$url = '/search?' . $query;
echo $url, "\n";
Ожидаемый результат
/search?q=C%2B%2B%20tea&page=2
Число 2 здесь становится текстом в query. При приёме формы/параметров его снова проверяют как допустимый номер страницы, а не считают автоматически доверенным целым.
Сетевых запросов в этом примере нет: создаётся только строка. Как HTML-поля попадают в PHP показывает принимающую сторону, а проверка внешних значений — различие формата и допустимости.
Что именно преобразуется: адрес, параметр, HTML или байты
- Сначала значения → query с %HH
- Затем полный готовый URL
- Перед HTML-атрибутом — htmlspecialchars
- Браузер получает правильное значение href
| Задача | Инструмент и граница |
|---|---|
| Несколько параметров запроса | http_build_query строит query без ?. Не сохраняет все типы PHP. |
| Один сегмент пути | rawurlencode кодирует компонент; разделяющие сегменты / добавляют отдельно. |
| Одно значение form-urlencoded | urlencode использует + для пробела. |
| Разобрать старый URL | parse_url выделяет части, но не валидирует адрес. Для PHP8.5+ есть стандартные URI-классы. |
| Бинарный набор в текстовом API | base64_encode и base64_decode по договору формата. Это не шифрование. |
| Получить HTTP-заголовки | get_headers выполняет запрос: нужны сеть, timeout и политика адресов. |
| Именованные meta из HTML | get_meta_tags читает файл до , не выполняет JavaScript. |
<?php
declare(strict_types=1);
$url = '/search?' . http_build_query(['q' => 'tea & cake', 'page' => 2], '', '&', PHP_QUERY_RFC3986);
echo '<a href="' . htmlspecialchars($url, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '">Search</a>', "\n";
Ожидаемый результат
<a href="/search?q=tea%20%26%20cake&page=2">Search</a>
%26 внутри q и & между HTML-параметрами выполняют разные задачи. Не храните заранее HTML-экранированный URL как адрес HTTP-клиента.
Все десять констант официальной категории
Константа — именованная настройка. Её имя пишется без $ и без кавычек. Восемь PHP_URL_* выбирают компонент parse_url, две PHP_QUERY_* — представление параметров в http_build_query.
- Адрес содержит :8443
- PHP_URL_PORT выбирает порт
- parse_url возвращает int 8443
- Отсутствующий порт дал бы null
| Константа | Что выбирает / результат |
|---|---|
| PHP_URL_SCHEME | Схема, например https; строка |
| PHP_URL_HOST | Имя узла; строка |
| PHP_URL_PORT | Порт; int, не строка |
| PHP_URL_USER | Пользователь из userinfo; строка, не подтверждённая авторизация |
| PHP_URL_PASS | Пароль из userinfo; не выводить в журнал |
| PHP_URL_PATH | Путь с сохранёнными %HH |
| PHP_URL_QUERY | Часть после ?, без самого ? |
| PHP_URL_FRAGMENT | Часть после #, без # |
| PHP_QUERY_RFC1738 | Form-urlencoded: пробел кодируется + |
| PHP_QUERY_RFC3986 | Пробел кодируется %20; полезно для согласованной RFC3986 query |
Отсутствующие выбранные компоненты дают null; parse_url с серьёзно неверным адресом может вернуть false. Без выбора компонента вернётся массив только найденных частей. С PHP 8.0 пустые query/fragment отличаются от отсутствующих.
Декодирование не является проверкой безопасности или схемы
Приложение должно знать, какой слой уже снял веб-сервер/PHP. В $_GET и обычном $_POST значения уже разобраны по правилам формы. Повторный urldecode может заменить настоящий плюс пробелом, а второй rawurldecode — превратить закодированный %2F в разделитель пути.
- Передали буквальный текст A%2FB
- В запросе он записан A%252FB
- PHP уже восстановил A%2FB
- Лишний декодер делает A/B — уже другое значение
<?php
declare(strict_types=1);
parse_str('q=C%2B%2B', $received);
echo $received['q'], "\n";
echo '[' . urldecode($received['q']) . "]\n";
Ожидаемый результат
C++
[C ]
parse_str моделирует разбор query. На реальном запросе PHP выполняет этот этап до обращения к $_GET. Сначала определяют формат и число слоёв, затем проверяют значение.
Percent/Base64 не проверяют полномочия, допустимость пути, HTML-контекст или назначение HTTP-запроса. Для ссылки «вернуться после входа» обычно выбирают разрешённый локальный маршрут; произвольный внешний redirect нуждается в отдельной политике. Парсер и сетевой клиент должны одинаково понимать адрес, включая редиректы и Unicode.
В PHP 8.5 доступны Uri\Rfc3986\Uri и Uri\WhatWg\Url. Выбор стандарта связан с задачей: серверная URI-обработка и браузерные URL могут иметь разные правила. Новую разработку не следует строить на случайных эвристиках parse_url.
Стоимость, сеть и границы учебных примеров
- Кодирование строки работает с памятью процесса
- Размер результата зависит от входа и формата
- get_headers/get_meta_tags(URL) добавляют сеть и ожидание
- Ограничения размера/времени задаёт приложение
Base64 увеличивает объём примерно на треть, percent-encoding может потребовать три ASCII-байта на один исходный байт. Большие вложенные массивы дают длинную query, которую могут ограничить браузер, прокси или сервер. Для больших данных обычно выбирают тело запроса и согласованный JSON/multipart, а не бесконечную ссылку.
get_headers на каждом показе карточки сайта создаёт дополнительную зависимость от сети. Кэшируйте нужные метаданные по правилам продукта и обрабатывайте недоступность источника. Полученный HTTP200 подтверждает ответ endpoint, а не корректность всех функций сервиса.
Примеры get_headers требуют локального HTTP fixture и отдельно проверены через него. Обычная проверка PHP-синтаксиса без сервера не выдаётся за сетевой тест. Локальные get_meta_tags читают приложенные HTML-файлы; это не проверка произвольного сайта, HTML5-парсинга или браузерного JavaScript.
Практика: название товара с плюсом, слешем и амперсандом
Соберите ссылку на поиск A/B + tea & cake с фильтром available=true. Сначала получите query, затем HTML-ссылку, а получатель должен увидеть исходную строку целиком. Не кодируйте весь URL, не удаляйте специальные символы и не используйте повторный urldecode.
- Исходное q не изменилось
- available передан как 1
- HTML-атрибут экранирован отдельно
- При разборе q восстановлен точно
<?php
declare(strict_types=1);
$original = 'A/B + tea & cake';
$query = http_build_query(['q' => $original, 'available' => true], '', '&', PHP_QUERY_RFC3986);
echo $query, "\n";
parse_str($query, $received);
var_dump($received['q'] === $original, $received['available'] === '1');
Ожидаемый результат
q=A%2FB%20%2B%20tea%20%26%20cake&available=1
bool(true)
bool(true)
Принимающая сторона получила текст "1", а не PHP bool. Далее проверяют, допустим ли такой фильтр, и используют подготовленные параметры запроса к базе. Следующий шаг — работа с базой.
Полный указатель функций URL
| Функция | Назначение |
|---|---|
| base64_decode | Восстановить байты из Base64 и явно отличить ошибку от пустого результата. |
| base64_encode | Передать произвольные байты в текстовом поле по договору Base64. |
| get_headers | Получить HTTP-заголовки ответа собственного известного endpoint и отдельно оценить статус. |
| get_meta_tags | Прочитать именованные meta-теги из HTML-файла, например описание страницы своего каталога. |
| http_build_query | Превратить набор параметров в query-строку, не собирая вручную амперсанды и escape-последовательности. |
| parse_url | Разобрать совместимый старый URL-код на компоненты, не принимая разбор за валидацию адреса. |
| rawurldecode | Раскодировать %HH, оставив знак + обычным плюсом. |
| rawurlencode | Закодировать один сегмент пути или компонент по RFC 3986. |
| urldecode | Раскодировать значение формы/query: %HH в байты, + в пробел. |
| urlencode | Закодировать одно значение формы/query по application/x-www-form-urlencoded. |
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
base64_encodeПередать произвольные байты в текстовом поле по договору Base64.
PHP 8.2+ · standard
base64_encode(string $string): stringПередать произвольные байты в текстовом поле по договору Base64.
Имя функции со скобками означает вызов: данные внутри скобок — аргументы, а результат можно сохранить через = в переменную с $. Строки записываются в кавычках; echo выводит текст, "\n" — перевод строки. Точка . склеивает строки. Массив ["q" => "чай"] хранит значение «чай» под ключом q. var_dump дополнительно показывает тип.
В проверках === false означает именно булев отказ, а не пустую строку или ноль. if выбирает действие по условию; throw new RuntimeException прекращает обычный путь при неожиданном отказе. foreach берёт элементы массива по очереди. Переменные, массивы и вызов и возврат функции можно повторить отдельно.
| Часть вызова | Значение |
|---|---|
| string | Исходная строка байтов: может быть текстом UTF-8 или бинарным содержимым с NUL. |
| Возврат | Строка стандартного Base64: буквы, цифры, +, / и возможное завершающее =. Она примерно на треть длиннее входа: 4 × ceil(N/3). |
| Границы | Кодирование обратимо без ключа: не скрывает пароли и не подтверждает подлинность. Это не Base64url и не автоматическое URL-кодирование. |
- Байты PHP
- Группы по три байта превращаются в четыре символа
- Результат UEhQ можно поместить в текстовое поле
- Получатель декодирует обратно ровно PHP
<?php
declare(strict_types=1);
$encoded = base64_encode('PHP');
echo $encoded, "\n";
echo base64_decode($encoded, true), "\n";
Ожидаемый результат
UEhQ
PHP
Упаковка видима, но не секретна. Любой получатель строки UEhQ может восстановить исходный текст.
<?php
declare(strict_types=1);
$bytes = "A\0B";
$encoded = base64_encode($bytes);
echo $encoded, "\n";
echo bin2hex(base64_decode($encoded, true)), "\n";
echo http_build_query(['data' => $encoded], '', '&', PHP_QUERY_RFC3986), "\n";
Ожидаемый результат
QQBC
410042
data=QQBC
bin2hex делает бинарный результат наблюдаемым. В URL Base64 всё равно помещают через построитель query: другие данные могут дать +, / и =.
<?php
declare(strict_types=1);
$encoded = base64_encode(hex2bin('fbff'));
echo $encoded, "\n";
$query = http_build_query(['blob' => $encoded], '', '&', PHP_QUERY_RFC3986);
echo $query, "\n";
parse_str($query, $received);
var_dump($received['blob'] === $encoded);
Ожидаемый результат
+/8=
blob=%2B%2F8%3D
bool(true)
hex2bin превращает известную шестнадцатеричную запись в байты. parse_str здесь моделирует декодирование query PHP. Base64url использует - и _ вместо + и /, а правило padding задаётся протоколом; это отдельный вариант.
Для большого файла полное чтение плюс Base64-копия увеличивают память. Если API принимает бинарный upload/multipart или поток — лишняя Base64-упаковка может не требоваться. Не вставляйте декодированный HTML без экранирования: смена представления не делает содержимое доверенным.
Связанные инструменты: base64_decode; http_build_query; urlencode.
base64_decodeВосстановить байты из Base64 и явно отличить ошибку от пустого результата.
PHP 8.2+ · standard
base64_decode(string $string, bool $strict = false): string|falseВосстановить байты из Base64 и явно отличить ошибку от пустого результата.
Имя функции со скобками означает вызов: данные внутри скобок — аргументы, а результат можно сохранить через = в переменную с $. Строки записываются в кавычках; echo выводит текст, "\n" — перевод строки. Точка . склеивает строки. Массив ["q" => "чай"] хранит значение «чай» под ключом q. var_dump дополнительно показывает тип.
В проверках === false означает именно булев отказ, а не пустую строку или ноль. if выбирает действие по условию; throw new RuntimeException прекращает обычный путь при неожиданном отказе. foreach берёт элементы массива по очереди. Переменные, массивы и вызов и возврат функции можно повторить отдельно.
| Часть вызова | Значение |
|---|---|
| string | Текст стандартного Base64, не автоматически Base64url или data: URL целиком. |
| strict | По умолчанию посторонние символы отбрасываются. true включает проверку недопустимых символов/формата, но не проверяет бизнес-схему, криптографическую подлинность или каноничность представления. |
| Возврат | Исходные байты как строка либо false при отказе. Пустая строка допустима; результат может содержать NUL и не быть UTF-8. |
- Пришёл UEhQ
- base64_decode(..., true) разбирает Base64
- Получена строка PHP либо false
- Отдельно проверяются размер и смысл восстановленных данных
<?php
declare(strict_types=1);
$decoded = base64_decode('UEhQ', true);
if ($decoded === false) { throw new RuntimeException('Invalid Base64'); }
echo $decoded, "\n";
var_dump(base64_decode('', true));
Ожидаемый результат
PHP
string(0) ""
Пустой корректный конверт возвращает пустую строку. Проверка if (!$decoded) спутала бы её с отказом.
<?php
declare(strict_types=1);
var_dump(base64_decode('UE!hQ'));
var_dump(base64_decode('UE!hQ', true));
Ожидаемый результат
string(3) "PHP"
bool(false)
Без strict восклицательный знак исчез. В интеграции такие потери могут скрыть повреждение сообщения; сначала согласуйте допустимый формат.
<?php
declare(strict_types=1);
foreach (['QQ==', 'QR==', 'QQ'] as $text) {
$bytes = base64_decode($text, true);
$canonical = $bytes !== false && base64_encode($bytes) === $text;
echo $text, ': ', $canonical ? 'canonical' : 'different representation', "\n";
}
Ожидаемый результат
QQ==: canonical
QR==: different representation
QQ: different representation
В PHP 8.5 strict ещё принимает эти неканонические варианты. Сравнение обратного кодирования дополнительно требует стандартный алфавит, padding и точную запись. Для протокола без padding/с разрешёнными пробелами нужна именно его политика, а не этот критерий.
Прежде чем декодировать огромный внешний ввод, ограничьте длину кодированной строки, затем размер восстановленного содержимого. Не «исправляйте» неизвестный Base64 заменой всех пробелов на +: это может скрыть ошибку переноса через форму. Исправьте кодирование на стороне отправителя.
Связанные инструменты: base64_encode; http_build_query; urldecode.
urlencodeЗакодировать одно значение формы/query по application/x-www-form-urlencoded.
PHP 8.2+ · standard
urlencode(string $string): stringЗакодировать одно значение формы/query по application/x-www-form-urlencoded.
Имя функции со скобками означает вызов: данные внутри скобок — аргументы, а результат можно сохранить через = в переменную с $. Строки записываются в кавычках; echo выводит текст, "\n" — перевод строки. Точка . склеивает строки. Массив ["q" => "чай"] хранит значение «чай» под ключом q. var_dump дополнительно показывает тип.
В проверках === false означает именно булев отказ, а не пустую строку или ноль. if выбирает действие по условию; throw new RuntimeException прекращает обычный путь при неожиданном отказе. foreach берёт элементы массива по очереди. Переменные, массивы и вызов и возврат функции можно повторить отдельно.
| Часть вызова | Значение |
|---|---|
| string | Отдельный компонент, а не весь адрес со схемой, слешами, ? и #. |
| Возврат | Байты кроме ASCII-букв/цифр и -_. заменяются %HH. Пробел становится +, настоящий плюс %2B; ~ кодируется как %7E. |
| Кодировка | Функция кодирует существующие байты, не переводит текст из одной кодировки в другую. Для Unicode заранее согласуйте UTF-8. |
| Граница | Не защищает HTML-атрибут сам по себе и не проверяет схему/домен URL. Повторное кодирование превратит % в %25. |
- Текст tea + cake содержит пробелы и плюс
- Пробелы получают +
- Настоящий плюс получает %2B
- Получатель восстанавливает данные по тому же договору
<?php
declare(strict_types=1);
echo urlencode('tea + cake'), "\n";
echo urldecode(urlencode('tea + cake')), "\n";
Ожидаемый результат
tea+%2B+cake
tea + cake
Разница между пробелом и знаком + сохраняется. Это не замена пробелов дефисами для красивого slug.
<?php
declare(strict_types=1);
$query = 'C++ tea';
$url = '/search?q=' . urlencode($query);
echo $url, "\n";
echo htmlspecialchars($url, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'), "\n";
Ожидаемый результат
/search?q=C%2B%2B+tea
/search?q=C%2B%2B+tea
Для нескольких параметров предпочтительнее http_build_query. Перед вставкой готового адреса в HTML выполняется отдельное HTML-экранирование; оно не заменяет кодирование параметра.
<?php
declare(strict_types=1);
$once = urlencode('A/B');
echo $once, "\n";
echo urlencode($once), "\n";
Ожидаемый результат
A%2FB
A%252FB
Вторая строка описывает буквальный текст A%2FB. Повторное кодирование не «усиливает защиту», а меняет значение.
Связанные инструменты: urldecode; http_build_query; rawurlencode.
rawurlencodeЗакодировать один сегмент пути или компонент по RFC 3986.
PHP 8.2+ · standard
rawurlencode(string $string): stringЗакодировать один сегмент пути или компонент по RFC 3986.
Имя функции со скобками означает вызов: данные внутри скобок — аргументы, а результат можно сохранить через = в переменную с $. Строки записываются в кавычках; echo выводит текст, "\n" — перевод строки. Точка . склеивает строки. Массив ["q" => "чай"] хранит значение «чай» под ключом q. var_dump дополнительно показывает тип.
В проверках === false означает именно булев отказ, а не пустую строку или ноль. if выбирает действие по условию; throw new RuntimeException прекращает обычный путь при неожиданном отказе. foreach берёт элементы массива по очереди. Переменные, массивы и вызов и возврат функции можно повторить отдельно.
| Часть вызова | Значение |
|---|---|
| string | Отдельный компонент, а не весь адрес со схемой, слешами, ? и #. |
| Возврат | Байты кроме ASCII-букв/цифр и -_.~ заменяются %HH. Пробел становится %20, настоящий плюс %2B. |
| Кодировка | Функция кодирует существующие байты, не переводит текст из одной кодировки в другую. Для Unicode заранее согласуйте UTF-8. |
| Граница | Не защищает HTML-атрибут сам по себе и не проверяет схему/домен URL. Повторное кодирование превратит % в %25. |
- Текст tea + cake содержит пробелы и плюс
- Пробелы получают %20
- Настоящий плюс получает %2B
- Получатель восстанавливает данные по тому же договору
<?php
declare(strict_types=1);
echo rawurlencode('tea + cake'), "\n";
echo rawurldecode(rawurlencode('tea + cake')), "\n";
Ожидаемый результат
tea%20%2B%20cake
tea + cake
Разница между пробелом и знаком + сохраняется. Это не замена пробелов дефисами для красивого slug.
<?php
declare(strict_types=1);
$category = rawurlencode('tea sets');
$product = rawurlencode('A/B');
echo '/catalog/' . $category . '/' . $product, "\n";
Ожидаемый результат
/catalog/tea%20sets/A%2FB
Слеш внутри A/B закодирован как часть одного сегмента. Но веб-сервер/роутер могут отдельно обрабатывать %2F: допустимость таких идентификаторов задаётся маршрутом. Не кодируйте всю строку https://… одной функцией.
<?php
declare(strict_types=1);
$once = rawurlencode('A/B');
echo $once, "\n";
echo rawurlencode($once), "\n";
Ожидаемый результат
A%2FB
A%252FB
Вторая строка описывает буквальный текст A%2FB. Повторное кодирование не «усиливает защиту», а меняет значение.
Связанные инструменты: rawurldecode; http_build_query; urlencode.
urldecodeРаскодировать значение формы/query: %HH в байты, + в пробел.
PHP 8.2+ · standard
urldecode(string $string): stringРаскодировать значение формы/query: %HH в байты, + в пробел.
Имя функции со скобками означает вызов: данные внутри скобок — аргументы, а результат можно сохранить через = в переменную с $. Строки записываются в кавычках; echo выводит текст, "\n" — перевод строки. Точка . склеивает строки. Массив ["q" => "чай"] хранит значение «чай» под ключом q. var_dump дополнительно показывает тип.
В проверках === false означает именно булев отказ, а не пустую строку или ноль. if выбирает действие по условию; throw new RuntimeException прекращает обычный путь при неожиданном отказе. foreach берёт элементы массива по очереди. Переменные, массивы и вызов и возврат функции можно повторить отдельно.
| Часть вызова | Значение |
|---|---|
| string | Один ещё закодированный компонент, не весь URL перед разделением. |
| Возврат | Строка с раскодированными %HH и пробелами вместо +. |
| Неправильный ввод | Неполные/неhex последовательности % не являются строгой ошибкой: обычно остаются как текст. Результат может содержать NUL/невалидный UTF-8. |
| Суперглобальные | PHP уже раскодировал query в $_GET и обычное form-urlencoded тело в $_POST. Повторный вызов часто портит исходное значение. |
- Пришло tea+cake%20%2B%20milk
- %20 превращается в пробел, %2B в плюс
- Исходный + превращается в пробел
- Результат ещё требует проверки назначения
<?php
declare(strict_types=1);
echo urldecode('tea+cake%20%2B%20milk'), "\n";
Ожидаемый результат
tea cake + milk
Декодер выбирают по формату отправителя. Query формы и сегмент пути могут использовать разные правила.
<?php
declare(strict_types=1);
$once = urldecode('A%252FB');
echo $once, "\n";
echo urldecode($once), "\n";
echo urldecode('%ZZ%2'), "\n";
Ожидаемый результат
A%2FB
A/B
%ZZ%2
После второго вызова появляется новый разделитель /. Нельзя сначала проверять путь, а затем раскодировать его ещё раз и использовать уже изменившееся значение.
<?php
declare(strict_types=1);
echo bin2hex(urldecode('A%00B')), "\n";
Ожидаемый результат
410042
Декодирование — преобразование, не очистка. Для файловых путей нулевой байт недопустим; для HTML всё равно требуется htmlspecialchars.
Связанные инструменты: urlencode; parse_url; http_build_query.
rawurldecodeРаскодировать %HH, оставив знак + обычным плюсом.
PHP 8.2+ · standard
rawurldecode(string $string): stringРаскодировать %HH, оставив знак + обычным плюсом.
Имя функции со скобками означает вызов: данные внутри скобок — аргументы, а результат можно сохранить через = в переменную с $. Строки записываются в кавычках; echo выводит текст, "\n" — перевод строки. Точка . склеивает строки. Массив ["q" => "чай"] хранит значение «чай» под ключом q. var_dump дополнительно показывает тип.
В проверках === false означает именно булев отказ, а не пустую строку или ноль. if выбирает действие по условию; throw new RuntimeException прекращает обычный путь при неожиданном отказе. foreach берёт элементы массива по очереди. Переменные, массивы и вызов и возврат функции можно повторить отдельно.
| Часть вызова | Значение |
|---|---|
| string | Один ещё закодированный компонент, не весь URL перед разделением. |
| Возврат | Строка с раскодированными %HH. Знак + остаётся +. |
| Неправильный ввод | Неполные/неhex последовательности % не являются строгой ошибкой: обычно остаются как текст. Результат может содержать NUL/невалидный UTF-8. |
| Суперглобальные | PHP уже раскодировал query в $_GET и обычное form-urlencoded тело в $_POST. Повторный вызов часто портит исходное значение. |
- Пришло tea+cake%20%2B%20milk
- %20 превращается в пробел, %2B в плюс
- Исходный + сохраняется
- Результат ещё требует проверки назначения
<?php
declare(strict_types=1);
echo rawurldecode('tea+cake%20%2B%20milk'), "\n";
Ожидаемый результат
tea+cake + milk
Декодер выбирают по формату отправителя. Query формы и сегмент пути могут использовать разные правила.
<?php
declare(strict_types=1);
$once = rawurldecode('A%252FB');
echo $once, "\n";
echo rawurldecode($once), "\n";
echo rawurldecode('%ZZ%2'), "\n";
Ожидаемый результат
A%2FB
A/B
%ZZ%2
После второго вызова появляется новый разделитель /. Нельзя сначала проверять путь, а затем раскодировать его ещё раз и использовать уже изменившееся значение.
<?php
declare(strict_types=1);
echo bin2hex(rawurldecode('A%00B')), "\n";
Ожидаемый результат
410042
Декодирование — преобразование, не очистка. Для файловых путей нулевой байт недопустим; для HTML всё равно требуется htmlspecialchars.
Связанные инструменты: rawurlencode; parse_url; http_build_query.
http_build_queryПревратить набор параметров в query-строку, не собирая вручную амперсанды и escape-последовательности.
PHP 8.2+ · standard
http_build_query(array|object $data, string $numeric_prefix = "", ?string $arg_separator = null, int $encoding_type = PHP_QUERY_RFC1738): stringПревратить набор параметров в query-строку, не собирая вручную амперсанды и escape-последовательности.
Имя функции со скобками означает вызов: данные внутри скобок — аргументы, а результат можно сохранить через = в переменную с $. Строки записываются в кавычках; echo выводит текст, "\n" — перевод строки. Точка . склеивает строки. Массив ["q" => "чай"] хранит значение «чай» под ключом q. var_dump дополнительно показывает тип.
В проверках === false означает именно булев отказ, а не пустую строку или ноль. if выбирает действие по условию; throw new RuntimeException прекращает обычный путь при неожиданном отказе. foreach берёт элементы массива по очереди. Переменные, массивы и вызов и возврат функции можно повторить отдельно.
| Часть вызова | Значение |
|---|---|
| data | Массив, включая вложенные массивы, либо объект с публичными свойствами. __toString не вызывается автоматически. null и пустые вложенные массивы не создают параметров; bool становится 1/0. |
| numeric_prefix | Префикс числовых ключей только верхнего уровня. Не переименовывает все вложенные индексы. |
| arg_separator | Разделитель параметров. null (с 8.0) берёт arg_separator.output; для сетевого URL здесь явно "&". HTML-экранирование делают позднее для всего атрибута. |
| encoding_type | PHP_QUERY_RFC1738: пробел +. PHP_QUERY_RFC3986: пробел %20. Передавайте документированную константу. |
| Возврат | Строка без начального ?. Это не полный URL и не произвольный JSON-сериализатор. |
| Версии | С 8.4 BackedEnum внутри data преобразуется в скалярное backing value. Pure enum не имеет такого значения. |
- q содержит C++ tea
- page содержит число 2
- Построитель кодирует ключи и значения
- Получаем q=C%2B%2B%20tea&page=2
<?php
declare(strict_types=1);
$parameters = ['q' => 'C++ tea', 'page' => 2];
$query = http_build_query($parameters, '', '&', PHP_QUERY_RFC3986);
echo $query, "\n";
echo '/search?' . $query, "\n";
Ожидаемый результат
q=C%2B%2B%20tea&page=2
/search?q=C%2B%2B%20tea&page=2
Символ ? добавляет ваше приложение между известным путём и query. Амперсанд отделяет параметры, а не является частью q.
<?php
declare(strict_types=1);
$parameters = ['filter' => ['stock' => true], 'tags' => ['tea', 'gift'], 0 => 'preview'];
echo http_build_query($parameters, 'flag_', '&', PHP_QUERY_RFC3986), "\n";
Ожидаемый результат
filter%5Bstock%5D=1&tags%5B0%5D=tea&tags%5B1%5D=gift&flag_0=preview
Квадратные скобки — соглашение PHP о вложенных query-параметрах. Другой API может требовать tags=tea&tags=gift или строку CSV: формат надо согласовать, а не считать универсальным.
<?php
declare(strict_types=1);
$parameters = ['missing' => null, 'list' => [], 'text' => '', 'enabled' => false, 'count' => 0];
$query = http_build_query($parameters, '', '&');
echo $query, "\n";
parse_str($query, $restored);
var_dump($restored['count']);
Ожидаемый результат
text=&enabled=0&count=0
string(1) "0"
Это не обратимый перенос PHP-массива: null/[] пропали, число стало текстом. Если API требует сохранить типы и пустые массивы, согласуйте JSON вместо query.
<?php
declare(strict_types=1);
class SearchLabel
{
public string $label = 'gift';
private string $internal = 'hidden';
public function __toString(): string { return 'Gift selection'; }
}
$label = new SearchLabel();
echo http_build_query(['item' => $label], '', '&'), "\n";
echo http_build_query(['item' => (string) $label], '', '&'), "\n";
Ожидаемый результат
item%5Blabel%5D=gift
item=Gift+selection
Сначала передаётся публичное свойство. Явное (string) вызывает __toString, и тогда строится обычное строковое значение. Private не попадает в query, но это не повод передавать произвольный объект с потенциально секретными public-полями.
<?php
declare(strict_types=1);
enum ViewMode: string { case Compact = 'compact'; }
echo http_build_query(['view' => ViewMode::Compact], '', '&'), "\n";
Ожидаемый результат
view=compact
В более старом PHP поведение отличалось. Для совместимой явной записи используйте ViewMode::Compact->value. Сам enum объясняется в разделе перечислений.
Для подписанного URL порядок параметров, представление пробела, дубликаты ключей и формат чисел должны совпадать с договором подписи. http_build_query сам не сортирует канонически все данные по внешнему стандарту. Не передавайте в него уже urlencode-обработанные значения: получите %25 вместо прежнего %. Объекты и вложенность обходятся рекурсивно, поэтому большой массив создаёт большую строку.
Связанные инструменты: parse_url; urlencode; rawurlencode.
parse_urlРазобрать совместимый старый URL-код на компоненты, не принимая разбор за валидацию адреса.
PHP 8.2+ · standard
parse_url(string $url, int $component = -1): int|string|array|null|falseРазобрать совместимый старый URL-код на компоненты, не принимая разбор за валидацию адреса.
Имя функции со скобками означает вызов: данные внутри скобок — аргументы, а результат можно сохранить через = в переменную с $. Строки записываются в кавычках; echo выводит текст, "\n" — перевод строки. Точка . склеивает строки. Массив ["q" => "чай"] хранит значение «чай» под ключом q. var_dump дополнительно показывает тип.
В проверках === false означает именно булев отказ, а не пустую строку или ноль. if выбирает действие по условию; throw new RuntimeException прекращает обычный путь при неожиданном отказе. foreach берёт элементы массива по очереди. Переменные, массивы и вызов и возврат функции можно повторить отдельно.
| Часть вызова | Значение |
|---|---|
| url | Строка адреса. Частичные и некорректные адреса тоже могут быть разобраны; функция не следует строго одному современному стандарту URL/URI. |
| component | Опустить/-1 — массив найденных частей. Либо одна из восьми PHP_URL_* констант в таблице ниже. |
| Возврат | Массив; выбранная строковая часть; целый port; null если выбранная часть отсутствует; false для серьёзно некорректного адреса. Неподдерживаемый component вызывает ValueError. |
| Декодирование | %HH автоматически не раскодируется. Управляющие символы в частях могут заменяться _. Это не сохранение любого исходного ввода без изменений. |
| PHP 8.0 | Отсутствующий query/fragment отличается от присутствующего, но пустого: null против "". |
| Новый код PHP 8.5+ | Официальный мануал рекомендует Uri\Rfc3986\Uri или Uri\WhatWg\Url под соответствующий стандарт. Совместимость parse_url имеет смысл сохранять осознанно. |
- https://shop.example:8443/products?q=tea#details
- scheme=https, host=shop.example, port=8443
- path=/products, query=q=tea
- fragment=details — отдельная часть, не параметр query
<?php
declare(strict_types=1);
$url = 'https://shop.example:8443/products?q=tea#details';
echo parse_url($url, PHP_URL_HOST), "\n";
echo parse_url($url, PHP_URL_PATH), "\n";
echo parse_url($url, PHP_URL_QUERY), "\n";
var_dump(parse_url($url, PHP_URL_PORT));
Ожидаемый результат
shop.example
/products
q=tea
int(8443)
Домен .example в этом опыте — только строка: сетевой запрос не выполняется. Фрагмент после # браузер обычно использует локально; он не входит в HTTP request target.
<?php
declare(strict_types=1);
var_dump(parse_url('https://shop.example/catalog', PHP_URL_QUERY));
var_dump(parse_url('https://shop.example/catalog?', PHP_URL_QUERY));
echo parse_url('/catalog/tea%20sets?q=A%2BB', PHP_URL_PATH), "\n";
Ожидаемый результат
NULL
string(0) ""
/catalog/tea%20sets
%20 остался в path. Не раскодируйте весь адрес до разделения: %2F, %3F и %23 могут стать новыми разделителями.
<?php
declare(strict_types=1);
$parts = parse_url('just some text');
echo $parts['path'], "\n";
var_dump(parse_url('https://shop.example:99999/'));
try { parse_url('https://shop.example/', 999); }
catch (ValueError $error) { echo "Unknown component\n"; }
Ожидаемый результат
just some text
bool(false)
Unknown component
Обычный текст принят как path. Это наглядная причина не считать parse_url проверкой разрешённой ссылки или безопасного назначения HTTP-запроса.
<?php
declare(strict_types=1);
echo parse_url('//shop.example/catalog', PHP_URL_HOST), "\n";
var_dump(parse_url('shop.example/catalog', PHP_URL_HOST));
echo parse_url('file:///tmp/report.txt', PHP_URL_SCHEME), "\n";
Ожидаемый результат
shop.example
NULL
file
file:// имеет историческое специальное поведение. Пример только разбирает текст и не открывает файл. Относительные URL требуют корректного разрешения относительно базового адреса; ручная склейка не заменяет стандартный URL API.
Проверка host одним парсером перед сетевым запросом другим может быть небезопасной: они способны различно разобрать адрес. Для внешних URL нужны разрешённые схема/назначение, согласованный парсер и транспорт, контроль редиректов/DNS/сетевого доступа. Сам разбор не предотвращает SSRF. Безопасность веб-приложения и современное расширение URI.
Связанные инструменты: http_build_query; rawurldecode; get_headers.
get_meta_tagsПрочитать именованные meta-теги из HTML-файла, например описание страницы своего каталога.
PHP 8.2+ · standard
get_meta_tags(string $filename, bool $use_include_path = false): array|falseПрочитать именованные meta-теги из HTML-файла, например описание страницы своего каталога.
Имя функции со скобками означает вызов: данные внутри скобок — аргументы, а результат можно сохранить через = в переменную с $. Строки записываются в кавычках; echo выводит текст, "\n" — перевод строки. Точка . склеивает строки. Массив ["q" => "чай"] хранит значение «чай» под ключом q. var_dump дополнительно показывает тип.
В проверках === false означает именно булев отказ, а не пустую строку или ноль. if выбирает действие по условию; throw new RuntimeException прекращает обычный путь при неожиданном отказе. foreach берёт элементы массива по очереди. Переменные, массивы и вызов и возврат функции можно повторить отдельно.
| Часть вызова | Значение |
|---|---|
| filename | Путь к локальному HTML либо поддерживаемый URL. Чтение идёт построчно до закрывающего . URL открывается через stream wrapper и может создавать сетевой запрос. |
| use_include_path | true включает поиск локального файла по include_path. К URL эта настройка не относится. |
| Возврат | Массив: name становится ключом, content — значением. Имена приводятся к нижнему регистру, некоторые специальные символы заменяются _. При повторении имени сохраняется последний тег. false при невозможности открыть/прочитать источник. |
| Что не извлекается | Нужен атрибут name. Open Graph с property, заголовок title и динамические DOM-изменения JavaScript не являются этой задачей. Это не полноценный современный HTML DOM-парсер. |
| Безопасность | Значения остаются недоверенным текстом. Для показа в HTML требуется экранирование. Нельзя принимать произвольный URL/путь от посетителя: это чтение сервером и возможный SSRF. |
- name выбирает ключ description
- content даёт значение Tea catalog
- После разбор прекращается
<?php
declare(strict_types=1);
$tags = get_meta_tags(__DIR__ . '/page.html');
if ($tags === false) { throw new RuntimeException('Cannot read HTML'); }
echo $tags['description'] ?? 'No description', "\n";
Ожидаемый результат
Tea catalog
Оператор ?? выбирает запасной текст, если ключ отсутствует. Существование файла ещё не означает наличие meta description.
<?php
declare(strict_types=1);
$tags = get_meta_tags(__DIR__ . '/page.html');
echo $tags['description'], "\n", $tags['geo_position'], "\n";
var_dump(isset($tags['og:title']), isset($tags['late']));
Ожидаемый результат
Second
10;20
bool(false)
bool(false)
DESCRIPTION преобразован в description, второй тег перезаписал первый. geo.position стал geo_position. property не учитывается, как и тег после head.
<?php
declare(strict_types=1);
$old = get_include_path();
try {
set_include_path(__DIR__ . '/templates');
$tags = get_meta_tags('page.html', true);
if ($tags === false) { throw new RuntimeException('Cannot read template'); }
echo $tags['author'], "\n";
} finally { set_include_path($old); }
Ожидаемый результат
Editorial team
Поиск разрешённого шаблона и восстановление настройки показаны явно. Не используйте include_path как механизм открытия произвольного файла по имени пользователя.
<?php
declare(strict_types=1);
$warned = false;
set_error_handler(static function (int $level) use (&$warned): bool {
if ($level !== E_WARNING) { return false; }
$warned = true; return true;
});
try { $tags = get_meta_tags(__DIR__ . '/missing.html'); }
finally { restore_error_handler(); }
var_dump($tags === false, $warned);
Ожидаемый результат
bool(true)
bool(true)
Обработчик отмечает ожидаемое предупреждение только в этом опыте. Пустой успешный массив и false — разные результаты. В приложении диагностику сохраняют без раскрытия внутренних путей посетителю.
У get_meta_tags нет параметра context. Изменение default stream context влияет на другие открытия в том же процессе; не прячьте такие глобальные эффекты в библиотечную функцию. Для контролируемого скачивания используйте HTTP-клиент с явными лимитами, затем полноценный HTML-парсер для нужных тегов. Даже построчное чтение может встретить огромную строку; сама функция не является лимитом размера.
Связанные инструменты: get_headers; parse_url; urlencode.
get_headersПолучить HTTP-заголовки ответа собственного известного endpoint и отдельно оценить статус.
PHP 8.2+ · standard
get_headers(string $url, bool $associative = false, ?resource $context = null): array|falseПолучить HTTP-заголовки ответа собственного известного endpoint и отдельно оценить статус.
Имя функции со скобками означает вызов: данные внутри скобок — аргументы, а результат можно сохранить через = в переменную с $. Строки записываются в кавычках; echo выводит текст, "\n" — перевод строки. Точка . склеивает строки. Массив ["q" => "чай"] хранит значение «чай» под ключом q. var_dump дополнительно показывает тип.
В проверках === false означает именно булев отказ, а не пустую строку или ноль. if выбирает действие по условию; throw new RuntimeException прекращает обычный путь при неожиданном отказе. foreach берёт элементы массива по очереди. Переменные, массивы и вызов и возврат функции можно повторить отдельно.
| Часть вызова | Значение |
|---|---|
| url | HTTP-адрес назначения. Это реальный сетевой запрос сервера, а не разбор текста URL. Требуются доступный wrapper, настройки и права сети. |
| associative | false — индексированный список строк, включая status lines. true — разобранные имена ключей; повторяющийся заголовок может стать массивом значений. С PHP 8.0 тип bool. |
| context | Контекст от stream_context_create либо null для default context. Добавлен в PHP 7.1. В нём задают метод, timeout, redirect policy и другие параметры HTTP-wrapper. |
| Метод | По умолчанию GET, а не HEAD. HEAD явно задают через context; некоторые серверы обрабатывают HEAD иначе. |
| Возврат | Массив заголовков либо false при отказе. Массив сам по себе не означает 2xx; при редиректах может содержать несколько ответов. |
| Граница | Заголовки ответа отличаются от входящих заголовков запроса посетителя. Последние берут другими API/серверными переменными. |
- PHP отправляет HEAD своему endpoint
- Сервер возвращает status line и заголовки
- get_headers получает список либо false
- Приложение проверяет статус и только нужные поля
Контекст — массив настроек транспорта, упакованный stream_context_create. Значение follow_location=0 запрещает автоматический переход в этих примерах. Переменная окружения PHP_MANUAL_HTTP_BASE указывает только на собственный учебный сервер на loopback. Эти примеры требуют запущенного HTTP fixture: отдельно от синтаксической проверки они проверены на настоящем локальном HTTP-ответе.
<?php
declare(strict_types=1);
$base = getenv('PHP_MANUAL_HTTP_BASE');
if ($base === false || $base === '') { throw new RuntimeException('Start the local lesson HTTP fixture'); }
$context = stream_context_create(['http' => [
'method' => 'HEAD', 'timeout' => 2, 'follow_location' => 0, 'ignore_errors' => true
]]);
$headers = get_headers($base . '/ok', false, $context);
if ($headers === false) { throw new RuntimeException('HTTP request failed'); }
echo $headers[0], "\n";
Ожидаемый результат
HTTP/1.1 200 OK
Статус фиксирован в нашем fixture. Внешний сервер может ответить другой версией HTTP или статусом. При выключенном follow_location первая status line относится именно к этому ответу.
<?php
declare(strict_types=1);
$base = getenv('PHP_MANUAL_HTTP_BASE');
if ($base === false || $base === '') { throw new RuntimeException('Start the local lesson HTTP fixture'); }
$context = stream_context_create(['http' => [
'method' => 'HEAD', 'timeout' => 2, 'follow_location' => 0
]]);
$headers = get_headers($base . '/repeat', true, $context);
if ($headers === false) { throw new RuntimeException('HTTP request failed'); }
$values = [];
foreach ($headers as $name => $value) {
if (is_string($name) && strcasecmp($name, 'X-Lesson') === 0) {
foreach ((array) $value as $item) { $values[] = $item; }
}
}
echo implode(' | ', $values), "\n";
Ожидаемый результат
first | second
Имена HTTP-заголовков нечувствительны к регистру. Фиксированное обращение к headers["Content-Type"] не является универсальным регистронезависимым поиском. Для Set-Cookie повторения особенно существенны; их нельзя слепо склеивать в одно значение.
<?php
declare(strict_types=1);
$base = getenv('PHP_MANUAL_HTTP_BASE');
if ($base === false || $base === '') { throw new RuntimeException('Start the local lesson HTTP fixture'); }
$context = stream_context_create(['http' => [
'method' => 'HEAD', 'timeout' => 2, 'follow_location' => 0, 'ignore_errors' => true
]]);
$headers = get_headers($base . '/redirect', false, $context);
if ($headers === false) { throw new RuntimeException('HTTP request failed'); }
echo $headers[0], "\n";
echo in_array('Location: /ok', $headers, true) ? "Redirect observed\n" : "No location\n";
Ожидаемый результат
HTTP/1.1 302 Found
Redirect observed
Location в тесте имеет известный регистр. В общем случае ищут имена без учёта регистра, затем разрешают относительный адрес по стандарту и заново проверяют допустимость назначения. Нельзя считать первый 301/302 окончательным статусом после всей цепочки.
<?php
declare(strict_types=1);
$base = getenv('PHP_MANUAL_HTTP_BASE');
if ($base === false || $base === '') { throw new RuntimeException('Start the local lesson HTTP fixture'); }
$context = stream_context_create(['http' => [
'method' => 'HEAD', 'timeout' => 2, 'follow_location' => 0, 'ignore_errors' => true
]]);
$headers = get_headers($base . '/missing', false, $context);
if ($headers === false) { throw new RuntimeException('Transport failure'); }
echo $headers[0], "\n";
Ожидаемый результат
HTTP/1.1 404 Not Found
Транспорт вернул ответ, но нужного ресурса нет. Не называйте любой непустой массив подтверждением доступности/исправности сайта.
timeout ограничивает операции чтения согласно wrapper, а не обещает полный deadline всей цепочки DNS/соединений/редиректов. Для контролируемого мониторинга/скачивания большого числа адресов обычно нужен HTTP-клиент с явной политикой ресурсов. Не выполняйте get_headers перед каждым последующим GET без необходимости: это дополнительный запрос, а ресурс между ними может измениться. TLS-проверку не отключают ради успешного ответа.
Связанные инструменты: parse_url; get_meta_tags; http_build_query.
Источники и обсуждения
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


