Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > PHP: Для профи > Модульность. Обсуждение принципов и проблем.


Автор: Wowa 4.5.2005, 18:32
Есть программа. Необходимо обеспечить подключение к ней модулей различной направленности. Состоящих не только из одного файла. Какие пути реализации вы видите?

я вижу такой путь:
1. Создается таблица
Код

CREATE TABLE modules (
modulename varchar(255) NOT NULL PRIMARY KEY,
description varchar(255) NOT NULL,
status enum('false','true'),
)


2. При запуске программы читаются все записи из таблицы modules WHERE status='true'

3. Теперь мы знаем, как называется каталог, в котором лежит модуль. Он называется также, как и modulename из таблицы.

4. Загружаем файл:
include("modules/extra/$modulename/conf.php");

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


Подключение нового модуля к системе
- Загружаем все файлы модуля в каталог: modules/extra/имя_модуля
- Делаем запись в таблицу modules о новом модуле.
- Подключаем файл install.php
Этот файл содержит две функции:
1. install_module
2. uninstall_module
- Запускаем функцию install_module, которая создает нужную таблицу, нужные поля в таблица и т.п., а также делает все, что нужно для установки модуля.
- По окончании своей работы, если модуль подключен успешно, то она в таблице modules ставит status=true для себя.

Автор: Wowa 4.5.2005, 18:57
Нужно очень хорошо продуманная система модульности.

Автор: Opik 4.5.2005, 19:01
А инфу из файла в таблицу зенести нельзя?

Автор: Wowa 4.5.2005, 19:01
Остается тонкий момент... Что делать, когда один подключаемый модуль должен как-то влиять на другой уже подключенный модуль? Например, добавить ему некоторую функциональность.
имхо это можно реализовать только двумя способами:
1. Подключенный модуль заменит своими некоторые функции прежнего модуля.
2. Если прежний модуль знает о возможности расширения функциональности за счет подключения внешнего модуля, то в нем должна быть организована стыковка с модулем.
Например, в тех местах, где требуется выполнить какое-то действие, проверяется по таблице modules наличие такого модуля, и если он там есть, то вызывается с него определенная функция.

Автор: Opik 4.5.2005, 19:02
Думаю вызывать через:
Module::method;

Автор: Wowa 4.5.2005, 19:03
Цитата(Opr @ 4.5.2005, 17:01)
А инфу из файла в таблицу зенести нельзя?

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

Автор: Farik 4.5.2005, 19:09
всё равно не совсем понятно, зачем разделять инициализацию модуля на два этапа - чтение из базы и чтение из файла
почему бы не хранить настройки в базе?
соответсвенни и взаимодействие модулей тоже можно в таком случае сохранять в базе и вызывать не конфигурационный файл, а конфигурационные опции....

Автор: Wowa 4.5.2005, 19:12
Цитата(Farik @ 4.5.2005, 17:09)
всё равно не совсем понятно, зачем разделять инициализацию модуля на два этапа - чтение из базы и чтение из файла
почему бы не хранить настройки в базе?

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

Автор: AntonioBanderaz 4.5.2005, 23:20
А автозпгрузкой не пробывал?

Код

<?php
   //Фкнуция автозагрузки
    function  __autoload($class) {
         include("module_".ucfirst($class).".php");
    }
    
    $u = new User; 
?>


Он сам будет искать в ТЕКУЩЕМ каталоге, все файлы типа module_траля-ля.php и смотреть нет ли в них класса User, если таковой не определён!!!

Автор: Wowa 5.5.2005, 00:20
AntonioBanderaz но тогда, чтобы отключить какой-то модуль, получается мне его надо удалять. А это плохо.

Автор: Opik 5.5.2005, 01:33
Wowa
Это почему удалять? он же подключается только при вызове.

Автор: Wowa 5.5.2005, 02:08
Цитата(Opr @ 4.5.2005, 23:33)
Это почему удалять? он же подключается только при вызове.

а если мне его не надо подключать? Есди этот модуль мне надо отключить?

Автор: AntonioBanderaz 5.5.2005, 05:00
Wowa - логично будет его не вызывать, тогда он и будет отключён....

Автор: AntonioBanderaz 5.5.2005, 05:30
Изменить функцию на проверку из какой-нидь хни и имени модуля, если он там есть - то не подключать, если нет, то подключать....
Т.е.
Код

