Шаблоны header и footer: эффективные приёмы в PHP

Раздел: Веб-разработка -> Шаблонизация

Обзор шаблонов header и footer в PHP

При разработке веб-приложений на PHP часто возникает необходимость повторно использовать общие элементы страниц: шапку (header) и подвал (footer). Грамотное отделение этих частей от основного контента упрощает поддержку и изменение дизайна сайта. Рассмотрим несколько подходов к реализации шаблонов header.php и footer.php, их преимущества и недостатки.

Основной подход: подключение через include / require

Наиболее распространённый и эффективный способ для небольших и средних проектов - вынести код шапки и подвала в отдельные файлы и подключать их в основном шаблоне. Это позволяет менять внешний вид на всех страницах, изменив всего два файла.

<!-- header.php -->
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title><?= $title ?? 'Мой сайт' ?></title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
    <header>
        <h1>Заголовок сайта</h1>
        <nav><!-- навигация --></nav>
    </header>

Index php tpl (шаблоны (tpl) в php)

<!-- footer.php -->
    <footer>
        <p>&copy; 2025 Мой сайт</p>
    </footer>
</body>
</html>

Header php footer php (шаблоны header и footer в php)

<!-- index.php -->
<?php
$title = 'Главная страница';
require 'header.php';
?>
<main>
    <h2>Добро пожаловать</h2>
    <p>Основной контент страницы.</p>
</main>
<?php require 'footer.php'; ?>

Index php option layout (настройка макета (layout) в php)

Типичные ошибки и их решение

  • Ошибка путей. Если файлы шаблона лежат в подпапке, require может не найти их. Решение: использовать абсолютный путь через __DIR__ или константу ROOT_PATH:
    require __DIR__ . '/../templates/header.php';
  • Дублирование подключения. Если один и тот же файл подключается дважды, могут возникнуть конфликты (например, повторное объявление функции). Используйте require_once или include_once.
  • Область видимости переменных. Переменные, объявленные до подключения, доступны внутри подключаемого файла, но не наоборот. Для передачи данных лучше использовать локальные переменные или массивы.

Как сделать так, чтобы переменная $title из основного файла автоматически отображалась в заголовке страницы? Ответ - передавать её до вызова require, как в примере выше. Это один из способов динамической настройки header.

Вариант 1: Функция для вывода header и footer

Как организовать вывод шапки и подвала, чтобы не писать каждый раз require в каждом файле?

Создаются функции, которые инкапсулируют подключение и позволяют передавать параметры в виде массива.

<?php
function renderHeader(array $data = []) {
    $title = $data['title'] ?? 'Default';
    $css = $data['css'] ?? '/css/style.css';
    include 'header.php'; // header.php использует $title, $css
}

function renderFooter() {
    include 'footer.php';
}
?>

<!-- Использование -->
<?php
renderHeader(['title' => 'О нас', 'css' => '/css/about.css']);
?>
<main>...</main>
<?php renderFooter(); ?>

Проблема: глобальное загрязнение переменными.

Внутри header.php переменные $title и $css извлекаются из локальной области видимости функции, но если в основном коде есть такие же переменные, конфликтов не будет, так как они изолированы. Однако нужно следить за именами параметров.

Вариант 2: Буферизация вывода и шаблонизаторы

Как отделить логику от представления и поддерживать сложные вложенные шаблоны?

Используются шаблонизаторы (Twig, Smarty) или встроенная буферизация с помощью ob_start().

<?php
ob_start();
?>
<main>
    <h2>Контент</h2>
</main>
<?php
$content = ob_get_clean();
require 'layout.php'; // layout содержит header, footer и место для $content
?>

В layout.php шапка и подвал уже статичны, а в середине вставлено <?= $content ?>. Это проще, чем компоновать отдельные файлы.

Проблема: уровень вложенности и отладка.

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

Вариант 3: Объектно-ориентированный подход

Как сделать систему шаблонов гибкой и расширяемой для крупных проектов?

Создаётся класс Template, который загружает файлы шаблонов и подставляет данные.

<?php
class Template {
    private string $basePath;
    public function __construct(string $basePath = '') {
        $this->basePath = $basePath;
    }
    public function render(string $__template, array $__data = []): void {
        extract($__data);
        include $this->basePath . '/' . $__template . '.php';
    }
}
// Использование
$tpl = new Template(__DIR__ . '/templates');
$tpl->render('header', ['title' => 'Контакты']);
?>
<main>...</main>
<?php $tpl->render('footer'); ?>

Этот подход легко масштабируется: можно добавить методы для вложенных шаблонов, кэширования и т.д.

Проблема: производительность при большом количестве вызовов extract().

Это незначительно, но в высоконагруженных системах лучше передавать данные напрямую через $this->data внутри шаблона.

Вариант 4: Использование include с проверкой на существование файла

Как избежать ошибок при отсутствии файла header.php?

Применяется file_exists() перед подключением или условный include без ошибки.

<?php
$headerFile = 'header.php';
if (!file_exists($headerFile)) {
    // логирование или альтернативный контент
    echo '<!-- header not found -->';
} else {
    include $headerFile;
}
?>

Этот вариант уместен, когда шаблоны могут отсутствовать в некоторых окружениях (например, при разработке модульной системы).

Проблема: дублирование кода проверки в каждом файле.

Решение: вынести проверку в функцию или использовать контейнер, который гарантирует наличие шаблона.

Расширенные примеры использования шаблонов header и footer

Пример 1: Динамический header с разными стилями для разных страниц

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

