Как создать простой сайт на PHP: структура, шаблоны и полезные приёмы
Важно: кодовые примеры сохраняют поведение оригинального проекта. Убедитесь, что корневая папка веб‑сервера указывает на каталог site.

Если вы получите базовое понимание того, как строить сайт на PHP, вы освоите включение файлов и генерацию вывода — самые простые способы кастомизировать веб‑страницу. Возможно, вы пока не знаете, с чего начать, или какие шаги предпринять для добавления новой функциональности. В этом руководстве я предлагаю компактную, понятную структуру проекта и описываю полезные PHP‑функции, которые приносят реальную пользу.
Что вы будете строить
Образец сайта (есть в репозитории GitHub) — простой сайт о птицах. Там несколько разделов, информационные страницы и главная страница. Главное не конечный дизайн, а подходы и идеи, которые вы сможете применить к своим проектам.
Если можно, скачайте репозиторий с GitHub и установите сайт локально, прежде чем продолжать — так вы сможете экспериментировать с кодом и видеть изменения сразу.
Краткое определение терминов
- Markdown: лёгкий формат разметки текста, читаемый человеком и легко конвертируемый в HTML.
- Шаблон (template): файл с общей HTML‑структурой, который повторно используют несколько страниц.
- DOCROOT: корневой каталог веб‑сервера, откуда отдаются файлы сайта.
Общая структура сайта
Ключевые файлы и каталоги проекта:
composer.json
data/
funcs.php
md/
site/
index.php
tpl/- Каждая страница представлена PHP‑скриптом в каталоге site. Это даёт небольшую избыточность, но упрощает логику и маршрутизацию. При настройке сервера укажите DOCROOT на каталог site.
- Каталог md содержит исходники в формате Markdown для отдельных страниц.
- Каталог tpl хранит шаблоны и файлы, определяющие общую HTML‑структуру, которую используют несколько страниц.
- Основная PHP‑логика сосредоточена в funcs.php.
Связанные материалы: как настроить локальный стек WAMP/LAMP/Valet.
Как работает сайт — пошагово
Ниже разберём главные компоненты и объясним, как они взаимодействуют.
Bootstrap: зачем и как подключать
Bootstrap — фронтенд‑фреймворк с набором CSS и JS для быстрого создания адаптивного интерфейса. Можно хранить файлы локально, но чаще проще подключать их через CDN, чтобы сократить объём раздачи и воспользоваться кешем браузера.
Откройте файл tpl/head.php и вы увидите пример подключения:
Совет: для продакшна иногда лучше зафиксировать версию и хранить CSS на своём сервере, если политика безопасности запрещает внешние источники.
Базовое шаблонирование
Файл site/index.php — это домашняя страница. В начале он подключает funcs.php и шаблон TPL_DIR.”/home.php”. В funcs.php константа TPL_DIR определяется как абсолютный путь к каталогу tpl.
Посмотрите tpl/home.php: это самый внешний каркас HTML — она содержит doctype и HTML‑элемент и подключает head и body через include.
Шаблон для раздела birds использует другой body: tpl/birds/body.tpl. В нём другой макет с боковой панелью.
Важно: такой подход оставляет заголовки, меню и футер общими, уменьшая повторение кода.
Разбор Markdown в HTML
В composer.json подключена библиотека erusev/parsedown. Это библиотека для преобразования Markdown в HTML.
Пример функции show_content() в funcs.php:
function show_content() {
$file = MD_DIR.PAGE.'.md';
if (file_exists($file)) {
$Parsedown = new Parsedown();
echo $Parsedown->text(file_get_contents($file));
} else if (function_exists("content")) {
echo content();
}
}Логика простая: если существует Markdown‑файл для текущей страницы, Parsedown конвертирует его в HTML и выводит. Если файла нет, ищется функция content(), которую может определить конкретная страница для динамического вывода.
Преимущества такого подхода:
- Авторство контента проще (Markdown читается лучше, чем HTML).
- Лёгкость версионирования — просто коммитим .md в Git.
- Быстрая разработка контента без админ‑панели.
Ограничения:
- Markdown не покрывает все случаи, где нужен сложный динамический вывод.
- Формат файлов должен быть согласован командой, иначе парсинг даст неконсистентный результат.
Загрузка и парсинг метаданных
Функция get_json в funcs.php читает JSON‑файл и возвращает ассоциативный массив:
function get_json($file) {
$data_file = DATA_DIR."/".$file;
if (!file_exists($data_file)) {
return array();
}
if (($json = file_get_contents($data_file)) === false) {
return array();
}
if (($out = json_decode($json, true)) === null) {
return array();
}
return $out;
}Такой код аккуратно оборачивает чтение/декодирование и возвращает пустой массив при ошибке. Для простых сайтов это часто удобнее, чем подключение СУБД.
На сайте есть два файла метаданных: data/titles.json и data/featured.json. Пример titles.json:
{
"/": "Home",
"/about": "About",
"/birds": "Bird profiles",
...
}Это позволяет хранить все заголовки в одном месте и использовать их в навигации, хлебных крошках и сайдбаре.
Функция page_title использует get_titles():
function page_title($page = PAGE) {
$titles = get_titles();
return array_key_exists($page, $titles) ? $titles[$page] : basename($page);
}Хлебные крошки
Функция breadcrumbs() строит список заголовков для каждой части URL. Пример: для /birds/blue-tit она сформирует массив с заголовками для “/“, “/birds” и “/birds/blue-tit”.
function breadcrumbs() {
$items = array();
$titles = get_titles();
$parts = explode("/", PAGE);
$href = "";
foreach ($parts as $part) {
$href .= ($href == "/" ? "" : "/").$part;
$items[$href] = $titles[$href];
}
...
}Хлебные крошки повышают удобство навигации и SEO, показывая иерархию страниц.
Получение информации о времени изменения контента
В футере (tpl/footer.php) выводится сообщение «Last updated» с помощью filemtime:
if (file_exists($file = MD_DIR.PAGE.'.md')) {
echo 'Last updated: '.date('r', filemtime(MD_DIR.PAGE.'.md'));
}Это простой способ получить дату последнего изменения содержимого. Минусы: если вы редактируете файл вне Git, timestamp изменится; если вы хотите больше контроля, храните дату отдельно в метаданных.
Извлечение данных с помощью регулярных выражений
Иногда нужно пересобирать данные из сырого Markdown, например — взять заголовок, изображение и первое предложение для списка новостей. В news/index.php парсятся файлы в md/news и регулярными выражениями извлекаются нужные части.
Пример извлечения заголовка (строка, начинающаяся с #):
if (preg_match("/^#\s+(.+)/", $contents, $matches)) {
$title = $matches[1];
}Внимание: регулярные выражения удобны, но требуют дисциплины в оформлении Markdown. Альтернативы — фронтматтер (YAML metadata в начале файла) или явные JSON‑данные.
Выбор случайного контента
На странице /birds для разнообразия показывают случайное изображение. Это реализовано так:
function content() {
$files = scandir(SITE_DIR."/img");
$files = array_filter($files, function($file) { return $file[0] != '.'; });
$file = $files[array_rand($files)];
echo 'Birds
';
echo '
';
}Идея простая: поместите все нужные изображения в отдельный каталог, прочитайте список и выберите случайный файл.
Практические рекомендации и шаблоны принятия решений
Когда такой подход работает хорошо
- Небольшие сайты с ограниченным количеством страниц.
- Блоги и справочники, где контент управляется Markdown и версионируется в Git.
- Проекты, где простота важнее гибкости админ‑панели.
Когда лучше выбрать другое решение
- Если нужно сложное моделирование данных с отношениями, выбор в пользу СУБД и CMS (например, WordPress, Drupal) чаще оправдан.
- Для больших проектов с множественными редакторами и правами доступа удобнее использовать CMS или headless CMS.
Альтернативные подходы
- Статические генераторы сайтов (Jekyll, Hugo, Eleventy) — генерируют HTML на этапе сборки и идеально подходят для простых сайтов, где нет сервера и PHP.
- Headless CMS + фронтенд (например, Strapi + React) — если нужен гибкий API и современный фронтенд.
- Полноценная CMS (WordPress) — если нужна админка, плагины и развитая экосистема.
Модель мышления и эвристики при разработке
- Разделяй вид и логику: шаблоны отвечают за HTML, funcs.php — за работу с данными.
- Принцип единой ответственности: каждая функция делает одно действие.
- Минимизируй состояние: храните конфигурацию в JSON или константах, а не в глобальных переменных.
- Предпочитайте простоту и явность перед сложной магией автоматического поведения.
Безопасность и надёжность (харднинг)
- Всегда проверяйте входные данные: используйте фильтры для GET/POST и экранирование при выводе.
- Для вывода Markdown используйте безопасные опции Parsedown (или ParsedownExtra с безопасной конфигурацией). Не выводите сырой HTML из неподтверждённых источников.
- Ограничьте доступ к каталогу md и data по правам файловой системы, если сервер отдаёт файлы напрямую.
- Не доверяйте filemtime для важных аудитов; используйте метаданные в файлах, если нужен точный контроль.
Примечание: Этот раздел даёт общие рекомендации. Для продакшн‑сайта проведите полноценный аудит безопасности.
Практическая методология внедрения (мини‑SOP)
- Клонируйте репозиторий и установите зависимости Composer.
- Настройте DOCROOT на каталог site.
- Проверьте права доступа для каталогов md, data и img.
- Запустите локальный сервер и откройте главную страницу.
- Внесите тестовые изменения в md/home.md и проверьте show_content().
- Добавьте новую страницу — создайте site/newpage.php и md/newpage.md.
- Протестируйте хлебные крошки, заголовки и футер.
- Перенесите изменения в Git и задеплойте.
Ролевые чек‑листы
Разработчик:
- Настроить среду разработки.
- Провести unit‑тесты для функций в funcs.php.
- Проверить обработку ошибок в get_json.
Девопс:
- Настроить права файлов и владельцев.
- Обеспечить резервное копирование md и data.
- Настроить автоматический деплой (CI).
Контент‑менеджер:
- Соблюдать формат Markdown.
- Поддерживать единообразие frontmatter (если используется).
- Проверять дату обновления и метаданные.
Тестовые сценарии и критерии приёмки
Критерии приёмки
- Главная страница загружается (HTTP 200).
- show_content выводит HTML для существующих .md файлов.
- breadcrumbs строит корректную цепочку для вложенных страниц.
- get_json возвращает массив для валидного JSON и пустой массив при ошибке.
- Функция content на странице /birds выводит изображение из каталога img.
Тестовые кейсы
- Удалить titles.json и проверить, что page_title возвращает basename.
- Переименовать один .md файл и проверить вывод Last updated.
- Создать некорректный JSON в data и убедиться, что сайт не падает.
Матричная таблица совместимости и миграция
- Платформы: любой веб‑сервер с поддержкой PHP 7.4+; для безопасности и производительности рекомендуем PHP 8.
- Зависимости: Composer для Parsedown.
- Миграция на СУБД: перенести data/*.json в таблицы, заменить get_json вызовами к БД.
Частые ошибки и способы их устранения
- DOCROOT указывает не на site — решение: перепривязать корень виртуального хоста.
- Права файлов мешают чтению — проверить chmod/chown.
- Неправильный формат Markdown — задать стиль и добавить линтер для .md.
- Подключение внешних ресурсов блокируется CSP — обновить политику CSP или хранить ресурсы локально.
Краткая галерея edge‑кейсов
- Если Parsedown конвертирует HTML внутри Markdown, и вы хотите его убрать, включите безопасный режим или предварительно фильтруйте содержимое.
- Если вам нужно хранить структурированные данные (например, характеристики птицы), используйте JSON рядом с Markdown или frontmatter в начале файла.
Небольшая справка (глоссарий)
- DOCROOT — корень веб‑сервера.
- Parsedown — PHP‑библиотека для конвертации Markdown в HTML.
- filemtime — функция PHP для получения времени последней модификации файла.
План развития и масштабирования
- Добавить CI, запускающий линтеры и unit‑тесты.
- Ввести frontmatter в Markdown для структурированных полей.
- При росте трафика перенести статические ресурсы на CDN и кешировать HTML.
- Если появится потребность — перенести метаданные в СУБД.
Примеры кода и сниппеты (шпаргалка)
Выбор случайного файла в каталоге img:
$files = scandir(SITE_DIR."/img");
$files = array_filter($files, function($file) { return $file[0] != '.'; });
$file = $files[array_rand($files)];Безопасное чтение JSON:
$json = @file_get_contents($path);
$data = json_decode($json, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$data = array();
}Decision tree: как выбрать архитектуру (Mermaid)
flowchart TD
A[Небольшой сайт, статичный контент?] -->|да| B[Flat‑file + Markdown]
A -->|нет| C[Нужна админ‑панель?]
C -->|да| D[CMS 'WordPress/Drupal']
C -->|нет| E[Headless CMS + frontend]
B --> F[Использовать Parsedown + JSON]
D --> G[Использовать существующие плагины]
E --> H[API + SPA]Примечания по локализации и GDPR
- Если вы собираете персональные данные посетителей (комментарии, подписки), храните их в соответствии с законодательством и добавьте страницу политики конфиденциальности.
- Для целевых стран учтите локальные требования к хранению данных и cookie‑баннеры.
Резюме
- Простая архитектура с каталогом site и шаблонами tpl упрощает разработку.
- Markdown + Parsedown ускоряют публикацию контента.
- JSON‑файлы удобны для метаданных, но имеют ограничения при масштабировании.
- Регулярные выражения дают гибкость, но требуют согласованного формата файлов.
Итог: этот подход позволяет быстро собирать и поддерживать небольшие веб‑проекты. Когда потребности вырастут, у вас уже будут понятные места для миграции на более сложные системы.
Похожие материалы
Защита смарт‑телевизора от слежки
Как включить тёмную тему в популярных приложениях
Резервное копирование в облако: дублирование
Pages для Mac — полное руководство по шаблонам
Как управлять доступом к местоположению на iPhone