<?php    
   //Функция автозагрузки    
    function  __autoload($class) {    
         if(searchModule($class))
             include("module_".ucfirst($class).".php"); //инклюднуть заразу
         else 
            // типо вывод, такой модуль отключён...    
    }    
    function searchModule($mod) {
         $conf = // читаешь запрещённые модули или наоборот, и распологай это где хош, хоть в базе, хоть в файле
         if($conf != str_replace($mod,'',$conf) return false; //наш модуль в списке запрещённых
         return true; // Его там не оказалось
    }
     
    $u = new User;    
?>


Вот Тебе и отключение....

Автор: IZ@TOP 5.5.2005, 09:26
Я бы делал немного по другому. Представим что у нас есть таблица скилета системы работающей с модулями и т.п. Вот ее SQL структура:

Код

create table site_tree (
    id int not null auto_increment,
    parent int not null default '0',
    modid int not null default '0',
    name varchar(30) not null,
    title varchar(150) not null,
    content text,
    alias varchar(20),
    visible bool,
    created timestamp,
    updated timestamp,
    primary key (id)
);


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

Код

create table site_modules (
    id int not null auto_increment,
    name varchar(20) not null,
    source varchar(50) not null,
    type enum('class', 'function'),
    initialize varchar(50),
    visible bool,
    accesslevel tinyint,
    primary key(id)
);


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

Представлю на конкретном примере как все это должно работать:

Представим что пользователь запршивает страничку с идентификатором 387, к которой привязан модуль с ID 8, этот модуль отвечает за вывод новостей.

Вот примерный план действий:

Код

<?php

// ... загрзили данные о страничке. Далее проерка некоторых параметров и загрзка модуля
// В siteTree находится инфа выбранная из таблицы site_tree

if($siteTree['modid'] != 0) {
    if(!$db->sendQuery('select name, source, type, initialize from site_modules where accesslevel <= ${0 i} and visible = 1 and id = ${1 i}',
        $siteTree['modid'])) {
        // Если модуль не найден, выводим сообщение об ошибке и завершаем работу
        die("Can't load module");
    }
    
    $Module    = $db->nextRow();
    
    // Проверяем сначало нужна ли инициализация модуля, затем его тип и в соответствии
    // с полученными данными выполняем необходимые действия
    
    if(!empty($Module['initialize'])) {
        
        switch($Module['type']) {
            case 'class': {
                // Если наш модуль это класс, то проверям его загрузку в массиве 
                // инициализированных модулей
                
                if(isset($GLOBAL_MODULES[$Module['name']])) {
                    
                    // Проверяем что наш класс был инициализирован, если нет - создаем объект
                    
                    if(!is_object($GLOBAL_MODULES[$Module['name']])) {
                        $GLOBAL_MODULES[$Module['name']]    = new $Module['name'];
                    }
                    
                    // Проверяем существует ли метод в нашем объекте, если да - вызываем,
                    // если нет - выводим сообщение об ошибке
                    
                    if(method_exists($GLOBAL_MODULES[$Module['name']], $Module['initialize'])) {
                        $GLOBAL_MODULES[$Module['name']]->$Module['initialize']();
                    } else {
                        die('Object method not exists');
                    }

                } else {
                    
                    // Если наш модуль не разу не загружался, выолняем подключение файла и
                    // инициализацию объекта класса
                    
                    require_once "{$CONFIG['MODULE_DIR']}/{$Module['name']}/{$Module['source']}";
                    $GLOBAL_MODULES[$Module['name']]    = new $Module['name'];
                    
                    if(method_exists($GLOBAL_MODULES[$Module['name']], $Module['initialize'])) {
                        $GLOBAL_MODULES[$Module['name']]->$Module['initialize']();
                    } else {
                        die('Object method not exists');
                    }
                }
                
            } default: {
                
                // По умолчанию у нас вызывается функция из файла модуля
                
                if(!isset($GLOBAL_MODULES[$Module['name']])) {
                    require_once "{$CONFIG['MODULE_DIR']}/{$Module['name']}/{$Module['source']}";
                    $GLOBAL_MODULES[$Module['name']] = true;
                }
                
                if(function_exists($Module['initialize'])) {
                    $Module['initialize']();
                } else {
                    die('function not exists');
                }
            }
        }
    }
}

?>


Код не проверял, могут быть ошибки. Но логика должна быть понятна.

Автор: Opik 5.5.2005, 15:01
AntonioBanderaz
Ага, и получим ошибку, если модуль вырублен.
это тогда уж в конструкторе класса нужно проверять, включен он или нет.

Автор: AntonioBanderaz 5.5.2005, 15:14
Opr - класс-то вызывается, а не подключается.... Хм... Это я упустил........

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)