Урок 12

Функции и организация кода

25 мин

Зачем нужны функции?

Функция — это блок кода с именем, который можно вызывать много раз.

Вы уже использовали встроенные функции PHP:

  • date() — получить дату
  • strlen() — длина строки
  • trim() — убрать пробелы
  • htmlspecialchars() — экранировать HTML

Теперь научимся создавать свои функции.

Создание функции

<?php
// Объявляем функцию
function sayHello($name) {
    return "Привет, $name!";
}

// Вызываем функцию
echo sayHello("Мир");      // Привет, Мир!
echo sayHello("PHP");      // Привет, PHP!
?>

Структура функции

function имя_функции($параметр1, $параметр2 = "значение по умолчанию") {
    // Код функции
    return $результат;
}
  • function — ключевое слово
  • имя_функции — любое имя (без пробелов, начинается с буквы)
  • $параметры — входные данные (можно со значениями по умолчанию)
  • return — возвращает результат (необязательно)

Файл с функциями-помощниками

Создайте файл public/includes/helpers.php:

<?php
declare(strict_types=1);

/**
 * Файл с вспомогательными функциями
 */

/**
 * Безопасный вывод текста
 * Защищает от XSS-атак
 */
function escape(string $text): string {
    return htmlspecialchars($text, ENT_QUOTES, "UTF-8");
}

/**
 * Форматирование даты на русском
 * Вход: "2024-01-15"
 * Выход: "15 января 2024"
 */
function formatDate(string $date): string {
    $months = [
        1 => "января", 2 => "февраля", 3 => "марта",
        4 => "апреля", 5 => "мая", 6 => "июня",
        7 => "июля", 8 => "августа", 9 => "сентября",
        10 => "октября", 11 => "ноября", 12 => "декабря"
    ];
    
    $timestamp = strtotime($date);
    $day = date("j", $timestamp);
    $month = $months[(int)date("n", $timestamp)];
    $year = date("Y", $timestamp);
    
    return "$day $month $year";
}

/**
 * Обрезка текста до указанной длины
 * Не обрезает слова посередине
 */
function truncate(string $text, int $length = 100): string {
    if (strlen($text) <= $length) {
        return $text;
    }
    
    $text = substr($text, 0, $length);
    $text = substr($text, 0, strrpos($text, " "));
    
    return $text . "...";
}

/**
 * Проверка, является ли текущая страница указанной
 * Полезно для выделения активного пункта меню
 */
function isCurrentPage(string $page): bool {
    $current = basename($_SERVER["PHP_SELF"]);
    return $current === $page;
}

/**
 * Генерация класса для пункта меню
 */
function menuClass(string $page): string {
    return isCurrentPage($page) ? "active" : "";
}

/**
 * Склонение слов (1 комментарий, 2 комментария, 5 комментариев)
 */
function pluralize(int $number, string $one, string $two, string $five): string {
    $n = abs($number) % 100;
    $n1 = $n % 10;
    
    if ($n > 10 && $n < 20) {
        return $five;
    }
    if ($n1 > 1 && $n1 < 5) {
        return $two;
    }
    if ($n1 == 1) {
        return $one;
    }
    
    return $five;
}

/**
 * Форматирование числа с окончанием
 * Пример: formatCount(5, "комментарий", "комментария", "комментариев")
 * Результат: "5 комментариев"
 */
function formatCount(int $number, string $one, string $two, string $five): string {
    return $number . " " . pluralize($number, $one, $two, $five);
}
?>

Подключаем helpers.php

Обновите includes/config.php:

<?php
declare(strict_types=1);

// Настройки сайта
$site_name = "Мой блог";
$site_description = "Блог о веб-разработке";
$current_year = date("Y");

// Подключаем функции-помощники
require_once __DIR__ . "/helpers.php";
?>
Что такое declare(strict_types=1);?
Это директива, которая включает строгую проверку типов в PHP. Теперь PHP не будет автоматически преобразовывать типы (например, строку "123" в число 123), что помогает избежать ошибок и делает код более предсказуемым. В PHP 8.4 это обязательная практика!
Что такое __DIR__?
__DIR__ — магическая константа PHP, содержащая путь к папке текущего файла.
Это надёжнее, чем относительный путь, потому что работает из любого места.

Используем функции

Обновите public/index.php:

<?php
require_once "includes/config.php";
$page_title = "Главная — $site_name";
include "includes/header.php";
?>

<h2>Добро пожаловать!</h2>
<p><?= escape($site_description) ?></p>

<h3>Последние посты</h3>

<?php
$posts = [
    [
        "title" => "Привет, мир!",
        "content" => "Это мой первый пост в блоге. Здесь будет много интересного о веб-разработке и программировании на PHP.",
        "date" => "2024-01-15",
        "comments" => 5
    ],
    [
        "title" => "Изучаю PHP",
        "content" => "Сегодня я узнал как работать с функциями. Функции делают код чище и понятнее.",
        "date" => "2024-01-16",
        "comments" => 12
    ],
    [
        "title" => "Docker — это круто",
        "content" => "Docker сильно упрощает настройку окружения для разработки. Рекомендую всем!",
        "date" => "2024-01-17",
        "comments" => 0
    ],
];
?>

