Разработка веб-приложений с использованием программных платформ

Тема 2. Основы PHP

Разработка веб-приложений с использованием программных платформ

План лекции

Основные вопросы

  • Выполнение PHP и структура файла
  • Значения, переменные и типы
  • Условия, функции и массивы
  • Формы и недоверенные данные
  • Cookies, сессии и CSRF
  • PDO и хранение паролей

Цели лекции

  • Запустить PHP-приложение локально
  • Прочитать и изменить простой скрипт
  • Безопасно обработать HTML-форму
  • Сохранить состояние между запросами
  • Выполнить параметризованный SQL-запрос
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Место PHP в цикле запроса

GET /hello.php?name=Анна
          |
          v
     веб-сервер
          |
          v
    PHP runtime -> hello.php
          |
          v
HTTP/1.1 200 OK + HTML
  • PHP-код выполняется на сервере
  • Скрипт получает данные текущего HTTP-запроса
  • Результат выполнения становится телом ответа
  • Браузер не получает исходный PHP-код
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Минимальная среда разработки

php -v
php -m
mkdir php-basics
cd php-basics
php -S localhost:8000

Для работы

  • Поддерживаемая версия PHP
  • Редактор с подсветкой синтаксиса
  • Терминал и браузер с DevTools
  • Расширение mbstring для строк UTF-8

Важно

  • Встроенный сервер предназначен для разработки
  • Версию сверяют с php.net/supported-versions.php
  • Ошибки разработки нельзя показывать пользователям
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Структура PHP-файла

<?php
declare(strict_types=1);

$title = 'Основы PHP';
?>
<!doctype html>
<html lang="ru">
<body>
    <h1><?= htmlspecialchars(
        $title,
        ENT_QUOTES | ENT_SUBSTITUTE,
        'UTF-8'
    ) ?></h1>
</body>
</html>

В файле только с PHP-кодом закрывающий тег ?> обычно опускают.

Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Переменные и значения

$name = 'Анна';
$attempts = 3;
$price = 19.90;
$active = true;
$middleName = null;

$attempts = $attempts + 1;
var_dump($name, $attempts, $middleName);

Переменная

  • Начинается со знака $
  • Имя чувствительно к регистру
  • Получает тип присвоенного значения

Следствие

  • Локальную переменную нельзя объявить как $name: string
  • Типы указывают у параметров, результатов и свойств
  • var_dump() показывает значение и его тип
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Основные типы значений

Тип Пример Назначение
bool true логическое условие
int, float 42, 19.9 числа
string 'PHP' текст и байтовые строки
null null отсутствие значения
array ['PHP', 'SQL'] упорядоченная карта
object new DateTimeImmutable() экземпляр класса
resource результат fopen() внешний ресурс
echo get_debug_type($value);
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Строки и UTF-8

$name = 'Анна';
$message = "Привет, $name";
$path = 'users/' . $name;

$email = trim("  user@example.com \n");
$bytes = strlen('Привет');
$characters = mb_strlen('Привет', 'UTF-8');

Операции

  • . соединяет строки
  • Двойные кавычки подставляют переменные
  • trim() удаляет пробелы по краям

Кодировка

  • strlen() считает байты
  • mb_strlen() считает символы в заданной кодировке
  • Для HTML обычно используют UTF-8
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Сравнение и логические условия

$input = '10';

var_dump($input == 10);  // true: с преобразованием
var_dump($input === 10); // false: типы различаются

$role = $_SESSION['role'] ?? 'guest';
$allowed = $active && $role === 'admin';
  • === сравнивает значение и тип
  • !== проверяет строгое неравенство
  • &&, ||, ! объединяют условия
  • ?? задает запасное значение для отсутствующего ключа или null
  • empty('0') возвращает true, поэтому для проверки формы нужны явные правила
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Декларации типов и strict_types

<?php
declare(strict_types=1);

function total(float $price, int $quantity): float
{
    return $price * $quantity;
}

$amount = total(19.90, 3);

Что проверяется

  • Типы параметров
  • Возвращаемый тип
  • Допустимость null, если указан ?Type

Чего режим не делает

  • Не фиксирует тип обычной переменной
  • Не валидирует данные формы
  • Не заменяет проверки предметных правил
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Функции отделяют операцию от сценария

