Программисты - народ ленивый. Поэтому, когда дело доходит до работы, они сначала ищут в сети какой-нибудь программный продукт, который в той или иной степени удовлетворяет их потребности в решении поставленной задачи. Если программист пишет что-то на PHP, то одной из первых систем, которые он найдёт, будет PHP Nuke. Поигравшись с ним некоторое время, программист понимает, что вещь, конечно, хорошая, но слишком уж "коряво" написанная, тяжело адаптируемая к задачам, отличных от web-портала, да и перевод на русский язык сделан человеком, имевшем не более трёх очков по великому могучему.
Тем не менее, подход к хранению кода в том виде, как это реализовано в PHP Nuke и описано на большом количестве сайтов, посвящённым PHP, кажется вполне логичным, доступным и приемлемым.
Общая схема этого похода, называемого "модульным", такова: Существует некий главный скрипт, например index.php. Этот скрипт в неком параметре, например, $module, принимает имя модуля, который отдаёт браузеру какую-нибудь страницу. index.php выглядит примерно таким образом:
<? include "config.php"; if (!$module || !file_exists("modules/$module.inc.php")) { $module="default"; } include "modules/$module.inc.php"; ?>
Типичный "модуль" при таком построении сайта выглядит так:
<? if (!eregi("index.php", $PHP_SELF)) { die ("Access denied"); } $page_title="..."; // устанавливаем имя страницы include "includes/header.php"; // подключаем дизайн // некий <a href="/php">php</a> - код, который генерирует данную страницу include "includes/footer.php"; // подключаем остатки дизайна ?>
Вроде бы всё хорошо, однако есть некоторые но:
В каждом модуле нужно делать include заголовка - иначе не сможем изменить <title> и другие тэги в начале страницы.
В каждом модуле необходимо делать
if (!eregi("index.php", $PHP_SELF)) { die ("Access denied"); }
- чтобы не вызвали напрямую, миновав различную инициализацию переменных, подключение к СУБД, include общих функций и т.д. в config.php. Хотя, на самом деле, такой запрет делается тремя строчками в .htaccess:
<files *.php> deny from all </files>
В каждом модуле надо делать include нижней части страницы - по той же причине, что и п.1
При программировании модулей приходиться многократно вызывать одни и те же процедуры: генерацию меню, навигации, баннеров, голосований и т.д. Даже если все эти процедуры будут с красивыми, легкозапоминаемыми именами, с небольшим количеством параметров, будут содержать всё HTML-форматирование внутри себя, то всё равно каждый раз при написании модуля надо будет последовательно написать вызовы всех этих процедур:
Таким образом, написание нового модуля начинается с копирования текста старого модуля с последующим исправлением блока, ответственного за контент.
Однако есть способ избавить себя от этого монотонного, длительного и никому не нужного процесса - для этого нужно вспомнить о существовании ООП (объектно-ориентированного программирования) и о том, что PHP очень неплохо это самое ООП поддерживает.
Для начала создадим класс - Web-страницу, в которой опишем все функции, используемые при генерации наших страниц:
class WebPage { // если имя функции совпадает с именем класса, то она считается конструктором // говоря по-русски, она выполняется при создании объекта function WebPage() { $this->page_title="демо-модуль"; } function PageHeader() { include "includes/header.inc.php"; // в этом файле вместо <?=$page_title;?> надо будет написать <?=$this->page_title;?> } function PageFooter() { include "includes/footer.inc.php"; } function PageNavigation() { // Код для навигации } function PageLeftMenu() { // Код для меню в левой части страницы } function PageContent() { // Код, генерирующий контент } function PageRelatedLinks() { // Код, генерирующий ссылки на связанные разделы } function PageNewsLinks() { // Код, генерирующий блок новостей } function PageVotes() { // Код, генерирующий блок голосований } function PageBanner() { // Код, генерирующий баннер } function Run() // ф-я Run последовательно вызывает все необходимые // методы класса WebPage для построения страницы { $this->PageNavigation(); $this->PageLeftMenu(); $this->PageContent(); $this->PageRelatedLinks(); $this->PageNewsLinks(); $this->PageVotes(); $this->PageBanner(); } } ?>
Теперь, если нам надо сделать, например, страницу, которая отличается от стандартной только блоком контента, то мы можем определить новый класс, производный от WebPage:
class CoolPage extends WebPage { // переопределяем конструктор, чтобы изменить имя модуля function CoolPage() { // вызываем конструктор родительского класса - вдруг он что-то полезное делает? ;) parent::WebPage(); $this->page_title="Крутой модуль"; } function PageContent() { // выводим контент } }
если нам на странице не нужны новости, то мы определяем другой класс:
class CoolPageWithoutNews extends CoolPage { // здесь мы не описываем функцию CoolPageWithoutNews // в этом случае PHP автоматически вызовет конструктор родительского класса // соответственно имя нашей страницы будет "крутой модуль" function PageNewsLinks() { // тут пусто, чтобы ссылки на новости не выводились } }
и так далее.
В принципе, если у вас появляется большое количество классов, то код каждого из ни их тоже можно разместить в отдельном файле, однако следует следить за тем, чтобы классы, у которых есть производные классы, были доступны всегда, иначе PHP не сможет понять, из какого файла брать код родительского класса.
Вот пример файла index.php, который может выступать в качестве "главного файла":
<? include "config.php"; include "base_classes.php"; if ($module && file_exists($file="modules/$module.inc.php")) { // проверяем, есть ли файл с "телом" класса include $file; } if (!class_exists($module)) { // проверяем, что класс существует $module="WebPage"; } $page=new $module; // создаём объект $page->Run(); // запускаем генерацию страницы } ?>
Примечание: не смотря на то, что весь приведённый выше код ни разу не запускался, вышеуказанная технология, с той разницей, что почти вся информация находится в БД, реально эксплуатируется, например, на моём сайте - dmitry.rsl.ru