<?php foreach ($posts as $post): ?>
<article style="background: #f9f9f9; padding: 20px; margin: 15px 0; border-radius: 8px;">
    <h4 style="margin-bottom: 10px;"><?= escape($post["title"]) ?></h4>
    <small style="color: #888;">
        <?= formatDate($post["date"]) ?> • 
        <?= formatCount($post["comments"], "комментарий", "комментария", "комментариев") ?>
    </small>
    <p style="margin-top: 10px;"><?= truncate(escape($post["content"]), 80) ?></p>
</article>
<?php endforeach; ?>

<?php include "includes/footer.php"; ?>

Обновите меню с активным пунктом

В includes/header.php замените меню:

<nav>
    <a href="index.php" class="<?= menuClass("index.php") ?>">Главная</a>
    <a href="about.php" class="<?= menuClass("about.php") ?>">О нас</a>
    <a href="feedback.php" class="<?= menuClass("feedback.php") ?>">Обратная связь</a>
</nav>

И добавьте стиль для активного пункта:

nav a.active { 
    font-weight: bold; 
    text-decoration: underline;
}

Результат

Откройте http://localhost:8080. Теперь вы видите:

  • Даты на русском языке
  • Правильное склонение "комментариев"
  • Обрезанный текст с многоточием
  • Подсвеченный активный пункт меню

Современные возможности PHP 8+

PHP 8 добавил множество удобных функций. Вот самые полезные:

Строковые функции

<?php
// str_starts_with и str_ends_with — проверка начала/конца строки
$email = "user@example.com";

// Раньше (громоздко):
if (substr($email, 0, 5) === "user@") { }
if (substr($email, -4) === ".com") { }

// PHP 8+ (читаемо!):
if (str_starts_with($email, "user@")) { }
if (str_ends_with($email, ".com")) { }

// str_contains — проверка подстроки
$text = "Привет, мир!";

// Раньше:
if (strpos($text, "мир") !== false) { }

// PHP 8+:
if (str_contains($text, "мир")) { }
?>

Оператор Nullsafe (?->)

<?php
// Раньше (много проверок):
$country = null;
if ($user !== null) {
    if ($user->address !== null) {
        $country = $user->address->country;
    }
}

// PHP 8+ (одна строка!):
$country = $user?->address?->country;
// Если что-то равно null, вернётся null без ошибки
?>

Named Arguments (именованные аргументы)

<?php
// Раньше — нужно помнить порядок аргументов:
htmlspecialchars($text, ENT_QUOTES, "UTF-8", false);

// PHP 8+ — можно указать по имени:
htmlspecialchars($text, encoding: "UTF-8", double_encode: false);

// Или наша функция:
function createUser(string $name, string $email, bool $isAdmin = false, bool $isActive = true) {
    // ...
}

// Вызов с именованными аргументами:
createUser(name: "Иван", email: "ivan@example.com", isAdmin: true);
?>

Match expression (вместо switch)

<?php
// switch (громоздко):
switch ($status) {
    case "draft": $label = "Черновик"; break;
    case "published": $label = "Опубликовано"; break;
    case "archived": $label = "В архиве"; break;
    default: $label = "Неизвестно";
}

// PHP 8+ match (компактно):
$label = match($status) {
    "draft" => "Черновик",
    "published" => "Опубликовано",
    "archived" => "В архиве",
    default => "Неизвестно",
};
?>

Обновим функцию truncate для поддержки Unicode:

/**
 * Обрезка текста (с поддержкой Unicode!)
 */
function truncate(string $text, int $length = 100): string {
    if (mb_strlen($text) <= $length) {
        return $text;
    }
    
    $text = mb_substr($text, 0, $length);
    $lastSpace = mb_strrpos($text, " ");
    
    if ($lastSpace !== false) {
        $text = mb_substr($text, 0, $lastSpace);
    }
    
    return $text . "...";
}

Функции mb_* корректно работают с русским текстом (и любым Unicode).

Принципы написания функций

  • Одна функция — одна задача. Не делайте функции, которые делают слишком много.
  • Понятные имена. formatDate понятнее чем fd.
  • Документация. Комментарии с описанием что функция делает.
  • Возвращайте значение, а не выводите через echo.
  • Используйте PHP 8+ функции — они делают код чище и понятнее.
Модуль 2 завершён!

Вы научились:
✅ Работать с PHP в реальных файлах
✅ Разделять код на файлы
✅ Обрабатывать формы
✅ Создавать свои функции

В следующем модуле подключим базу данных MySQL!

Мы используем файлы cookie для улучшения работы сайта и персонализации контента. Продолжая использовать сайт, вы соглашаетесь с использованием cookies в соответствии с нашей Политикой конфиденциальности.