function formatPrice(
    float $amount,
    string $currency = 'BYN'
): string {
    if ($amount < 0) {
        throw new InvalidArgumentException(
            'Цена не может быть отрицательной'
        );
    }

    return number_format($amount, 2, '.', ' ')
        . ' ' . $currency;
}

echo formatPrice(19.9);
  • Имя функции описывает действие
  • Параметры являются входом
  • return возвращает результат
  • Исключение сообщает о невозможной операции
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Массив PHP является упорядоченной картой

$colors = ['красный', 'зеленый', 'синий'];

$student = [
    'name' => 'Иван',
    'group' => 'ИС-31',
    'active' => true,
];

echo $colors[0];
echo $student['name'];

Индексированные данные

  • Ключи обычно 0, 1, 2
  • Подходят для последовательности значений

Ассоциативные данные

  • Строковые ключи называют поля
  • По форме похожи на строку результата БД
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Перебор и отбор элементов

$products = [
    ['name' => 'Книга', 'price' => 25.0],
    ['name' => 'Клавиатура', 'price' => 140.0],
];

foreach ($products as $product) {
    if ($product['price'] <= 100) {
        echo $product['name'] . "\n";
    }
}
  • foreach получает очередное значение массива
  • Форма foreach ($items as $key => $value) дает ключ и значение
  • array_key_exists() проверяет наличие ключа, включая значение null
  • isset() возвращает false и для отсутствующего ключа, и для null
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Ветвление: if и match

if ($score >= 70) {
    $result = 'зачтено';
} else {
    $result = 'не зачтено';
}

$label = match ($status) {
    'draft' => 'Черновик',
    'published' => 'Опубликовано',
    'archived' => 'Архив',
    default => 'Неизвестный статус',
};
Конструкция Удобна для
if / elseif / else диапазонов и сложных условий
match строгого сопоставления одного значения
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Ошибки во время разработки

php -d display_errors=1 \
    -d error_reporting=E_ALL \
    -S localhost:8000
try {
    echo formatPrice(-10);
} catch (InvalidArgumentException $error) {
    error_log($error->getMessage());
    http_response_code(400);
    echo 'Некорректные данные';
}
  • В разработке ошибка должна быть заметна
  • В рабочей среде подробности пишут в журнал
  • Пользователю возвращают безопасное сообщение
  • Пароли, токены и строки подключения не записывают в журнал
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Данные текущего запроса

Источник Суперглобальный массив Пример
Query string $_GET ?page=2
HTML-форма POST $_POST email=user@example.com
Заголовки и сервер $_SERVER REQUEST_METHOD
Загруженные файлы $_FILES поле avatar
Cookies $_COOKIE настройка темы
Сессия $_SESSION идентификатор пользователя

Любое входное значение недоверенное и может иметь неожиданную форму.

Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

HTML-форма задает имена полей

<form method="post" action="/register.php">
    <label>Имя
        <input name="name" required>
    </label>
    <label>Email
        <input name="email" type="email" required>
    </label>
    <button type="submit">Зарегистрироваться</button>
</form>
  • method определяет способ отправки
  • action задает адрес обработчика
  • Только элементы с name попадают в данные формы
  • Проверки браузера удобны, но сервер обязан повторить валидацию
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Конвейер обработки формы

HTTP-запрос
  -> проверить метод и форму данных
  -> нормализовать и валидировать
  -> выполнить операцию и сохранить
  -> экранировать при выводе
  -> HTTP-ответ
Этап Пример
Нормализация trim($email)
Валидация filter_var(..., FILTER_VALIDATE_EMAIL)
Хранение исходные нормализованные данные
Вывод в HTML htmlspecialchars(...)
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Чтение и проверка POST-данных

$errors = [];
$email = '';

if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    $emailInput = $_POST['email'] ?? null;

    if (!is_string($emailInput)) {
        $errors['email'] = 'Ожидалась строка';
    } else {
        $email = trim($emailInput);

        if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
            $errors['email'] = 'Укажите корректный email';
        }
    }
}
  • Порядок проверок: метод → тип значения → формат email
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Экранирование при выводе в HTML

