вторник, 14 сентября 2010 г.

Как написать свой движок блога

Вступление.

Собственный движок блога обладает многими преимуществами. В нем можно поменять вообще все, до мелочей и уйдет на это в 10 раз меньше времени, чем ковыряться в чужих движках, таких как WordPress или DLE (DLE особенно страшен в этом плане на мой взгляд). Для него легко поставить собственный дизайн. К нему легко прикрутить собственные другие наработки, например, портал, каталог статей и прочее.

Большим плюсом номер два я считаю невозможность взлома собственного движка. Ведь для взлома уникального, нигде больше не установленного движка хакеру придется поломать голову. А вот для стандартных движков обычно делают даже так называемые эксплойты – скрипт, запустив который, даже безмозглый 12летний идиот может получить пароли от админки Вашего блога.

Да, это палка о двух концах – если плохо написать движок с точки зрения безопасности, ломаться он будет довольно легко. Но я лично не собираюсь писать его плохо  А Вы?

Еще плюсы? Например, скорость работы. Тот же огромный и «тяжелый» WordPress будет работать в разы медленнее, чем заточенный под конкретные задачи собственный движок.

Я расскажу, как написать свой движок за пару часов. А если копировать код у меня из статьи – за 15 минут. А если сразу скачать исходник – за 30 секунд 

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

Недостатки есть?

Есть, не без этого. Главным недостатком своего движка и огромным плюсом для стандартных движков я считаю плагины. То есть для собственного движка их просто нет и быть не может, пока автор движка сам их не напишет. Например, в WordPress можно за 1 минуту установить плагин для вывода последних комментариев, вывода смайликов, голосования (и вообще чего угодно), а в своем блоге придется писать это все самому. И если вывод последних комментариев и смайликов – задача не сложная, то, например, с голосованием уже посложнее.

Зачем Вам все это?

В любом случае, даже если Вы выбираете стандартный движок, я все равно рекомендую прочитать статью – особенно начинающим программистам. Эта статья фактически описание создания несложной CMS, которую можно превратить из блога во что угодно 

И еще момент. После этого небольшого цикла статей я напишу, как сделать генератор сателлитов! И по плану – там как раз пригодится этот собственный движок  Запахло деньгами?

Составляем ТЗ.

(ТЗ – Техническое Задание, документ, по которому программист пишет программу, скрипт, сайт и т.п.)

Даже в таком несложном деле лучше составить небольшое ТЗ и не отходить от него в процессе разработки. Мы просто опишем, что хотим видеть в движке, а потом сделаем ровно так, как написали. Я считаю, что это полезная практика – иначе можно «расплыться» (захотеть сделать и то и се и пятое и десятое, в итоге не сделать ничего или сделать частично и плохо).

«Нужны возможности: 2* добавлять категории, редактировать их названия, вывод категорий по алфавиту, вложенность не нужна. Добавлять, редактировать или удалять посты. В посте есть заголовок и основной текст. Визуальный редактор не нужен, разрешить HTML-теги. 3* Возможность комментировать пост, вводя имя, почту, сайт и комментарий. Регистрация пользователей не нужна. Все адреса в виде ЧПУ (человеку понятный URL, а-ля dimoning.ru/hello.html). Категории открываются по адресам /category/catname/, где catname – имя категории. Посты открываются по адресам /postname.html, где postname – адес поста. Эти адреса тоже можно редактировать. *4 Прикрутить RSS последней версии протокола. Весь блог в кодировке UTF-8. Все изменения администратор вводит через админку по адресу /vrotmnenogi-admin/. Добавить постраничный вывод постов.»

Вот такое тех-задание. Сделаем четко по нему 

Я решил разделить создание собственного движка блога на четыре части – первая – это проектирование и еще три отмечены звездочками в ТЗ [сначала хотел 3 части, но эта статья уже вышла довольно большой, а для понимания читать большую статью, я думаю, тяжеловато]. У меня еще нет готового движка (на момент написания этих строк), поэтому исходник каждый раз будет все более дополняться.

Начнем с начала.

В начале работы я обычно прикидываю, какие мне нужны таблицы в базе данных и как их будет использовать движок. Например, здесь. Я перечислю поля и тип данных в них записываемый, а также объясню – для чего то или иное поле. Для простоты мы будем использовать только int и text.

Категории:
id (int auto increment) | url (text) | title (text)

id – уникальный, автоматически увеличивающийся при добавлении записи, идентификатор категории
url – адрес категории, который будет подставляться /category/сюда/
title – название категории, которое будет выводиться в браузер

Посты:
id (int auto increment) | url (text) | title (text) | post (text) | dt (datetime)

