Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

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

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11149 глав4458 карточек справочника
Дерево руководства

Глава 22 · С нуля → практика

Регулярные выражения: точная грамматика и пределы

Проверяем артикула и извлекаем значения, отличая несовпадение от ошибки самого выражения.

Проверить формат артикула по маленькому правилу

Склад принимает артикул вида AB-1234: две заглавные латинские буквы, дефис, четыре цифры. Регулярное выражение описывает эту форму. Если нужно просто сравнить один готовый код, хватит ===; шаблон нужен, когда допустим целый набор строк по одному правилу.

Часть шаблонаСмысл
/ ... /Границы записи шаблона для preg_match; не часть артикула.
\A и \zНачало и конец всей строки: посторонний текст по краям не принимается.
[A-Z]{2}Две буквы из диапазона A–Z.
-Буквальный дефис.
[0-9]{4}Ровно четыре ASCII-цифры.
Один артикул проходит проверку формыСамостоятельный пример
<?php
$sku = 'AB-1234';
$result = preg_match('/\A[A-Z]{2}-[0-9]{4}\z/', $sku);
if ($result === 1) {
    echo "Формат подходит\n";
} elseif ($result === 0) {
    echo "Формат не подходит\n";
} else {
    echo "Ошибка самого шаблона\n";
}
Ожидаемый результат
Формат подходит

preg_match возвращает 1 при совпадении, 0 при отсутствии, false при ошибке. Значения проверены строго и раздельно. Совпадение с шаблоном не доказывает, что такой артикул существует в магазине.

Попробуйте сами. Проверьте ab-1234, AB-123 и XAB-1234.

Разбор решения

Все три не подходят: неверный регистр, нехватка цифры и лишний символ перед началом. Не убирайте якоря, чтобы «починить» ввод.

Чтобы повторять проверку, вынесите шаблон и выбор результата в функцию с аргументом sku. Ниже — извлечение групп, Unicode, диагностика ошибок и пределы длины/сложности. Начните с фиксированного собственного шаблона; произвольный пользовательский regex — отдельный договор и расходы.

Когда шаблон действительно нужен

Наглядная модель

Шаблон как трафарет для артикула

Магазин принимает артикулы вида AB-1234. Нужно проверить всю строку, а не случайно найти подходящий кусок внутри мусора.

  1. Строка от пользователя

    Например AB-1234 или prefixAB-1234suffix.

  2. Правило формы

    Две заглавные латинские буквы, дефис и четыре цифры; от начала до конца строки.

  3. Разрешить или отклонить

    AB-1234 подходит. Строка с посторонними краями — нет.

Регулярное выражение проверяет форму записи, а не существование товара в базе. Ошибка самого шаблона — отдельный исход, её нельзя смешивать с обычным несовпадением.

Регулярное выражение описывает форму строки: две латинские буквы, дефис, четыре числа. Это полезно для артикула, фиксированного фрагмента лога или разделения по нескольким разделителям. Оно не заменяет JSON-парсер, HTML-парсер или бизнес-правило «товар существует и принадлежит продавцу». Для одной подстроки возьмите str_contains, для фиксированного разделителя explode: цель будет видна сразу.

Проверить формат артикула полностьюСамостоятельный пример
<?php
declare(strict_types=1);

foreach (['AB-2048', 'ab-2048', 'AB-2048 extra'] as $sku) {
    $matched = preg_match('/\A[A-Z]{2}-[0-9]{4}\z/', $sku);
    if ($matched === false) {
        throw new RuntimeException(preg_last_error_msg());
    }
    echo $matched === 1 ? "Формат принят\n" : "Неверный формат\n";
}
Ожидаемый результат
Формат принят
Неверный формат
Неверный формат

\A и \z задают начало и конец всей строки. Это исключает частичное совпадение. preg_match возвращает 1,0 или false: ошибка движка отличается от обычного отсутствия совпадения. Проверка формата не выдает доступ к товару.

Именованные группы делают извлечение понятным

Прочитать учебную строку журналаСамостоятельный пример
<?php
declare(strict_types=1);

$line = 'order=O-17 status=paid';
$result = preg_match('/\Aorder=(?<id>O-[0-9]+) status=(?<status>[a-z]+)\z/', $line, $matches);
if ($result !== 1) {
    throw new UnexpectedValueException('Неизвестный формат журнала');
}
echo $matches['id'], ':', $matches['status'], "\n";
Ожидаемый результат
O-17:paid

