Движок для сайта своими руками

Автор статьи: Сергей Василенко
Сайт Автора: mysitez.km.ua
E-mail Автора: serg_w@svitonline.com
Дата публикации: 29.01.2006

Движок для сайта своими руками. Вариант первый: если у вас нету базы...

Каждый сайтостроитель в один прекрасный момент вдруг замечает, что он уже занимается не столько подготовкой новых метериалов для своего сайта, сколько банальными и рутинными вещами: там выковырял меню, заменил; там пересохранил, обновил; тут — copy, там — paste, а потом всё это save и upload. «Ну нет — думает вебмастер — так больше нельзя! Но что же делать?». А делать нужно движок для сайта...

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

Движком принято называть набор скриптов и программ, на основе которых держится, живёт и обновляется сайт. Движок может быть как простым PHP-скриптом и статьями, хранящимися в текстовых файлах определённого формата, так и сложным комплексом программных средств в связке с базами данных (MySQL, Oracle, etc.) и веб-службами, написанными на Java.

Лучшим (но при этом не самым сложным) был бы вариант с применением баз данных. Но чаще всего вебмастерам база данных недоступна, поскольку дают её (насколько мне известно) только на платных хостингах. Поэтому наш первый движок мы организуем при помощи PHP и набора файлов. При этом можно себя утешить тем, что на работоспособность нашего сайта не будут влиять дополнительные факторы риска, вносящиеся использованием баз данных (все, я полагаю, уже слышали о истории с дырой в Microsoft SQL Server 2000) (1).

Наш движок будет специалиально приспособленным под контент-проекты (то есть сайты, которые регулярно пополняются авторскими статьями или другими материалами). А это значит, что нам придётся сделать всё для удобного и быстрого обновления содержания сайта.

Итак, для начала нам надо опредилить пару функций для чтения данных из файла. Перед тем, как приводить исходные коды, рассмотрим имеющиеся у нас (вернее, в PHP) средства для работы с файлами (а те, кто не в курсе — сразу и узнают).

Функции чтения файлов в PHP.

Вобщем, прочитать файл мы можем несколькими способами. Первый, и самый простой — это использовать функцию file(). Она, получив имя файла, сразу же возвращает массив строк этого самого файла:

...
$strings = file("myfolder/myfile.txt");
$first_string = $strings[0];
...

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

function loadTemplate($path)
/* функция загружает шаблон по указанному пути,
и возвращает его в виде строки, например,
для обработки функцией parceTemplate() */
{
$template = fopen($path, "r") or print("Не удалось загрузить файл шаблона [".$path."]");

if ($template)
return fread($template, filesize($path));
else
return false;

fclose($template);
}

function parceTemplate($template, $params_values)
/* функция синтаксически разбирает указанный шаблон,
заменяя названия параметров, служащих индексами
масива $params_values на их значения */
{
while (list($param, $value) = each($params_values))
{
$template = str_replace("¤{".$param."}¤", $value, $template);
}

return $template;
}


function readArticle($path)
/* функция читает указанный файл и возвращает
его содержимое в виде массива частей статьи,
разделенных конструкцией ¤{имя части}¤ */
{
$article = fopen($path, "r") or print("Не удалось загрузить файл статьи [".$path."]");

if ($article)
{
$astring = fread($article, filesize($path));
$result = split("[¤]{1}[{]{1}([ a-z_-]*)[}]{1}[¤]{1}", $astring);
$result[2] = eregi_replace("[ ,]*([ — ]{1})[, ]*", " — ", $result[2]);
$result[3] = eregi_replace("[ ,]*([ — ]{1})[, ]*", " — ", $result[3]);
$result[0] = basename($path);
return $result;
}
else
{
return false;
}
}

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

