В прошлой статье мы разобрали структуру фреймворка, но не его реализацию. Для того чтобы фреймворк начал функционаровать, нам потребуется его доработать. Для начала подключим файл конфигурацию. Мы обозначили файл, как config.php, но никак его не использовали.
По опыту разработки сразу оговорюсь, удобно использовать два файла конфигурации: первый для общих настроек - название сайта, адрес домена, время жизни куков, и второй для настроек подключения к БД. Это в первую очередь качается разработки на фреймворке. Так как для такой системы важно быстро развернуть ее на локальном компьютере и зачем перенести на сервер. При таком переносе, один из конфигов будет общим, а второй будет заполнен лишь единожды для каждого сервера. Поэтому определим два файла config.php и config.db.php
Структура этих файлов должна быть как можно проще. Идеальный вариант - ini файлы. Но для них нужно писать отдельные парсеры и они доступны из вне. Поэтому не будем изобретать велосипед, а сделаем простой php ассоциативный массив с парами ключ-значение.
Дла начала опишем config.db.php
<?php return array( 'host' => 'localhost', 'user' => 'root', 'password' => '', 'dbname' => 'ideal', 'pref' => 'idl_' // префикс для всех таблиц фреймворка );
Чтобы иметь в наличии лишь один файл конфигурации, в файле config.php используем уже имеющийся файл config.db.php
<?php return array( 'sitename' => 'Тестовая страница php фреймворка', 'encode' => 'utf-8', 'cookietime' => 3600, // время жизни куков администратора в секундах 'version' => '1.0.0 ', 'default_module' => 'index', 'default_controller' => 'index', 'default_action' => 'index', 'db' => include 'config.db.php', // ... );
Теперь файл конфигурации можно подключить к примеру так
class App extends Singleton{ public $config = null; function start(){ $this->config = include APP.'config.php'; Router::gi()->parse(); $controller = app::gi(Router::gi()->controller.'Controller'); $controller->__call('action'.Router::gi()->action); } }
Теперь в любом месте нашего фреймворка можно использовать необходимые настройки.
<?php // код echo app::gi()->config['sitename'];
К примеру доработаем наш роутер. В предыдущей статье, мы указали, что фреймворк не сможет работать без htaccess. Но это не совсем так, там, он прекрасно работает без него. Дело в том, что мы не написали логику класса Router. Исправим досадное упущение.
Логика будет такая: на вход роутера подается ЧПУ (Человеко понятные URL) разделенные слешами сущности Модель/Контроллер/Действие/ID, этого в большинстве случаев достаточно. Когда не заданы модуль, контроллер или все три, будет использоваться значения по умолчанию из файла конфигурации.
<?php class Router extends Singleton{ public $controller; public $action; public $id; private $reg_paths = array( '([a-z0-9+_\-]+)/([a-z0-9+_\-]+)/([0-9]+)' => 'controller/action/id', '([a-z0-9+_\-]+)/([a-z0-9+_\-]+)' => 'controller/action', '([a-z0-9+_\-]+)' => 'controller', ); function parse(){ $path = $_REQUEST['route']; $this->controller = app::gi()->config['default_controller']; $this->action = app::gi()->config['default_action']; $this->id = 0; foreach($this->reg_paths as $regxp=>$keys) { if (preg_match('#'.$regxp.'#Uuis', $path, $res)) { $keys = explode('/',$kyes); foreach ($keys as $i=>$key) { $this->$key = $res[$i+1]; } } } } }
Массив $reg_paths содержит в себе регулярные выражения, по которым разбирается строка $path. Если по какой-то из них будет найдено совпадение, то все поля заполняются по ней. Практически также работает роутер из Yii, только регулярки там заменены другими паттернами. К примеру ([a-z0-9+_\-]+) там записывается как <controller>. Это сделано лишь для удобства программиста. В конечном итоге, в недрах Yii происходит обратная замена.
Для большего удобства массив $reg_paths можно вынести в файл конфигурации, как сделано в Yii.
Последнее, о чем хотелось бы сказать, это то, что с файлом конфигурации вернее всего все же работать, как с моделью а не как с массивом. При таком методе, вам ничего не помешает переписать модель на работу с базой данных, и хранить настройки в ней, а не в файле. Но пока, доработаем наш класс Model, для того чтобы он работал с массивами.
class Model{ private $data = array(); function __construct($data = array()) { $this->data = $data; } function __get($name){ return isset($this->data[$name])?$this->data[$name]:null; } function __set($name,$value){ $this->data[$name] = $value; } }
Тогда загрузка конфигурации в App немного изменится.
class App extends Singleton{ public $config = null; function start(){ $this->config = new Model(include APP.'config.php'); Router::gi()->parse(); $controller = app::gi(Router::gi()->controller.'Controller'); $controller->__call('action'.Router::gi()->action); } }
А обращение к элементам конфига станет более интуитивным. Продемонстрируем на примере файла представления.
<!doctype html> <html lang="en"> <head> <meta charset="<?=app::gi()->config->encode?>"> <title><?=app::gi()->config->sitename?></title> </head> <body> <?=$content?> </body> </html>
На этом пока все. Все подробности, как обычно, в исходниках. Статьи по этой теме будут появляться при наличии интереса у читателей блога, и вопросах в комментариях. Всего доброго.
Комментарии
Возник вопрос... Фильтрацию данных лучше производить в роутере или создать отдельную модель?
содержание main.phpКод:
<?echo $this->renderPartial('header.php')?>
<?echo $this->content;?>
<?echo $this->renderPartial('footer.php')?>
Код:
Fatal error: Uncaught exception 'Except' with message 'Class uController no exist!' in test/ideal/classes/Singleton.php:21
Stack trace:
#0 test/ideal/classes/Singleton.php(26): Singleton::getInstance('uController')
#1 test/ideal/classes/App.php(10): Singleton::gI('uController')
#2 test/main.php(12): App->start()
#3 {main} thrown in test/ideal/classes/Singleton.php on line 21
Насколько я понимаю, функция parse() в роутере обрезает до первой буквы. И по закону подлости это единственное, что никак не понимаю в вашем замечательном фреймворке (роутинг и сложные регеспы), потому сам поправить не могу.
Пожалуйста, поправьте, и лучше с комментариями - что было не так.
Заменить в паттернах последний плюс на два плюса, например:
Код:
на'([a-z0-9+_\-]+)'
Код:
'([a-z0-9+_\-]++)'
Почему так - не понимаю, просто слямзил из роутера Коханы.
foreach ($keys as $i=>$key) {
$this->$key = $res[$i+1];
}
Что такое $this->$key ?
$keys = explode('/',$kyes);
т.е. если $kyes = 'controller/action/id'; то $keys будет равен
$keys = array('controller','action','id');
тогда такой цикл
foreach ($keys as $i=>$key) {
$this->$key = $res[$i+1];
}
будет эквивалентен трем присваиваниям
$this->controller = $res[1]; // $key == 'controller'
$this->action= $res[2]; // $key == 'action'
$this->id= $res[3]; // $key == 'id'
в php можно делать даже так
$first = 1;
$second = 'first';
$$second = 5;
echo $first;// вернет 5
2) Очень сложная работа с htaccess, когда одно правило может полностью сломать сайт. Не все могу тего редактировать
3)никакой регуляркой вы не сделаете более сложную логику когда один параметр может быть и контроллером и методом
4)такие правила из path можно вывести в админку и дать конечному пользователю самому решать как будет выглядеть его cpu маршрутеризация