Имена id и status переживут добавление некаптурирующей группы лучше, чем магические индексы 1 и 2. Здесь журнал имеет фиксированный формат; для структурных JSON-логов используйте json_decode, чтобы обрабатывать экранирование правильно.

Unicode, пользовательский текст и замена

Модификатор u включает работу с UTF-8; невалидная последовательность может дать ошибку. Для букв разных алфавитов применяют Unicode-классы, но набор допустимых знаков выбирается под задачу. Имя человека не ограничивайте искусственным [A-Za-z]+: в реальности есть апострофы, дефисы и другие алфавиты. Не вклеивайте сырой пользовательский ввод в шаблон: для буквального поиска используйте preg_quote с тем же разделителем либо обычную строковую функцию.

Нормализовать пробелы в ограниченном текстеСамостоятельный пример
<?php
declare(strict_types=1);

$title = "  Красный\t чай\n";
$normalized = preg_replace('/\s+/u', ' ', trim($title));
if ($normalized === null) {
    throw new RuntimeException(preg_last_error_msg());
}
echo $normalized, "\n";
Ожидаемый результат
Красный чай

preg_replace возвращает null при ошибке. Нормализация уместна для однострочного заголовка, но уничтожит значимые переносы в письме, коде или адресе. Сначала назовите тип поля, затем выбирайте преобразование.

Опасные шаблоны и стоимость

Вложенные неоднозначные повторения могут породить огромный перебор на почти совпадающем вводе. Ограничивайте размер строки, предпочитайте ясные диапазоны и длину, тестируйте длинный неподходящий хвост. Повышение pcre.backtrack_limit само по себе не метод лечения. Для пароля, например, часто яснее несколько проверок длины и политики, чем один гигантский шаблон. Отдельно собирайте ошибки движка, чтобы сломанный шаблон не выглядел как обычная ошибка пользователя.

Попробуйте сами. Артикул должен состоять из 1..20 латинских букв, цифр, дефиса. Какой шаблон и какие тесты?

Разбор решения

/\A[A-Za-z0-9-]{1,20}\z/. Проверьте пустую строку, длину 20 и 21, перевод строки в конце, кириллицу, пробел и массив вместо строки. До preg_match нужен is_string. Шаблон не устанавливает уникальность артикула.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

preg_matchПроверить формат кода заказа и извлечь его номер за один поиск.

PHP 4+ · pcre

preg_match(string $pattern, string $subject, array &$matches = null, int $flags = 0, int $offset = 0): int|false

В ~\AORD-([0-9]+)\z~ тильды обрамляют шаблон, \A/\z требуют всю строку, ORD- — буквальный префикс, [0-9]+ — хотя бы одна цифра, (...) сохраняет номер. $matches — обычная переменная; функция заполнит её сама.

Аргумент / результатДоговор
patternСтрока с разделителями: ~\AORD-[0-9]+\z~.
subjectСтрока, в которой ищем. Поиск без якорей допускает совпадение внутри текста.
&matches = nullЗаписываемый массив: [0] всё совпадение, [1] первая захватывающая группа; именованные группы имеют и имя, и номер.
flags = 0PREG_OFFSET_CAPTURE меняет каждый элемент в [текст, byteOffset]; PREG_UNMATCHED_AS_NULL сохраняет незаполненные группы как null, вместе с offset как [null,-1]. Объединение через |.
offset = 0Начальная позиция в байтах; отрицательная — от конца. Это не substr: начало исходной строки и предыдущий контекст остаются видны. /u требует границы UTF-8 символа.
Результат1 — найдено, 0 — не найдено, false — отказ. Сравнивайте === false, затем === 1.
ОшибкиНекомпилируемый шаблон выдаёт E_WARNING; ошибка выполнения видна в preg_last_error сразу после вызова. Неверные типы/значения аргументов могут вызвать TypeError/ValueError. Не смешивайте технический отказ с отсутствием совпадения.
ВерсияPHP 4+; встроенное расширение pcre
Данные первого примера
  1. ORD-104 → проверка префикса и цифр.
  2. Группа ([0-9]+) сохраняет 104.
  3. $status = 1; $matches[1] = "104" → номер заказа.
Извлечь номер заказаСамостоятельный пример
<?php
declare(strict_types=1);

$status = preg_match('~\AORD-([0-9]+)\z~', 'ORD-104', $matches);
if ($status === 1) { echo 'Номер: ', $matches[1], "\n"; }
Ожидаемый результат
Номер: 104

if выполняет блок только при найденном совпадении. Индекс 1 содержит текст цифр, не целое число; ведущие нули не теряются.

Отделить неправильный код от неисправного правилаСамостоятельный пример
<?php
declare(strict_types=1);