function e(string $value): string
{
    return htmlspecialchars(
        $value,
        ENT_QUOTES | ENT_SUBSTITUTE,
        'UTF-8'
    );
}
<input name="email" value="<?= e($email) ?>">

<?php if (isset($errors['email'])): ?>
    <p class="error"><?= e($errors['email']) ?></p>
<?php endif; ?>

htmlspecialchars() подходит для текста HTML и значений в кавычках, но не для SQL, JavaScript или CSS.

Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Post/Redirect/Get предотвращает повтор POST

POST /register.php
   |
   | валидация и сохранение
   v
303 See Other
Location: /registered.php
   |
   v
GET /registered.php

При ошибке

  • Показать форму с сообщениями
  • Обычно вернуть 422 Unprocessable Content
  • Не терять допустимые введенные значения

При успехе

  • Сохранить данные один раз
  • Отправить перенаправление
  • Обновление страницы повторит GET, а не POST
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Cookies и сессия решают разные задачи

Свойство Cookie Серверная сессия
Где данные в браузере в серверном хранилище
Что идет в запросе имя и значение cookie обычно cookie с ID сессии
Типичный пример тема интерфейса user_id, CSRF-токен
Доверие значение проверяет сервер доступ защищает идентификатор
  • Cookie может хранить не только ID сессии
  • Серверная сессия сама по себе не гарантирует безопасность
  • Secure, HttpOnly и SameSite ограничивают использование cookie
  • SameSite снижает часть риска CSRF, но не заменяет токен
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Жизненный цикл сессии

ini_set('session.use_strict_mode', '1');

$isHttps = !empty($_SERVER['HTTPS'])
    && $_SERVER['HTTPS'] !== 'off';

session_start([
    'cookie_httponly' => true,
    'cookie_secure' => $isHttps,
    'cookie_samesite' => 'Lax',
]);

// Только после успешной проверки логина и пароля
session_regenerate_id(true);
$_SESSION['user_id'] = $userId;
  1. session_start() вызывают до вывода ответа.
  2. Сервер читает ID и загружает данные сессии.
  3. После входа ID заменяют, чтобы снизить риск фиксации сессии.
  4. Выход очищает данные, cookie и серверную сессию.
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

CSRF-токен связывает форму с сессией

$_SESSION['csrf'] ??= bin2hex(random_bytes(32));
<input type="hidden" name="csrf"
       value="<?= e($_SESSION['csrf']) ?>">
$token = $_POST['csrf'] ?? null;

if (!is_string($token)
    || !hash_equals($_SESSION['csrf'], $token)) {
    http_response_code(403);
    exit('Запрос отклонен');
}

CSRF-защита нужна для запросов, которые изменяют состояние.

Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Подключение к MySQL через PDO

$dsn = 'mysql:host=localhost;dbname=app;charset=utf8mb4';

$pdo = new PDO($dsn, $dbUser, $dbPassword, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,
]);

Конфигурация

  • Учетные данные находятся вне репозитория
  • Приложение использует отдельного пользователя БД
  • Права ограничены нужными операциями

Ошибки

  • PDOException записывается в журнал
  • Пользователь не видит DSN, SQL и пароль
  • Неудачное подключение завершает операцию
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Подготовленный запрос отделяет SQL от значений

$statement = $pdo->prepare(
    'INSERT INTO users (email, password_hash)
     VALUES (:email, :password_hash)'
);

$statement->execute([
    'email' => $email,
    'password_hash' => $passwordHash,
]);
  • Параметры передают значения без конкатенации SQL
  • Placeholder нельзя использовать вместо имени таблицы или столбца
  • Уникальный индекс на email защищает правило и при гонке запросов
  • Транзакция объединяет несколько зависимых изменений
  • Пользователю не показывают текст внутренней ошибки БД
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Пароли не хранят в открытом виде

$passwordHash = password_hash(
    $password,
    PASSWORD_DEFAULT
);

if (password_verify($password, $passwordHash)) {
    session_regenerate_id(true);
    $_SESSION['user_id'] = $userId;
}

if (password_needs_rehash($passwordHash, PASSWORD_DEFAULT)) {
    // Создать новый хеш после успешной проверки
}
  • Поле для хеша должно допускать изменение длины, обычно VARCHAR(255)
  • Соль создает password_hash()
  • MD5, SHA-1 и обычный SHA-256 не подходят для хранения паролей
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Сквозной сценарий регистрации

