basename($_SERVER['SCRIPT_NAME']) === 'index.php'
301 редирект с /index.php на / для SEO и чистоты URL.
if ($request_path !== '') { include '404.php'; exit; }
Любой непустой путь (кроме корня) вызывает страницу 404.
/?q=текст
Поиск по title, preview_text, content в таблице articles и по title, intro в code_posts.
🔒 Безопасность: htmlspecialchars() + подготовленные запросы PDO.
?page=N
По 10 статей на страницу + 6 код-постов.
Лимиты привязаны через bindValue(PDO::PARAM_INT) — защита от инъекций.
| Таблица | Назначение |
|---|---|
articles | Статьи блога (id, title, preview_text, content, image, category_id, created_at) |
code_posts | Посты с кодом/примерами (id, title, intro, image, category_id, created_at) |
categories | Категории (id, name) |
htmlspecialchars()session_start(), без утечекincludes/db.php: new PDO("mysql:host=localhost;dbname=...", "user", "pass")/uploads — 755, запретить выполнение PHP через .htaccessmazzick@iot-things:~$ sudo cat /var/www/blog/public_html/index.php
<?php
// === SEO: редирект с /index.php на / ===
if (basename($_SERVER['SCRIPT_NAME']) === 'index.php'
&& parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) === '/index.php') {
header('HTTP/1.1 301 Moved Permanently');
header('Location: /');
exit;
}
// ====================================
// Получаем запрошенный путь (без query string)
$request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$request_path = trim($request_uri, '/');
// Любой не-пустой путь — 404
if ($request_path !== '') {
http_response_code(404);
include '404.php';
exit;
}
session_start();
require 'includes/db.php';
$search_query = trim($_GET['q'] ?? '');
$is_search = !empty($search_query);
// Настройки пагинации
$articles_per_page = 10; // сколько статей на одной странице
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
$page = max(1, $page); // минимум — первая страница
$offset = ($page - 1) * $articles_per_page;
// Подсчёт общего количества статей
$total_stmt = $pdo->query("SELECT COUNT(*) FROM articles");
$total_articles = $total_stmt->fetchColumn();
$total_pages = ceil($total_articles / $articles_per_page);
// Основной запрос с пагинацией
if ($is_search) {
$search_sql = " AND (a.title LIKE :search OR a.preview_text LIKE :search OR a.content LIKE :search)";
} else {
$search_sql = "";
}
$stmt = $pdo->prepare("SELECT a.id, a.title, a.preview_text, a.image, a.created_at, c.name as category
FROM articles a
LEFT JOIN categories c ON a.category_id = c.id
WHERE 1=1 $search_sql
ORDER BY a.created_at DESC
LIMIT :limit OFFSET :offset");
// Привязываем limit/offset ВСЕГДА
$stmt->bindValue(':limit', $articles_per_page, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
// Привязываем search ТОЛЬКО если нужно
if ($is_search) {
$stmt->bindValue(':search', '%' . $search_query . '%', PDO::PARAM_STR);
}
$stmt->execute();
?>
<?php include 'includes/header.php'; ?>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Ключевые мета-теги -->
<title>IoTProf Blog — Публикации о IoT, промышленной автоматизации и IT</title>
<meta name="description" content="Технический блог о промышленном IoT, автоматизации, Linux и сетевой инфраструктуре. Практические руководства, разборы оборудования и кейсы.">
<link rel="canonical" href="https://blog.iotprof.ru" />
<meta name="robots" content="index, follow">
<!-- OpenGraph (для соцсетей) -->
<meta property="og:title" content="IoTProf Blog — Публикации о IoT и автоматизации">
<meta property="og:description" content="Технический блог о промышленном IoT, Linux и сетевых решениях.">
<meta property="og:url" content="https://blog.iotprof.ru/">
<meta property="og:type" content="website">
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/css/callback.css">
</head>
<body>
<div class="it-logo">
<div class="it-abbr">iOt</div>
<div class="it-blog">blog</div>
<div class="it-slogan">там, где технологии думают за нас</div>
</div>
<div class="container">
<h1>Техноблог pro it и iot</h1>
<!-- ТЕСТОВАЯ ФОРМА -->
<form method="GET" action="/" style="display: flex; gap: 8px; margin: 10px 0; width: 100%;">
<input type="text" name="q" value="<?= htmlspecialchars($_GET['q'] ?? '') ?>"
placeholder="Поиск по блогу..." style="width: 70%;">
<input type="submit" value="🔍 Поиск" style="width: 30%;">
</form>
<?php include 'includes/category_block.php'; ?>
<?php if ($is_search): ?>
<h2>🔍 Результаты поиска по «<?= htmlspecialchars($search_query) ?>»</h2>
<?php else: ?>
<h2>📜 Последние статьи</h2>
<?php endif; ?>
<section class="posts-section">
<div class="posts-grid">
<?php
$counter = 0;
while ($row = $stmt->fetch()):
$counter++;
?>
<article class="post-card article-card"> <!-- Убрали $image_position_class -->
<?php if ($row['image']): ?>
<img src="/uploads/<?= htmlspecialchars($row['image']) ?>" alt="<?= htmlspecialchars($row['title']) ?>" class="post-image">
<?php endif; ?>
<div class="post-content">
<span class="date_cat_article"><?= $row['category'] ?: 'Без категории' ?> | <?= date('Y-m-d', strtotime($row['created_at'])) ?></span>
<h3><a href="article.php?id=<?= $row['id'] ?>"><?= htmlspecialchars($row['title']) ?></a></h3>
<p><?= nl2br(htmlspecialchars($row['preview_text'])) ?></p>
</div>
</article>
<?php endwhile; ?>
</div>
</section>
<!-- ============ БЛОК ПОСЛЕДНИХ КОДОВ С ПАГИНАЦИЕЙ ============ -->
<?php
// Настройки пагинации для кодов
$codes_per_page = 6;
$codes_offset = ($page - 1) * $codes_per_page;
if ($is_search) {
$search_sql_codes = " AND (cp.title LIKE :search OR cp.intro LIKE :search)";
} else {
$search_sql_codes = "";
}
$codes_stmt = $pdo->prepare("
SELECT cp.id, cp.title, cp.intro, cp.image, cp.created_at, c.name as category_name
FROM code_posts cp
LEFT JOIN categories c ON cp.category_id = c.id
WHERE 1=1 $search_sql_codes
ORDER BY cp.created_at DESC
LIMIT :limit OFFSET :offset
");
$codes_stmt->bindValue(':limit', $codes_per_page, PDO::PARAM_INT);
$codes_stmt->bindValue(':offset', $codes_offset, PDO::PARAM_INT);
if ($is_search) {
$codes_stmt->bindValue(':search', '%' . $search_query . '%', PDO::PARAM_STR);
}
$codes_stmt->execute();
?>
<?php if ($is_search): ?>
<h3>Код-посты</h3>
<?php else: ?>
<h2>⚡ Последние код-посты</h2>
<?php endif; ?>
<section class="posts-section">
<div class="posts-grid">
<?php while ($code = $codes_stmt->fetch()): ?>
<article class="post-card code-card">
<?php if ($code['image']): ?>
<img src="/uploads/<?= htmlspecialchars($code['image']) ?>" alt="<?= htmlspecialchars($code['title']) ?>" class="post-image">
<?php endif; ?>
<div class="post-content">
<span class="category-tag">
<?= $code['category_name'] ?: 'Без категории' ?> |
<?= date('Y-m-d', strtotime($code['created_at'])) ?>
</span>
<h3><a href="code_post.php?id=<?= $code['id'] ?>"><?= htmlspecialchars($code['title']) ?></a></h3>
<?php if (!empty($code['intro'])): ?>
<p><?= nl2br(htmlspecialchars($code['intro'])) ?></p>
<?php endif; ?>
</div>
</article>
<?php endwhile; ?>
</div>
</section>
<!-- ============ КОНЕЦ БЛОКА КОДОВ ============ -->
<div class="pagination">
<?php if ($page > 1): ?>
<a href="?page=<?= $page - 1 ?>" class="btn">← Назад</a>
<?php endif; ?>
<span>Страница <?= $page ?> из <?= $total_pages ?></span>
<?php if ($page < $total_pages): ?>
<a href="?page=<?= $page + 1 ?>" class="btn">Вперёд →</a>
<?php endif; ?>
</div>
<div>
</div>
<?php include 'includes/callpack.php'; ?>
<?php include 'includes/footer.php'; ?>
Комментарии
Пока нет комментариев. Будьте первым!