Вся логика фреймворка, его архитектурные ограничения и возможности мы опишем в файле app/App.php. Этот файл очень важен, так как именно он отвечает за инициализацию всего приложения и доступ к его частям, но чтобы вам было проще понять как он выполняет свои функции, рассмотрим его составные части последовательно.
Для начала рассмотрим структуру класса инициализации:
<?php
// Файл app/App.php
require('vendor/autoload.php');
use Bricks\Autoload\Loader;
use Bricks\ServiceLocator\Manager;
use Bricks\Http\Routing\Router;
use Bricks\Http\Routing\Request;
use Bricks\Http\Routing\Response;
class App{
public function run(){
...
}
}
В классе подключается автозагрузчик зависимостей, создаваемый Composer, а затем объявляются все используемые в нем классы пакетов Bricks. Структура самого класса App довольно проста. Он не относится к какому либо пространству имен (namespace), так как это единственный модуль в приложении. В нем реализуется единственный метод run, который и вызывается из файла public/index.php и инициализирует приложение в целом.
Рассмотрим реализацию метода run. В первую очередь в нем необходимо подключить и настроить автозагрузчик, так как он используется всеми компонентами приложения:
public function run(){
$loader = new Loader;
$loader->pref('conf', 'app/conf'); // Конфигурации ищем в app/conf
$loader->pref('model', 'app/model'); // Бизнес-логику в app/model
$loader->pref('controller', 'app/controller'); // а контроллеры в app/controller
...
}
Думаю здесь комментарии излишни.
Далее необходимо заполнить инициализировать локатор служб. Этот механизм позволит всем частям приложения получить доступ к доступным компонентам фреймворка:
public function run(){
// Автозагрузчик
$locator = new Manager;
$router = new Router; // Создадим роутер предварительно, чтобы его можно было добавить в локатор служб.
$locator->set('loader', $loader); // В локатор добавляется автозагрузчик
$locator->set('router', $router); // роутер
$locator->set('conf', array_merge($loader->load('conf\global.conf'), $loader->load('conf\local.conf'))); // и конфигурации приложения.
...
}
Теперь самое интересное - настройка роутинга:
public function run(){
// Автозагрузчик
// Локатор служб
// Обработка запросов вида http://site.com/controller/action
$router->all('~^/([A-Za-z0-9_]+)/([A-Za-z0-9_]+)~',
function(Request $request, Response $response, array $match) use($locator){
list($subject, $action) = $match; // Определяем целевой контроллер и метод.
$controller = 'controller\\' . ucfirst($subject); // Так как мы определили в автозагрузчике местонахождение всех контроллеров, его будет легко подключить.
$controller = new $controller; // Создаем экземпляр контроллера
$method = $action . 'Action'; // и определяем имя целевого метода как "имяAction"
return $controller->$method($request, $response, $match); // Вызываем метод контроллера возвращая результат его работы.
}
);
...
}
Этот маршрут подходит для запросов на адрес вида: http://site.com/controller/action - но этого недостаточно, необходимо так же определить маршрут для запросов на адрес без URI: http://site.com - что мы и сделаем:
public function run(){
// Автозагрузчик
// Локатор служб
// Маршрут A
// Все запросы без URI передаем в контроллер controller\Index::indexAction
$router->all('~^/?$~',
function(Request $request, Response $response, array $match) use($locator){
$controller = new controller\Index;
return $controller->indexAction($request, $response, $match);
}
);
...
}
Осталось только выполнить маршрутизацию:
public function run(){
// Автозагрузчик
// Локатор служб
// Маршрут A
// Маршрут B
$request = new Request;
$response = new Response;
$response->header('Content-Type', 'text/html;charset=utf-8'); // Определяем тип ответа как HTML-документ в кодировке UTF-8.
$content = $router->run($request, $response); // Выполняем маршрутизацию и получаем ответ контроллера.
include($loader->path('app/layout', 'html')); // Загружаем используемый нами шаблон страницы. Так как ответ контроллера записан в переменную $content, он будет вставлен в тело шаблона автоматически.
$response->send(); // Отправляем ответ клиенту.
}
Полный листинг класса App приведен здесь:
App (+/-)
Архив с примером.