GET /register.php -> форма + CSRF-токен
          |
          v
POST /register.php
          |
          +-> проверить метод, типы и CSRF
          +-> нормализовать и валидировать поля
          +-> password_hash()
          +-> INSERT через PDO
          +-> session_regenerate_id()
          +-> записать user_id в сессию
          |
          v
303 -> GET /profile.php

Каждый шаг решает одну проверяемую задачу. Пропуск шага создает конкретный дефект.

Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Связь с лабораторными работами

Лабораторная работа Результат
Среда и основы PHP запуск скрипта, типы, функции, массивы, ошибки
Формы и строки POST, UTF-8, валидация, безопасный HTML, CSRF
БД и аутентификация PDO, prepared statements, хеш пароля, сессия
php -l register.php
curl -i http://localhost:8000/
curl -i -X POST -d "email=user@example.com" \
    http://localhost:8000/register.php
  • В отчет включите команды запуска и проверок
  • Зафиксируйте успешный сценарий и ошибки валидации
  • Не помещайте пароли и секреты в снимки экрана
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Резюме

Язык PHP

  • Скрипт выполняется на сервере
  • Переменные получают тип значения
  • Типы функций проверяют границы операций
  • Массив объединяет последовательность и карту
  • if, match и foreach управляют выполнением

Веб-приложение

  • Входные данные имеют недоверенную форму
  • Валидация и HTML-экранирование решают разные задачи
  • Сессия хранит состояние между запросами
  • CSRF-токен защищает изменяющие запросы
  • PDO и password_hash() закрывают две разные угрозы
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Вопросы для самопроверки: язык PHP

Выполнение и типы

  1. Где выполняется PHP-код и что получает браузер?
  2. Почему declare(strict_types=1) ставят в начале файла?
  3. Можно ли объявить тип обычной локальной переменной?
  4. Чем strlen() отличается от mb_strlen()?

Управление кодом

  1. Чем === отличается от ==?
  2. Когда выбирать if, а когда match?
  3. Как устроены индексированные и ассоциативные массивы PHP?
  4. Что показывают var_dump() и get_debug_type()?
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Вопросы для самопроверки: Web и данные

Формы и состояние

  1. Почему сервер повторяет проверку HTML-формы?
  2. Чем валидация отличается от экранирования?
  3. Почему htmlspecialchars() не защищает SQL-запрос?
  4. Что хранится в cookie сессии и что хранится на сервере?
  5. Для чего нужны session_regenerate_id() и CSRF-токен?

База данных и вход

  1. Как prepared statement защищает структуру SQL?
  2. Почему placeholder нельзя подставить вместо имени столбца?
  3. Как создать и проверить хеш пароля?
  4. Зачем базе нужен уникальный индекс на email?
Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Полезные ресурсы: язык PHP

Синтаксис и значения

Типизация и версии

Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Полезные ресурсы: формы и состояние

Ввод и безопасный HTML

Сессии и CSRF

Лекция 2. Основы PHP
Разработка веб-приложений с использованием программных платформ

Полезные ресурсы: база данных и пароли

PDO

Пароли

Лекция 2. Основы PHP

Лекция 2, 2 академических часа. Цель: перейти от архитектуры запроса к исполняемому PHP-коду и безопасной обработке пользовательских данных. Лекция опирается на клиент-серверную модель, HTTP и MVC из лекции 1. Материал готовит к лабораторным работам по настройке PHP, формам и строкам, PDO и аутентификации.

Объясните, что это обзор основы языка в контексте веб-приложения. Синтаксис сразу применяется к форме регистрации, а не изучается отдельно от HTTP.

Свяжите схему с лекцией 1. Откройте исходный код страницы в браузере после первого запуска и покажите, что в нем есть HTML, но нет PHP.

Выполните команды в аудитории. На 1 сентября 2026 года текущая ветка PHP 8.5, но примеры лекции используют стабильные возможности PHP 8.x и не требуют одной конкретной минорной версии.

Укажите две границы: `<?php` открывает блок кода, а `<?=` выводит выражение. `declare(strict_types=1)` должен быть первым оператором файла.

