У интерпретируемых языков есть одна общая проблема, нет готовой жесткой системы организации кода. Что я имею ввиду:
- Каждый класс, контроллер, вьювер в отдельном файле. (на практике люди умудряются в один файл все приложение запихать на 5000 строк)
- На один файл – одна точка входа. (люди же обычно обработку запросов строют через условия if-else, либо switch-case)
- Хорошая система вывода сгенерированных на сервере данных. (передал классу данные, готовые к выводу и указал формат, вот идеальный вариант)
Использовать готовый фреймворк для решения небольшой задачи, это равносильно подключение jQuery для выборки по id. Что есть небольшая задача для меня: промо-сайт, api сервис, сайт с логикой на клиенте (js RIA), где PHP как прослойка между базой и приложением на JS.
Типичный файл index.php которые я встречал при работе с чужим кодом. Максимум логику одного кейса вынесут либо в отдельный файл через include, либо оформят в статический класс.
$action = @$_REQUEST['action'];
switch($action){
case "list":
$filter = @$_REQUEST['filter'];
//...
echo json_encode($result);
break;
case "get":
$id = @$_REQUEST['id'];
//...
echo $result;
break;
case "add":
$name = @$_REQUEST['name'];
//...
echo "ok";
break;
default:
//404
break;
}
Предлагаю элегантное решение проблемы в виде роутера запросов.
$route = @$_REQUEST['r']; $format = @$_REQUEST['f']; unset ($_REQUEST['r']); unset ($_REQUEST['f']); $view = new View(); $api = new Api($view); $api->call($route, $_REQUEST, $format);
Класс View занимается только форматом вывода, т.е. представлением. Одни и те же данные мы можем вывести как JSON, HTML, Шаблон Smarty или еще как-то.
Вот примерная реализация на все случаи жизни.
class View implements IView
{
public $modes = array(
'html' => 'html',
'text' => 'html',
'tpl' => 'tpl',
'js' => 'js',
'json' => 'json',
'result' => 'result',
'debug' => 'debug'
);
public function html($result)
{
Header("content-type: text/html; charset=utf-8");
echo $result;
}
public function tpl($result)
{
require('Smarty.class.php');
$smarty = new Smarty();
$smarty->setTemplateDir('smarty/templates');
$smarty->setCompileDir('smarty/templates_c');
$smarty->setCacheDir('smarty/cache');
$smarty->setConfigDir('smarty/configs');
$smarty->assign('item', $result['data']);
Header("content-type: text/html; charset=utf-8");
echo $smarty->fetch($result['tpl']);
}
public function debug($result)
{
Header("content-type: text/html; charset=utf-8");
print_r($result);
}
public function json($result)
{
Header("content-type: application/json; charset=utf-8");
return json_encode($result);
}
public function js($result)
{
Header("content-type: application/javascript; charset=utf-8");
echo $result;
}
public function result($result)
{
$json = $result === true ? array('result' => true) : array('result' => false);
return $this->json($json);
}
public function output($result, $mode, $callback = null)
{
if (isset($callback) && ($mode != 'json' || $mode != 'result')) {
$mode = 'json';
}
$raw = '';
if (array_key_exists($mode, $this->modes)) {
$methodName = $this->modes[$mode];
$raw = $this->$methodName($result, null);
}
if (isset($callback)){
echo $callback . '(' . $raw . ')';
}else if (isset($raw)){
echo $raw;
}
}
}
Класс Api наследует App класс, в котором описана все логика. Сам же Api описывает только кейсы.
class Api extends App
{
public $export = array(
'list' => 'getList',
'add' => 'addItem',
'get' => 'getItem'
);
public function getList()
{
$filter = $this->arg('filter', '')
$items = array();
return $items;
}
public function addItem()
{
$name = $this->arg('name', 'default name')
return true;
}
public function getItem()
{
$id = $this->arg('id', null)
return array(
'name' => 'Name',
'url' => 'URL'
);
}
}
Класс App реализует всю внутреннюю логику работы с таблицей доступа
abstract class App
{
public $view;
public $args;
public $export = array();
public function __construct($view = null)
{
if (isset($view)) {
$this->view = $view;
if (!($this->view instanceof IView)) {
throw new Exception('View must implement IView interface');
}
}
}
public function arg($name, $default)
{
return (isset($this->args[$name]) && !empty($this->args[$name]) ? urldecode($this->args[$name]) : $default);
}
public function call($route, $args, $format = null)
{
if (!isset($route)) throw new Exception('Route is not exists');
$this->args = $args;
$callback = $this->arg('jsoncallback', null);
if (array_key_exists($route, $this->export)) {
$methodName = $this->export[$route];
$result = $this->$methodName();
if (!isset($this->view)) {
return $result;
} else {
if (!isset($format)) throw new Exception('Format is not exists');
$this->view->output($result, $format, $callback);
}
}
}
}
Api можно использовать и без представления, в случае, если у нас api используется не только как сервис.
$api = new Api();
$api->call('list', array('filter' = > '100'));
Примеры и сама реализация лежат на github
No comments:
Post a Comment