У интерпретируемых языков есть одна общая проблема, нет готовой жесткой системы организации кода. Что я имею ввиду:
- Каждый класс, контроллер, вьювер в отдельном файле. (на практике люди умудряются в один файл все приложение запихать на 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