Сравните `$name` и `$Name`. Подчеркните: PHP типизирован динамически, но поддерживает декларации типов на границах функций и классов.

Не перегружайте вводную лекцию полным справочником типов. `enum`, `never`, `void` и составные декларации можно дать для самостоятельного чтения после освоения функций и классов.

Покажите разные результаты strlen и mb_strlen для русского слова. Если mb_strlen недоступна, проверьте расширение командой `php -m`.

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

`strict_types` существует с PHP 7.0. Режим задается для файла вызова и влияет на скалярные аргументы и возвращаемые значения; он не превращает PHP в язык со статической типизацией.

Покажите отличие результата функции от `echo`: функция вычисляет строку, а вызывающий код решает, куда ее вывести.

Термины "индексированный" и "ассоциативный" описывают способ использования одного типа `array`, а не два разных типа.

Дайте короткое упражнение: добавить третий товар и изменить предел цены. `array_map` и `array_filter` покажите позже как альтернативу, а не как обязательную замену foreach.

`match` появился в PHP 8.0. Это выражение, а не функция. Оно использует строгое сравнение и без подходящей ветви или `default` выбрасывает `UnhandledMatchError`.

Сопоставьте синтаксическую ошибку, TypeError и исключение предметного правила. Не приучайте подавлять ошибки оператором `@`.

`$_POST['email']` может оказаться строкой, массивом или отсутствовать. Не рекомендуйте `$_REQUEST`: он смешивает источники и скрывает способ передачи данных. JSON читают отдельно из `php://input`.

Измените HTML через DevTools и отправьте форму без required. Так видно, почему клиентские ограничения нельзя считать защитой сервера.

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

Отправьте `email[]=a` через curl или DevTools и покажите, что `is_string` предотвращает предупреждение и случайное преобразование массива в строку.

Введите `"><script>alert(1)</script>` и сравните сырой вывод с `e()`. Атакующую строку не нужно "очищать" перед хранением: правильное кодирование выполняют в месте вывода.

Покажите диалог браузера о повторной отправке формы без PRG. Код 303 явно предписывает перейти к GET после POST.

Откройте Application/Storage в DevTools и покажите cookie сессии. Не демонстрируйте реальные рабочие токены или учетные данные.

На локальном HTTP `cookie_secure=true` помешает отправке cookie, поэтому настройка зависит от HTTPS. Объясните, что срок cookie, серверная очистка и прикладной тайм-аут являются разными механизмами.

Атака использует действующую cookie пользователя, но отправляет запрос с другого сайта. Случайный токен неизвестен атакующему сайту и проверяется на сервере.

Не используйте в учебном шаблоне `root` без пароля. Для лабораторной подготовьте отдельную базу и пользователя с минимально нужными правами.

Сравните с опасной строкой `"... WHERE email = '$email'"`, но не оставляйте ее как шаблон для копирования. Подготовленный запрос предотвращает изменение структуры SQL пользовательским значением.

Не показывайте самодельное хеширование. После успешного входа замените ID сессии; при ошибке логина верните одно и то же сообщение для неизвестного email и неверного пароля.

Попросите студентов найти на схеме границу HTTP, границу базы данных и момент создания аутентифицированной сессии.

`php -l` проверяет синтаксис одного файла, но не заменяет запуск и тестирование. Свяжите каждый этап регистрации с отдельным наблюдаемым результатом.

Вернитесь к сквозному сценарию. Попросите студента назвать функцию или механизм для каждого перехода от POST-запроса к записи пользователя и входу в систему.

Для ответа о strict_types попросите привести пример типизированного параметра. Для массивов ожидайте формулировку "один тип, упорядоченная карта".

Разберите ответы на примере регистрации. Если студент предлагает сохранять результат htmlspecialchars в БД, вернитесь к контексту вывода.

Предложите использовать руководство PHP как справочник по конкретной конструкции. Версию на локальной машине всегда сопоставляют со списком поддерживаемых веток.

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

Для лабораторной сначала откройте страницы о PDO и password_hash. Обратите внимание на примеры обработки ошибок и на то, что алгоритм PASSWORD_DEFAULT может измениться.