В прошлой статье мы разобрали структуру фреймворка, но не его реализацию. Для того чтобы фреймворк начал функционаровать, нам потребуется его доработать. Для начала подключим файл конфигурацию. Мы обозначили файл, как 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>

На этом пока все. Все подробности, как обычно, в исходниках. Статьи по этой теме будут появляться при наличии интереса у читателей блога, и вопросах в комментариях.  Всего доброго.

Рассказать друзьям

Добавить комментарий


Защитный код
Обновить

Комментарии   

+2
Михаил
# Михаил 08.12.2014 22:33
Да я эту статью ПОЛГОДАЖДАЛ. Автор тебе огромный, нет, ОГРОМНЕЙШИЙ респект в карму. Я жду продолжения!!!! Мир!
+1
Макс
# Макс 16.01.2015 18:16
Очень хорошая статья. Явно писал профессиональный программист пхп. Примеры mvc с других сайтов - детский сад, по сравнению с этой статьей.
Возник вопрос... Фильтрацию данных лучше производить в роутере или создать отдельную модель?
+1
Leroy
# Leroy 18.01.2015 15:19
Роутер просто управляет процессом. Если говорить о защите, то лучше чтобы этим занимался отдельный компанент фреймворка. Если говорить о данных которые сохраняются в модели, то разумеется модель сама должна уметь отфильтровывать не нужные данные. Пример Yii, там при сохранении в модель можно подать любые данные. Но сохранятся только те которые описаны в массивах валидации.
0
Макс
# Макс 16.01.2015 18:35
В классе Router опечатка... В explode $kyes вместо $keys
0
ВасилийВасильевич
# ВасилийВасильевич 03.02.2015 15:03
Что делает метод render, класса Controller?
0
Valeriy Chupurnov
# Valeriy Chupurnov 03.02.2015 15:07
Выводит шаблон с вставленными данными в браузер
0
Макс
# Макс 04.02.2015 20:47
Если я хочу подключить footer и header как мне воспользоваться этим методом? Или просто инклюдить их в main.php (который в каталоге views)?
0
Valeriy Chupurnov
# Valeriy Chupurnov 05.02.2015 08:04
не просто инклудить а использовать метод renderPartial, $this в шаблоне это и есть сам контроллер
содержание main.phpКод:<?echo $this->renderPartial('header.php')?>
<?echo $this->content;?>
<?echo $this->renderPartial('footer.php')?>
0
Макс
# Макс 05.02.2015 19:46
Спасибо.
0
UlvHare
# UlvHare 06.02.2015 18:38
При попытке перейти на test/user идёт ругань:
Код: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() в роутере обрезает до первой буквы. И по закону подлости это единственное, что никак не понимаю в вашем замечательном фреймворке (роутинг и сложные регеспы), потому сам поправить не могу.
Пожалуйста, поправьте, и лучше с комментариями - что было не так.
0
UlvHare
# UlvHare 10.02.2015 13:39
Методом тыка нашёл:
Заменить в паттернах последний плюс на два плюса, например:
Код:
'([a-z0-9+_\-]+)'
на
Код:
'([a-z0-9+_\-]++)'

Почему так - не понимаю, просто слямзил из роутера Коханы.
0
Leroy
# Leroy 10.02.2015 13:41
сегодня выйдет третья часть, про роутер
0
Leroy
# Leroy 11.02.2015 10:56
Новая статья про роутер и шаблонизатор http://xdan.ru/kak-napisat-svoj-frejmvork-na-php-chast-3.html
0
Игорь
# Игорь 22.04.2015 17:34
В классе роутинга не совсем понял ,что происходит в последнем цыкле
foreach ($keys as $i=>$key) {
$this->$key = $res[$i+1];
}
Что такое $this->$key ?
0
Leroy
# Leroy 23.04.2015 09:35
смотрим чуть выше видим что
$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
0
Dzyanis
# Dzyanis 18.11.2015 19:18
Привет! Прошу прощение за свою "чайниковость", но хотел бы всё же узнать в чем преимущества парсинга "path" с помощи пхп перед разбором параметров в .htaccess и последующей передачей их в скрипт?
0
Leroy
# Leroy 20.11.2015 07:51
1) Не у всех apache и htaccess.
2) Очень сложная работа с htaccess, когда одно правило может полностью сломать сайт. Не все могу тего редактировать
3)никакой регуляркой вы не сделаете более сложную логику когда один параметр может быть и контроллером и методом
4)такие правила из path можно вывести в админку и дать конечному пользователю самому решать как будет выглядеть его cpu маршрутеризация