id – уникальный, автоматически увеличивающийся при добавлении записи, идентификатор поста
url – адрес поста, который будет подставляться /сюда.html
title – заголовок поста, выводится в браузер
post – содержимое поста, выводится туда же
dt – дата и время написания поста, проставляется автоматически и изменению не подлежит

Комментарии к постам:
id (int auto increment) | post_id (int) | nick (text) | email (text) | site (text) | comment (text) | ip (text) | dt (datetime)

В ТЗ ничего не сказано про запись IP комментатора, но я считаю, что это необходимо. Может помочь отловить злого спамера или забанить по IP. В общем, если «враг» появится, лучше знать про него как можно больше.

id – уникальный, автоматически увеличивающийся при добавлении записи, идентификатор комментария
post_id – идентификатор поста, к которому написан комментарий
nick – имя комментатора (никнейм)
email – почта комментатора
site – сайт комментатора
comment – сам комментарий
ip – IP-адрес комментатора
dt – дата и время написания комментария. Так. Для протокола. 

Знатоки из Что-Где-Когда, конечно, заметили бы, что IP адрес можно хранить в виде long-числа, а я храню его в виде текста. Я считаю, что так нагляднее и вообще редко храню его в виде числа. Говорят, что по использованию памяти это лучше, не знаю, я не замерял. Но знаю, что это хуже по производительности – нужно преобразовывать число в IP и обратно. Я так не делаю, в общем.

С базой данных все. Теперь пара слов о ЧПУ.

Из ТЗ видно, что должны быть ЧПУ (человеку понятный Url). Для этого нам нужно создать .htaccess, который мог бы разбирать адреса вида /category/name/ и /post.html и передавать в скрипт значения этих полей в виде переменных. Например, пусть имя категории передается в переменной category, а имя поста в переменной post из массива $_GET.

Заметьте, нужно предусмотреть и постраничный вывод! Лучше подумать об этом сразу. Я предлагаю сделать примерно так же, как сделано в WordPress. А именно, для категорий страницы показываются по адресу /category/name/page/1/, где 1 – номер страницы. А если категория не выбрана (главная страница), то адреса для вывода страниц будут иметь вид /page/1/ – прямо от корня.

И еще нужно предусмотреть зарезервированное имя для RSS. Я предлагаю сделать простой адрес: /rss.html, почему бы и нет?

Какой же .htaccess файл нам понадобится? Я бы сделал такой:

RewriteEngine On
1 RewriteRule ^(rss).html$ rss.php [L]
2 RewriteRule ^([A-Za-z0-9_]+).html$ index.php?post=$1 [L]
3 RewriteRule ^(category)/([A-Za-z0-9_]+)/$ index.php?category=$2 [L]
4 RewriteRule ^(category)/([A-Za-z0-9_]+)/(page)/([0-9+])/$ index.php?category=$2&page=$4 [L]
5 RewriteRule ^/(page)/([0-9+])/$ index.php?page=$2 [L]

Я пронумеровал строки. В рабочей версии нумерации, конечно, нет.

Строка 1. Перекидывает с rss.html на rss.php прозрачно для пользователя. В rss.php будет генерироваться сама RSS.

Строка 2. При открытии адреса вида /some.html передает все между слешем и .html в скрипт index.php в переменной $_GET['post'];

Строка 3. При открытии категорий (адрес вида /category/имя/) передает в скрипт index.php имя категории в перменной $_GET['category'];

Строка 4 и строка 5 – аналогичное действие, только для других видов URL.

Ключ L не позволяет серверу идти дальше по списку, если нужное нам совпадение с адресом найдено.

Между прочим, здесь есть еще один большой плюс: с помощью регулярных выражений мы задали конкретные символы, которые можно использовать в адресах. Если хакер попытается ввести в адрес не буквенно-цифровой символ, то сервер прервет запрос сразу же. То есть заботиться об этом в самом движке уже не надо.

 

Теперь о создании своего движка блога. Я реализовал следующее: создание/редактирование/удаление постов, создание/редактирование/удаление категорий, написание комментариев и их редактирование через систему администрирования. Скачать то, что получилось можно по ссылке в конце поста (там пока что архив без редактирования комментариев из админки). Когда дойдем до безопасности, я залью этот движок на тестовый поддомен и можно будет посмотреть, что вышло. И опять эта статья обещает быть очень длинной. Боюсь, что придется ее разрезать еще на три части – авторизация в админке (эта статья), действия с постами и категориями, действия с комментариями и комментирование. А потом еще две части – вывод всего этого на сайт и безопасность движка. Пора писать книгу. (Если кого-то не устраивает такая «санта барбара» – пишите, буду делать статьи длиннее)