function orderNumber(string $code): ?string
{
    $result = preg_match('~\AORD-(?<number>[0-9]+)\z~', $code, $m);
    if ($result === false) { throw new RuntimeException(preg_last_error_msg()); }
    return $result === 1 ? $m['number'] : null;
}
echo orderNumber('ORD-007'), "\n";
echo orderNumber('ORD-7x') ?? 'Исправьте код', "\n";
Ожидаемый результат
007
Исправьте код

function объявляет повторно используемое действие; ?string означает строку или null. Оператор ?? подставляет сообщение только для null. Ошибка движка не становится сообщением «плохой код».

Байтовая позиция и незаполненная группаСамостоятельный пример
<?php
declare(strict_types=1);

preg_match('~(?<code>BR[0-9]+)(?:-(?<size>[0-9]+))?~u', 'Я BR41', $m,
    PREG_OFFSET_CAPTURE | PREG_UNMATCHED_AS_NULL);
echo $m['code'][0], '@', $m['code'][1], "\n";
echo $m['size'][0] === null ? 'size:null' : 'size:set';
echo '@', $m['size'][1], "\n";
Ожидаемый результат
BR41@3
size:null@-1

Я занимает 2 байта, затем пробел: BR41 начинается с байта 3. (?:...) группирует без отдельного результата; ? делает суффикс необязательным. Эти формы подробно разобраны в синтаксисе главы.

Не читайте $matches до проверки успеха. PREG_UNMATCHED_AS_NULL появился в 7.2; с 7.4 включает и незаполненные конечные группы. Для поиска буквального фрагмента удобнее str_contains/strpos. Отрицательный offset слишком далеко до начала в PHP прижимается к 0; offset после конца даёт false и internal error. Не делайте из ошибочного offset отсутствие совпадения.

Попробуйте. Замените + на {3}: ORD-007 проходит, ORD-7 нет. {3} требует ровно три цифры.

Дальше: preg_match_all, preg_quote, preg_last_error. Как устроен шаблон и вызов; ошибка или нет совпадения.

Следующая практика. Прежние упражнения продолжают введение.

Возвращает 1 при совпадении,0 при отсутствии и false при ошибке. Matches получает найденные группы; шаблон принадлежит программе.

Количество в строгом формате формыСамостоятельный пример
<?php
declare(strict_types=1);

$raw = '25';
$ok = preg_match('/\A[1-9][0-9]{0,2}\z/', $raw);
echo $ok === 1 ? "1..999\n" : "Неверно\n";
Ожидаемый результат
1..999

Тип raw должен быть проверен до этого фрагмента. Ограниченная длина не позволит переполнить int при последующем приведении.

Достать ID из внутренней ссылкиСамостоятельный пример
<?php
declare(strict_types=1);

$result = preg_match('/\Aorder:(?<id>[1-9][0-9]*)\z/', 'order:2048', $matches);
if ($result !== 1) {
    throw new UnexpectedValueException('Не ссылка заказа');
}
echo $matches['id'], "\n";
Ожидаемый результат
2048

Группа возвращается строкой. Перед int добавьте допустимый предел; проверка формата не заменяет права доступа к заказу.

Сравните. Для подстроки есть str_contains; preg_match_all находит все совпадения; парсеры JSON/CSV лучше регулярки для своих форматов.

preg_replaceПривести повторяющиеся пробелы к одному или перестроить текст по захваченным полям.

PHP 4+ · pcre

preg_replace(string|array $pattern, string|array $replacement, string|array $subject, int $limit = -1, int &$count = null): string|array|null

Три обязательных аргумента читаются как «что искать, чем заменить, где искать». В первом примере [ \t]+ собирает подряд пробелы и табуляции, замена " " оставляет один пробел. $1 в одинарных кавычках остаётся текстом до обработки preg_replace.

Аргумент / результатДоговор
patternОдна строка-шаблон с разделителями либо массив шаблонов.
replacementСтрока либо массив замен, по порядку элементов pattern, не по совпадающим ключам. Если replacement — массив, pattern тоже должен быть массивом; иначе TypeError. Одна строка обслуживает все шаблоны; недостающие элементы массива означают пустую замену. $0 — всё совпадение, $1…$99 / \1…\99 — числовые группы; ${1}2 отделяет группу 1 от цифры 2.
subjectСтрока либо массив строк. Ключи массива сохраняются; правила применяются к каждому значению.
limit = -1Максимум замен на каждый шаблон в каждой строке. -1 — все, 0 — ни одной.
&count = nullНеобязательная переменная: общее число сделанных замен. & в сигнатуре означает запись результата в переданную переменную.
РезультатНовая строка/массив, прежние значения без совпадений сохраняются. null при технической ошибке.
ОшибкиНекомпилируемый шаблон выдаёт E_WARNING; ошибка выполнения видна в preg_last_error сразу после вызова. Неверные типы/значения аргументов могут вызвать TypeError/ValueError. Не смешивайте технический отказ с отсутствием совпадения.
ВерсияPHP 4+; встроенное расширение pcre
Данные первого примера
  1. "Bolt M10" → найдено три пробела.
  2. Одна замена на " ".
  3. Результат "Bolt M10", count = 1.