Пример
<!-- header.php -->
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title><?= htmlspecialchars($title) ?></title>
    <link rel="stylesheet" href="<?= $cssFile ?>">
</head>
<body class="<?= $bodyClass ?? '' ?>">
    <header class="<?= $headerClass ?? 'main-header' ?>">
        <nav>
            <a href="/" class="<?= $currentPage === 'home' ? 'active' : '' ?>">Главная</a>
            <a href="/about" class="<?= $currentPage === 'about' ? 'active' : '' ?>">О нас</a>
        </nav>
    </header>
Пример
<!-- about.php -->
<?php
$data = [
    'title' => 'О компании',
    'cssFile' => '/css/about.css',
    'bodyClass' => 'about-page',
    'headerClass' => 'header-about',
    'currentPage' => 'about'
];
extract($data);
include 'header.php';
?>
<main>...</main>
<?php include 'footer.php'; ?>
Результат: в браузере отобразится страница с заголовком "О компании", подключён about.css, body получит класс about-page, а ссылка "О нас" будет подсвечена классом active.

Пример 2: Вложенные шаблоны с буферизацией (layout + content)

Создадим файл layout.php, который будет содержать общую обёртку, а контент будем подставлять через переменную $content.

Пример
<!-- layout.php -->
<!DOCTYPE html>
<html>
<head>
    <title><?= $title ?? 'Без названия' ?></title>
    <link rel="stylesheet" href="/css/<?= $style ?? 'main' ?>.css">
</head>
<body>
    <header>...</header>
    <main><?= $content ?></main>
    <footer>...</footer>
</body>
</html>
Пример
<!-- page.php, который использует layout -->
<?php
$title = 'Галерея';
$style = 'gallery';
ob_start();
?>
<section class="gallery">
    <img src="photo1.jpg" alt="Фото 1">
    <img src="photo2.jpg" alt="Фото 2">
</section>
<?php
$content = ob_get_clean();
include 'layout.php';
?>
Результат: страница содержит header, footer и вставленный блок с галереей. Стили из gallery.css подключаются в head.

Пример 3: Многоуровневое наследование шаблонов с помощью Twig

Хотя Twig не является встроенной функцией PHP, его часто используют в современных проектах. Покажем, как с его помощью организовать header и footer.

Пример
<!-- base.html.twig -->
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}Мой сайт{% endblock %}</title>
    <link rel="stylesheet" href="/css/style.css">
</head>
<body>
    <header>{% block header %}Шапка по умолчанию{% endblock %}</header>
    <main>{% block content %}{% endblock %}</main>
    <footer>{% block footer %}Подвал по умолчанию{% endblock %}</footer>
</body>
</html>
Пример
<!-- index.html.twig, расширяющий base -->
{% extends 'base.html.twig' %}
{% block title %}Главная{% endblock %}
{% block header %}
    <h1 class="fw-bold">Приветствую!</h1>
{% endblock %}
{% block content %}
    <p>Основной контент.</p>
{% endblock %}
Результат: страница получает заголовок "Главная", header заменён на кастомный, остальное - из базового шаблона. Файлы шаблонов обрабатываются Twig.

Пример 4: Автоматическая подстановка мета-тегов через массив данных

Расширим стандартный header.php для поддержки переменного количества мета-тегов.

Пример
<!-- header.php -->
<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title><?= $title ?></title>
    <?php if (!empty($meta)): ?>
        <?php foreach ($meta as $name => $content): ?>
            <meta name="<?= htmlspecialchars($name) ?>" content="<?= htmlspecialchars($content) ?>">
        <?php endforeach; ?>
    <?php endif; ?>
    <?php if (!empty($styles)): ?>
        <?php foreach ($styles as $href): ?>
            <link rel="stylesheet" href="<?= $href ?>">
        <?php endforeach; ?>
    <?php endif; ?>
</head>
<body>
    <header>...</header>
Пример
<!-- page.php -->
<?php
$data = [
    'title' => 'Контакты',
    'meta' => [
        'description' => 'Страница контактов компании',
        'keywords' => 'контакты, телефон, email'
    ],
    'styles' => ['/css/contact.css', '/css/icons.css']
];
extract($data);
include 'header.php';
?>
<main>...</main>
<?php include 'footer.php'; ?>
Результат: в head появятся два meta-тега и два link для CSS.

Пример 5: Использование include_once для предотвращения повторного подключения библиотек

Если в header.php подключается файл с функциями, а footer.php также его содержит, возникает ошибка переопределения. Решение - include_once.

Пример
<!-- functions.php -->
<?php
function getCopyrightYear(): string {
    return '2005-' . date('Y');
}
?>
Пример
<!-- header.php -->
<?php include_once 'functions.php'; ?>
<!DOCTYPE html>...</pre>

<!-- footer.php -->
<?php include_once 'functions.php'; ?>
<footer>&copy; <?= getCopyrightYear() ?> Моя компания</footer>
Ошибки не возникает, так как второй раз файл не подключается.

Пример 6: Передача переменных из сессии для отображения статуса пользователя в header

Пример
<!-- header.php -->
...
<header>
    <div class="user-info">
        <?php if (isset($_SESSION['user'])): ?>
            <span>Привет, <?= htmlspecialchars($_SESSION['user']['name']) ?>!</span>
            <a href="/logout">Выход</a>
        <?php else: ?>
            <a href="/login">Войти</a>
        <?php endif; ?>
    </div>
</header>
При залогиненном пользователе в шапке отображается его имя и ссылка на выход.

Шаблоны header и footer в PHP - comments

En
Header php footer php (php)