Для начала замечу, что все GET-параметры передаются через .htaccess и «откуда они берутся» вопрос не принимается  Это я объяснил в первой части.

Создание системы администрирования.

Я вообще рекомендую всегда разрабатывать движок с админки, т.к. в ней частично или полностью реализован вывод данных, то есть фактически – пользовательская часть. Тогда создание пользовательской части пойдет быстрее, на мой взгляд. В общем, с админки и начнем.

Располагается она по адресу /vrotmnenogi-admin/, как заявлено в ТЗ (см. первую часть). Я довольно быстро раскаялся в своей дурацкой шутке, т.к. писать везде такой длинный адрес не особенно удобно  . Но от ТЗ мы не будем отступать принципиально, это полезный навык. Простите мне такой бред, я честно не хотел 

Конечно, для начала нужно сделать в админке систему входа, чтобы кто попало не мог редактировать содержимое блога. Авторизация будет вполне обычная – через логин и пароль. Их md5-хеш будет сохраняться в Cookie и по нему пользователь будет проверяться каждый раз, когда что-то делает в админке.

MD5 – в данном случае применяется как однонаправленный алгоритм шифрования строк произвольной длины и получения 128-битного хеша. Другими словами, если зашифровать любое слово этим алгоритмом, то обратно расшифровать его уже нельзя. Понятно, что для проверки нужно зашифровать введенный пользователем логин/пароль и сравнить его с уже имеющимся хешем. Если они совпадают, то пользователь ввел правильный логин/пароль.

Храниться имя и пароль будут прямо в скрипте в виде переменных (файл /vrotmnenogi-admin/index.php, самый верх). Для еще большей путаницы мы будем добавлять в эту связку секретный ключ (произвольный набор символов), чтобы хакер совсем сломал голову, расшифровывая данный хеш.

Итак, как мы будем проверять вход? Для этого создан файл logintest.php, лежащий прямо в корне админки. Первой строкой в этом файле мы смотрим, не пытается ли пользователь залогиниться? Логин передается в переменной $_POST['login'] и если она не пуста, значит – пытается. Соответственно первым ветвлением IF мы проверяем, что он нам такого прислал и сравниваем с хешем строк пароля, логина и секретного ключа:

if ($_POST['login']){
    if ($_POST['login'] == $admin_login &&
        $_POST['password'] == $admin_password){
        setcookie ("admin_login", $protection_combo,
                                 time() + 60*60*24*7);
        header ("location: /vrotmnenogi-admin/");
    }
}

Если проверка пройдена, то мы устанавливаем куки, в которую и записан этот хеш. Куки ставится на неделю и работает только для каталога /vrotmnenogi-admin/ нашего движка. И потом отправляем заголовок, который переадресовывает нас на главную страницу. То есть – для пользователя не видно, что выполняется какой-то скрипт, все происходит «мгновенно».

Следующий блок в этом файле – это как раз вывод формы ввода логина и пароля. То есть, если не передано поле $_POST['login'] и данные в куки не совпадают с хешем (попытка взлома или просто нет куки – не залогинен), то выводится форма входа:

// Проверка, введен ли пароль админа
if ($_COOKIE['admin_login'] != $protection_combo){
    ?>
    <form action="./index.php" method="post">
        Логин: <input name="login"><br>
        Пароль: <input name="password" type="password"><br>
        <input type="submit" value="Зайти на огонек">
    </form>
    <?php
    exit;
}

Кстати, обратите, пожалуйста внимание на то, что после вывода формы обязательно должна быть команда exit. Иначе после формы выведется сама админка. После нажатия кнопки «Войти» все данные как раз и проверяются блоком кода, описанным чуть выше.

Теперь выход. Тут все очень просто. В файле index.php я создал кнопку, при нажатии на которую выполняется скрипт logout.php. Кнопку назвал незатейливо – «Выйти» 
<form action="./logout.php" method="post">
    <input type="submit" value="Выйти">
</form>

Никакого криминала  Ну и сам скрипт выхода. Он просто убивает куки с хешем логина, пароля и секретного ключа, из-за чего пользователь не пройдет проверку в файле logintest.php:
<?php
setcookie ("admin_login", "");
header ("location: /vrotmnenogi-admin/");
?>

Убиваем куки (передав ей пустое значение) и отправляем пользователя на главную страницу. Вот так.

С админкой на этом все. Следующая статья в этой серии будет, как я и сказал, о постах и о категориях. Удачи!

Скачать архив с движком dblog. Используйте как угодно, только название dblog остается за мной  В архиве, кстати, функционала больше, чем я здесь описал.

 

Оригинал статьи: часть1 и часть 2