Нормализовать горизонтальный промежутокСамостоятельный пример
<?php
declare(strict_types=1);

$clean = preg_replace('~[ \t]+~', ' ', 'Bolt   M10', -1, $count);
echo $clean, "\n", 'Замен: ', $count, "\n";
Ожидаемый результат
Bolt M10
Замен: 1

Количество замен — число совпадений, не число удалённых байтов. Эта версия сохраняет переводы строк.

Перестроить метку без вычисленийСамостоятельный пример
<?php
declare(strict_types=1);

$label = preg_replace('~\A([A-Z]{2})([0-9]+)\z~', '$1 / ${2}0', 'BR41');
echo $label, "\n";
Ожидаемый результат
BR / 410

Группа 2 содержит 41; ${2}0 добавляет буквальный 0. Это форматирование, не умножение числа. $name по имени в replacement не поддерживается: для имён применяйте callback.

Порядок правил и ограничение на каждую строкуСамостоятельный пример
<?php
declare(strict_types=1);

$result = preg_replace(['~old~', '~new~'], ['new', 'ready'],
    ['a' => 'old old', 'b' => 'old'], 1, $count);
foreach ($result as $key => $value) { echo $key, ':', $value, "\n"; }
echo 'Замен: ', $count, "\n";
Ожидаемый результат
a:ready old
b:ready
Замен: 4

Сначала old становится new, затем new становится ready. limit=1 действует отдельно на каждую пару шаблон/строка, count суммирует четыре операции.

Правила массива последовательны: следующее видит результат предыдущего. Ключи pattern/replacement не сопоставляются: важен порядок обхода; согласуйте порядок явно. Для одновременного словаря буквальных замен используйте strtr. preg_quote предназначен для pattern, не для replacement. Старый /e удалён в PHP7: вместо исполнения текста используйте явную callback-функцию. Перед чтением результата отличите null от допустимой пустой строки.

Попробуйте. Поставьте limit=0 в первом примере: исходные три пробела сохранятся, count станет 0.

Дальше: preg_replace_callback, preg_filter, preg_quote. Как устроен шаблон и вызов; ошибка или нет совпадения.

Следующая практика. Прежние упражнения продолжают введение.

Возвращает результат замен, либо null при ошибке. Limit ограничивает число замен для каждого шаблона; count получает выполненное число.

Сжать пробелы однострочного заголовкаСамостоятельный пример
<?php
declare(strict_types=1);

$result = preg_replace('/\s+/u', ' ', "Чай\t зеленый");
if ($result === null) {
    throw new RuntimeException(preg_last_error_msg());
}
echo $result, "\n";
Ожидаемый результат
Чай зеленый

Нормализация подходит для однострочного поля. Не применяйте к отзыву с абзацами без решения, что переносы незначимы.

Скрыть цифровой код в учебном журналеСамостоятельный пример
<?php
declare(strict_types=1);

$line = 'pin=123456 status=sent';
$safe = preg_replace('/\bpin=[0-9]{6}\b/', 'pin=[hidden]', $line, 1, $count);
if ($safe === null) {
    throw new RuntimeException('Ошибка шаблона');
}
echo $safe, ':', $count, "\n";
Ожидаемый результат
pin=[hidden] status=sent:1

Лучше изначально не писать секрет в журнал. Это ограниченный пример известного формата, а не универсальный редактор персональных данных.

Сравните. str_replace проще для точной строки; callback позволяет вычислять замену по именованным группам.

preg_splitРазделить список тегов, где между словами встречаются запятые, точки с запятой и пробелы.

PHP 4+ · pcre

preg_split(string $pattern, string $subject, int $limit = -1, int $flags = 0): array|false

[,;\s]+ — один или более символов из набора: запятая, ; или пробельный. NO_EMPTY убирает пустую часть перед первым и после последнего разделителя. implode здесь только собирает результат для печати.