Писать много раз об одном и том же нет смысла, поэтому я процитирую одну из статей о регулярных выражениях (Источник: http://sitemaker.ru/):

Регулярные выражения.

Немного истории.

Математик Стивен Клин впервые представил регулярные выражения в 1956, в результате его работы с рекурсивными наборами в естественном языке. Они были созданы как синтаксические наборы, использовавшиеся для нахождения соотвествий шаблонов в строках, которые позже помогали обращаться к появляющейся технологической информации, облегчая автоматизацию.

С тех пор, регулярные выражения прошли через множество итераций, и текущий стандарт сохраняется ISO (Международной организацией по стандартизации) и определен Open Group, совместным усилием различных технических некоммерческих организаций (2).

Соответствие символов.

Трудность регулярных выражений состоит в том, что Вы хотите искать или чему это должно соответствовать. Без этой концепции, RE бесполезны. Каждое выражение будет содержать некоторую команду о том, что искать:

Соответствие символов в регулярных выражениях
Оператор Описание Пример Результат
. Соответствует любому одному символу .ord Будет соответствовать «ford», «lord», «2ord», и т.д. в файле sample.txt.
[] Соответствует любому одному символу, заключенному в квадратные скобки [cng]ord Будет соответствовать только «cord», «nord» и «gord»
[^] Соответствует любому одному символу, не заключенному в квадратные скобки [^cn]ord Будет соответствовать «lord», «2ord» и т.д., но не «cord» или «nord»
[a-zA-Z] Соответствует любой букве [a-zA-Z]ord Будет соответствовать «aord», «bord», «Aord», «Bord» и т.д.
[^0-9] Соответствует любой нецифре в промежутке 0-9 [^0-9]ord Будет соответствовать «Aord», «aord» и т.д., но не «2ord» и т.д.

Операторы повторения.

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

Операторы повторения в регулярных выражениях
Оператор Описание Пример Результат
? Соответствует определенному символу единожды, если тот существует ?erd Будет соответствовать «berd», «herd», и т.д. и «erd»
* Соответствует определенному символу многократно, если тот существует n.*rd Будет соответствовать «nerd», «nrd», «neard» и т.д.
+ Соответствует определенному символу один или более раз [n]+erd Будет соответствовать «nerd», «nnerd» и т.д., но не «erd»
{n} Соответствует определенному символу точно n раз [a-z]{2}erd Будет соответствовать «cherd», «blerd» и т.д., но не «nerd», «erd», «buzzerd» и т.д.
{n,} Соответствует определенному символу минимум n раз .{2,}erd Будет соответствовать «cherd» и «buzzerd», но не «nerd»
{n,N} Соответствует определенному символу минимум n раз, но не более чем N раз n[e]{1,2}rd Будет соответствовать «nerd» и «neerd»

Якоря.

Якоря описывают где соответствовать шаблону. Они могут быть удобными, когда Вы ищете общие строковые комбинации.

Якоря регулярных выражений
Оператор Описание Пример Результат
^ Соответствует началу строки ereg_replace("^/", "blah") Вставляет «blah» в начало строки
$ Соответствует концу строки ereg_replace("$/", "blah") Вставляет «blah» в конец строки
< Соответствует началу слова ereg_replace("<", "blah") Вставляет «blah» в начало слова
    <BLAH< td> Соответствует «blahfield» и т.д.
> Соответствует концу слова ereg_replace(">", "blah") Вставляет «blah» в конец слова
    >blah Соответствует «soupblah» и т.д.
b Соответствует началу или концу слова bblah Соответствует «blahcake» и «countblah»
B Соответствует середине слова Bblah Соответствует «sublahper» и т.д.

(конец цитаты, источник описания: http://sitemaker.ru/)

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

¤{имя части}¤
содержание части
...
...
...
¤{ещё одно имя части}¤
её содержание и так далее...

Символы ¤{ и }¤ используется для отделения частей друг от друга. Имя части же никакого значения не имеет и может быть любым набором символов английского алфавита, пробела, подчеркивания или дефиса.

Для вывода списка статей используется цикл, перебирающий все файлы из нужного каталога. Если он натыкается на файл *.art, то сразу на радостях добавляет его в масив. В зависимости от указанного параметра, он может либо добавить имя этого файла, либо название содержащейся в нём статьи, либо сразу готовую ссылку на эту статью.

Что ж, небольшая часть работы над нашим движком уже проделана. Эта часть кода — основа нашего первого движка. Для конкретных целей к нему нужно цеплять дополнительные функции и создавать сами тексты и шаблоны страниц.


Движок для сайта своими руками. Часть вторая.

В прошлый раз мы рассмотрели способ организации «базы данных» без собственно самой базы данных. Сегодня продолжим тему создания «без-MySQL’ного» сайтового движка разговором об каталогах, файлах и include’ах. Также будет немного теории и практики о собственно работе такого двигателя.

Основные принципы организации работы.

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

Собственно, одной из целей создания движка для сайта есть как раз организация удобной работы по обновлению материалов, и, как предусловие, практически полное отделение дизайна сайта от собственно его полезного содержания (во загнул ). Но в любом случае, будущий дизайн надо учитивать, каким образом — об этом немного позже.

Итак, само слово «разделение» подразумевает уже, как минимум, разделение страницы сайта на два файла — с шаблоном дизайна (который может быть общим для нескольких страниц) и файла с самим контентом, то есть информацией.

Кроме этих двух файлов нам понадобиться ещё один, включаемый во все динамические страницы (имеются ввиду страницы, содержащие PHP-код). В этом файле мы будем хранить все общие функции движка (собственно, их можно назвать «ядром»), а также определим некоторые полезные глобальные константы.

Основной задачей функций ядра будет чтение файлов с текстами статей, картинками или иными материлами сайта, а также вывод этого контента в нужной форме на экран. Третью функцию — ввод данных — мы не рассматриваем, так как способ хранения данных (файлы с разделителями) позволяет вводить информацию при помощи стандартных средств (любимого текстового редактора, например).

Таким образом, схема создания новых материалов сайта выглядит следующим образом:

Рис. 1. Текст → движок → страница сайта

А под фразой «учитывать дизайн», высказанной немного выше, имелось ввиду создание системы шаблонов, или, проще говоря, набора оформлений разных страниц (HTML-файлов, по сути), где места под изменяемое содержание (заголовки, меню, тексты — всё, что генерируется динамически) оставлены пустыми. Подставлятся они будут «на лету» при обращении пользователя к определенной странице. Получается даже дополнительный выигрыш — кроме всего прочего, уменьшаеться объём хранимых на сервере файлов, так как оформление страниц не повторяется в каждом файле, а хранится в одном месте. Про удобство при возможном желании изменения дизайна, я думаю, и говорить не надо.

Расположение файлов.

Итак, вернемся к собственно организации нашей системы. Основной принцип, который будет использоваться в нашем примере — это одноуровневость разделов. Но не волнуйтесь — это лишь для упрощения примеров. Если для вас это слишком серъёзное ограничение — просто придется подождать следующего выпуска, в котором мы поищем обходные пути.

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

Рис. 2. Структура каталогов

Итак, у нас имеются каталоги, каждый из которых является разделом сайта (естественно, кроме служебных каталогов, таких, как “images”).

Это значит, что в каждом таком каталоге должен лежать так называемый «индексный файл» — страница, которая загружается по умолчанию при таком обращении к разделу: http://site.com/Razdel. Имя этого файла (или возможные имена) вам нужно узнать у вашего хостера. Чаще всего это такие имена, как “index.html”, “index.php” и т. п. — расширение зависит от используемого серверного языка.

Значит, с именами файлов разобрались. Но что же нам положить в эти файлы? Вот теперь-то мы и переходим собственно к основной части сегодняшнего разговора.

В самом начале файла стоит вставить код включения ядра движка. Подобное обращение на языке PHP выглядит следующим образом:

// инициализация ядра
include("bin/core.php");

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

В этом же файле стоит описать ещё некоторые полезные функции. Например, функция непосредственного получения какого-либо файла в виде строки (может пригодится):

function getInclude($path)
{
return str_replace("n", "", (implode(file($path), "")));
}

Новостная система.

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

Суть её работы сводится к следуещему. Имеется текстовый файл с новостями, разделенными символом перевода строки (словом, каждая новость — в новой строке). Каждая строка разделена символом вертикальной черты («|») на два поля: дату и, собственно, саму новость.

Опредилив функцию новостной системы в нашем включамом файле («ядре»), мы получаем возможность на любой странице получить нужное количество последних новостей. Первым параметром передаётся часть пути, указывающая на размещение файла с новостями. Количество выводимых новостей, как вы уже догадались, задаётся вторым, необязательным, параметром.

Вот моя реализация функции новостной системы:

function getNews($path="", $lim=3)
{
$news = file($path."news.txt");
$result = "";

if ($lim == 0)
{
$lim = count($news);
}

for ($i=0; $i<$lim && $i<COUNT($NEWS); $i++)
{
$newslist = loadTemplate("templates/_news.tpl");

$newslist = eregi_replace("¤date¤",
strtok($news[$i], "|"), $newslist);
$newslist = eregi_replace("¤text¤",
strtok("|"), $newslist);

$result = $result.$newslist;
}

return $result;
}
// end of getNews();

Что ж, на сегодня пока что всё. Продолжение следует...