Аргумент / результатДоговор
patternШаблон разделителя, не шаблон нужного слова.
subjectОдна строка.
limit = -1Максимум обычных частей; остаток попадает в последнюю. -1 и 0 — без ограничения, 1 — вся строка. Захваченные разделители не считаются обычными частями.
flags = 0PREG_SPLIT_NO_EMPTY убирает пустые части; PREG_SPLIT_DELIM_CAPTURE возвращает захватывающие группы разделителя; PREG_SPLIT_OFFSET_CAPTURE делает элементы парами [текст, byteOffset]. Флаги объединяются |.
РезультатМассив частей; false при ошибке. Без найденного разделителя обычно [subject], а не отказ.
ОшибкиНекомпилируемый шаблон выдаёт E_WARNING; ошибка выполнения видна в preg_last_error сразу после вызова. Неверные типы/значения аргументов могут вызвать TypeError/ValueError. Не смешивайте технический отказ с отсутствием совпадения.
ВерсияPHP 4+; встроенное расширение pcre
Данные первого примера
  1. " гайки, болты; шайбы " → группы разделителей.
  2. Пробелы и пунктуация удаляются как разделители.
  3. Остаются три тега в исходном порядке.
Три вида разделителя в одном полеСамостоятельный пример
<?php
declare(strict_types=1);

$tags = preg_split('~[,;\s]+~u', ' гайки, болты; шайбы ', -1, PREG_SPLIT_NO_EMPTY);
echo implode(' / ', $tags), "\n";
Ожидаемый результат
гайки / болты / шайбы

/u — UTF-8 и Unicode-пробелы; запятая внутри самого тега этим форматом не допускается. Для CSV с кавычками нужен CSV-парсер.

Разобрать ключ, сохранив остальное значениеСамостоятельный пример
<?php
declare(strict_types=1);

$parts = preg_split('~\s*=\s*~', 'title = Bolt = M10', 2);
echo $parts[0], "\n", $parts[1], "\n";
Ожидаемый результат
title
Bolt = M10

limit 2 запрещает дальнейшее деление после первого разделителя; второе = остаётся частью значения.

Сохранить знаки и исходные позицииСамостоятельный пример
<?php
declare(strict_types=1);

$parts = preg_split('~([+\-])~', '12+5-2', -1,
    PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE);
foreach ($parts as [$text, $offset]) { echo $text, '@', $offset, "\n"; }
Ожидаемый результат
12@0
+@2
5@3
-@4
2@5

Скобки захватывают знак, поэтому он не теряется. Это только токены калькулятора, ещё не вычисление и не разрешение выполнять ввод как PHP.

Пустой шаблон может разбить текст на байты, с /u — на кодовые точки; это не всегда видимые графемы. Для графем есть \X, для одного буквального разделителя — explode. NO_EMPTY удаляет пустые строки, но не строку "0". Вложенные захватывающие группы разделителя добавляют дополнительные элементы — чаще нужна одна группа или (?:...).

Попробуйте. Замените строку на "0;;1": NO_EMPTY оставит оба значения 0 и 1.

Дальше: preg_match_all, preg_quote. Как устроен шаблон и вызов; ошибка или нет совпадения.

Следующая практика. Прежние упражнения продолжают введение.

Возвращает части строки либо false при ошибке. PREG_SPLIT_NO_EMPTY пропускает пустые части; другие флаги позволяют сохранить разделители и байтовые смещения.

Теги через запятую или точку с запятойСамостоятельный пример
<?php
declare(strict_types=1);

$parts = preg_split('/\s*[,;]\s*/u', 'php, api; shop', -1, PREG_SPLIT_NO_EMPTY);
if ($parts === false) {
    throw new RuntimeException('Не разделить строку');
}
echo implode('|', $parts), "\n";
Ожидаемый результат
php|api|shop

Формат явно разрешает два разделителя. Если сам тег может содержать запятую, нужен структурный формат или экранирование с настоящим парсером.

Слова простой поисковой строкиСамостоятельный пример
<?php
declare(strict_types=1);

$words = preg_split('/\s+/u', trim('  чай   зеленый '), -1, PREG_SPLIT_NO_EMPTY);
if ($words === false) {
    throw new RuntimeException('Неверный текст');
}
echo count($words), ':', implode(',', $words), "\n";
Ожидаемый результат
2:чай,зеленый

Это разбиение по пробелам, а не языковая морфология. Для кавычек, операторов поиска и синонимов потребуется отдельный разбор.

Сравните. explode для одного фиксированного разделителя понятнее и не требует движка шаблонов.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.