Browse Source

now right

git-svn-id: svn+ssh://code.netmonsters.ru/svn/majestic/trunk@4 4cb57b5f-5bbd-dd11-951b-001d605cbbc5
master
akulikov 16 years ago
parent
commit
bbc1bf5c21
  1. 101
      Action.class.php
  2. 58
      Cache.class.php
  3. 40
      DBConnector.class.php
  4. 55
      Decorator.class.php
  5. 64
      Env.class.php
  6. 35
      Load.class.php
  7. 57
      MJException.class.php
  8. 186
      Model.class.php
  9. 27
      PageController.class.php
  10. 106
      Router.class.php
  11. 93
      Sublimer.class.php
  12. 90
      User.class.php

101
Action.class.php

@ -0,0 +1,101 @@
<?php
/**
* Рутовый класс для любого действия.
* Описывает основной функционал для работы с классами действий.
*
*/
abstract class Action
{
public $template; //шаблон действия
public $template_dir = '.'; //путь к шаблону действия
public $class; //имя дочернего класса
protected $templater; //шаблонизатор
public function __construct()
{
$this->class = get_class($this);
$this->template = substr($this->class, 0, -strlen(ACTION_POSTFIX));
if (CACHE_ENABLE && ($cache_name = $this->getCacheKey()) !== false) {
$cache = new Cache($this->class.'_'.$cache_name, $this->getCacheTime());
if ($cache->isCached()) {
$this->restore($cache->load());
} else {
$this->init();
$cache->save($this->store());
}
} else {
$this->init();
}
}
/**
* Выдает результат действия.
*
* @return string
*/
public function display()
{
$this->templater = Load::templater(ACTION_TPL_PATH.'/'.$this->template_dir);
$this->prepare();
return $this->templater->fetch($this->template.'.tpl');
}
/**
* Инициализация данных действия.
* Тут должна быть ВСЯ работа с установкой данных,
* дабы потом они нормально закешировались.
* Этот метод НЕ исполняется при срабатывании кеша.
*
*/
abstract protected function init();
/**
* Подготовка данных для шаблона.
* Этот метод выполняется ВСЕГДА
*/
protected function prepare() {}
/**
* Возвращает имя файла для кеширования
* Переопределяется в доченрих классах.
*
* @return false/string - имя файла кеша либо false если кеш запрещен
*/
protected function getCacheKey()
{
return false;
}
/**
* Возвращает время жизни кеша в секундах
* При отрицательном значении кеш вечен.
* Переопределяется в доченрих классах.
*
* @return false/integer - время жизни кеша либо false если кеш запрещен
*/
protected function getCacheTime()
{
return 0;
}
/**
* Выдает строку для кеширования.
*/
protected function store()
{
return serialize(get_object_vars($this));
}
/**
* Разбирает строку кеша, созданную методом store
*/
protected function restore($data)
{
foreach(unserialize($data) as $key => $val) {
$this->$key = $val;
}
}
}
?>

58
Cache.class.php

@ -0,0 +1,58 @@
<?php
/**
* Класс кеша.
* Отвечает за киширование результатов выполнения действий.
*
*/
final class Cache
{
private $cache_file;
private $cache_time;
function __construct($cache_name, $cache_time)
{
$this->cache_time = (int) $cache_time;
$this->cache_file = CACHE_PATH.'/'.$cache_name;
}
/**
* Сохраняет кэш в файл
*
* @return boolean - сохранил или нет
*/
function save($data)
{
if ($this->cache_time != 0) {
return (bool) file_put_contents($this->cache_file, $data);
}
return false;
}
/**
* Достает кэш из файла
*
* @return string - содержимое кеша
*/
function load()
{
return file_get_contents($this->cache_file);
}
/**
* Проверяет, закешированы ли данные с таким именем
*
* @return boolean - закеширован или нет
*/
function isCached()
{
if (! file_exists($this->cache_file)) {
return false;
}
if ($this->cache_time > 0 && $this->cache_time + filemtime($this->cache_file) < TIME_NOW) {
return false;
}
return true;
}
}
?>

40
DBConnector.class.php

@ -0,0 +1,40 @@
<?php
/**
* Класс базы данных.
* Возвращает идентификатор соединения
*
*/
class DBConnector
{
static private $handlers = array();
static public $queries = array();
/**
* Получение соединения.
* Если соединение с такими параметрами уже есть - новое не создается.
*
* @param array $db_settings - массив настроек
* @return resource - идентификатор соединения
*/
static function getConnect($db_settings)
{
$handler_name = self::getConnectionName($db_settings);
if (isset(self::$handlers[$handler_name])) {
return self::$handlers[$handler_name];
}
if (!$handler = @mysqli_connect($db_settings['host'], $db_settings['user'], $db_settings['password'], $db_settings['database'])) {
throw new MJException('Can\'t connect to DB '.mysqli_connect_error(), 2);
}
mysqli_query($handler, "SET NAMES 'utf8'"); //cheat!!!
return self::$handlers[$handler_name] = $handler;
}
static function getConnectionName($db_settings)
{
return $db_settings['host'].'-'.$db_settings['database'];
}
}
?>

55
Decorator.class.php

@ -0,0 +1,55 @@
<?php
/**
* Родительский класс для всех декораторов. Содержит основной функционал.
*
*/
abstract class Decorator
{
protected $layout = false;
protected $action = false;
protected $action_name = 'Action';
protected $templater = false;
function __construct()
{
if (!$this->layout) {
throw new MJException('$layout not set in '.get_class($this));
}
$this->templater = Load::templater();
}
/**
* Основной метод вывода
*
*/
function display(Action $action)
{
$this->action = $action;
$this->templater->assign($this->action_name, $this->action->display(), $this->layout);
$this->exec();
$this->templater->setPath(WRAPPERS_TPL_PATH);
return $this->templater->fetch($this->layout);
}
/**
* Добавить данные на вывод
*
* @param string $var_name - имя переменной, в которую будет осуществлен вывод
* @param Action $action - действие
* @param Decorator $decorator - декоратор, в котором должно выполнится действие.
*/
function addOutput($var_name, Action $action, Decorator $decorator = null)
{
$this->templater->assign($var_name, $decorator ? $decorator->display($action) : $action->display(), $this->layout);
}
/**
* Основной метод для дочерних декораторов
*
*/
abstract function exec();
}
?>

64
Env.class.php

@ -0,0 +1,64 @@
<?php
/**
* Класс для работы с переменными окружения.
*
*/
final class Env
{
static public $params = array();
static function Get($var, $default = false)
{
return isset($_GET[$var]) ? $_GET[$var] : $default;
}
static function Post($var, $default = false)
{
return isset($_POST[$var]) ? $_POST[$var] : $default;
}
static function Server($var, $default = false)
{
return isset($_SERVER[$var]) ? $_SERVER[$var] : $default;
}
static function Session($var, $default = false)
{
return isset($_SESSION[$var]) ? $_SESSION[$var] : $default;
}
static function getCookie($var, $default = false)
{
return isset($_COOKIE[$var]) ? $_COOKIE[$var] : $default;
}
static function setCookie($var, $value, $time = 0, $path = '/')
{
return setcookie($var, $value, $time, $path);
}
static function getParam($var, $default = false)
{
return isset(self::$params[$var]) ? self::$params[$var] : $default;
}
static function setParam($var, $val)
{
self::$params[$var] = $val;
}
static function setParams($params=array())
{
self::$params = self::$params + $params;
}
static function parseTimer($time)
{
$hours = intval($time / 3600);
$minutes = intval($time/60 - $hours*60);
return sprintf('%02d:%02d:%02d', $hours, $minutes, $time - $hours*3600 - $minutes*60);
}
}
?>

35
Load.class.php

@ -0,0 +1,35 @@
<?php
class Load
{
static $models = array();
static $templater = false;
static $router = false;
static function model($model_name)
{
if (isset(self::$models[$model_name])) {
return self::$models[$model_name];
}
$class_name = $model_name.MODEL_POSTFIX;
return self::$models[$model_name] = new $class_name;
}
static function templater($path = '')
{
if (self::$templater) {
if ($path) {
self::$templater->setPath($path);
}
return self::$templater;
}
return self::$templater = new Sublimer($path);
}
static function router()
{
return self::$router ? self::$router : self::$router = new Router;
}
}
?>

57
MJException.class.php

@ -0,0 +1,57 @@
<?php
/**
* Обработчик эксепшенов
*/
class MJException extends Exception
{
private $line_range = 6;
public function terminate()
{
if (!DEBUG_ENABLE) {
return "Sorry, server temporary unavaible";
}
$return = "<b>MJ Error:</b> ";
$return .= str_replace("\n", "<br/>\n", $this->getMessage())."<br>\n";
$trace = $this->getTrace();
$file = reset($trace);
//смещение в трейсе, указаное при вызове эксепшена (2й параметр). Нужно для более инофрмативного вывода
if ($shift = abs($this->getCode())) {
while($shift--) {
$file = next($trace);
}
}
if ($fp = fopen($file['file'], 'r')) {
$error_line = $file['line'];
$start = $error_line - $this->line_range;
$end = $error_line + $this->line_range;
$i = 1;
$return .= "<pre style=\"background-color:#e4e4e4\">";
while ($line = fgets($fp, 4096) and $i<=$end) {
$line = htmlspecialchars($line);
if ($i >= $start && $i <= $end) {
if ($i == $error_line) $return .= '<div style="background-color:#cccccc">'.$i.' '.$line.'</div>';
else $return .= $i.' '.$line;
}
$i++;
}
$return .= "</pre>";
fclose($fp);
}
$return .= '<table border="1" cellpadding="2" cellspacing="0"> <caption><b>Backtrace</b></caption>';
$return .= "\n<tr><td><b>".$this->getFile().'</b></td><td><b>'.$this->getLine().'</b></td></tr>';
foreach($trace as $row) {
if (isset($row['file'])) { //throwing exception from __call method will not return file and line
$return .= "\n<tr".($file['file'] == $row['file'] ? ' style="background-color:#ffcccc"': '')."><td>".$row['file'].'</td><td>'.$row['line'].'</td></tr>';
}
}
return $return . '</table>';
}
}
?>

186
Model.class.php

@ -0,0 +1,186 @@
<?php
/**
* Класс модели данных
*
*/
abstract class Model
{
private $handler;
protected $table = false;
protected $primary_key = 'id';
function __construct()
{
$this->handler = DBConnector::getConnect(Env::getParam('db_settings'));
}
/**
* Выполняет запрос и возвращает сырой результат
*
* @param string $sql
* @return resource
*/
function exec($sql)
{
$time = microtime(true);
$res = mysqli_query($this->handler, $sql);
if (mysqli_errno($this->handler)) {
throw new MJException("<b>Query Error:</b>\n".$sql."\n<b>Error:</b>\n".mysqli_error($this->handler), 1);
}
if (DEBUG_ENABLE) {
DBConnector::$queries[] = $sql.'; ('.round((microtime(true)-$time)*1000, 1).'ms)';
}
return $res;
}
/**
* Выполняет запрос и возвращает объект результата
*
* @param string $sql
* @return object
*/
function query($sql)
{
$res = $this->exec($sql);
switch (strtolower(substr($sql, 0, 6))) {
case 'select':
return new ModelSelectResult($res);
case 'insert':
return new ModelInsertResult($this->handler);
default:
return new ModelChangeResult($this->handler);
}
}
/**
* Экранирует строку
*
* @param string $data - строка для экранирования
* @return string
*/
function escape($data)
{
return mysqli_real_escape_string($this->handler, $data);
}
//////////////////////////
function update($id, $data)
{
$sql = '';
foreach ($data as $key => $val) {
$sql .= $key."='".$this->escape($val)."', ";
}
return $this->query('UPDATE '.$this->table.' SET '.rtrim($sql, ', ').' WHERE '.$this->primary_key.'='.(int) $id);
}
function insert($data, $postfix = '')
{
$sql = '';
foreach ($data as $key => $val) {
$sql .= $key.'="'.$this->escape($val).'", ';
}
return $this->query('INSERT '.$this->table.' SET '.rtrim($sql, ', ').' '.$postfix);
}
function delete($id)
{
return $this->query('DELETE FROM '.$this->table.' WHERE '.$this->primary_key.'='.(int) $id);
}
function get($id)
{
return $this->query('SELECT * FROM '.$this->table.' WHERE '.$this->primary_key.'='.(int) $id);
}
function getList($limit = false, $sort = 'ASC')
{
return $this->query('SELECT * FROM '.$this->table.' ORDER BY '.$this->primary_key.' '.($sort == 'ASC' ? 'ASC' : 'DESC').($limit === false ? ' LIMIT '.(int) $limit : ''));
}
}
class ModelResult
{
function __call($name, $args)
{
throw new MJException('Call undeclared method "'.$name.'" in "'.get_class($this).'" class', -1);
}
}
class ModelSelectResult extends ModelResult
{
public $result;
function __construct($res)
{
$this->result = $res;
}
function fetch()
{
return mysqli_fetch_object($this->result);
}
function fetchField($field, $default = false)
{
$row = $this->fetch();
return isset($row->$field) ? $row->$field : $default;
}
function fetchAll()
{
$array = array();
while ($row = mysqli_fetch_object($this->result)) {
$array[] = $row;
}
return $array;
}
function count()
{
return mysqli_num_rows($this->result);
}
function free()
{
mysqli_free_result($this->result);
}
function __destruct() {
$this->free();
}
}
class ModelChangeResult extends ModelResult
{
public $affected;
function __construct($resource)
{
$this->affected = mysql_affected_rows($resource);
}
function count()
{
return $this->affected;
}
}
class ModelInsertResult extends ModelResult
{
public $id;
function __construct($resource)
{
$this->id = mysqli_insert_id($resource);
}
function getId()
{
return $this->id;
}
}
?>

27
PageController.class.php

@ -0,0 +1,27 @@
<?php
final class PageController
{
/**
* Вывод в браузер всего сайта.
*
*/
public function display()
{
try {
$this->route = Load::router()->proccess(MJ_PATH);
$decorator = new $this->route->decorator;
return $decorator->display(new $this->route->action);
} catch (MJException $e) {
return $e->terminate();
} catch (Exception $e) {
$decorator_name = DEFAULT_DECORATOR;
$action_name = DEFAULT_ACTION;
$decorator = new $decorator_name;
return $decorator->display(new $action_name($e->getMessage()));
}
}
}
?>

106
Router.class.php

@ -0,0 +1,106 @@
<?php
/**
* Класс для работы с роутерами
*
*/
final class Router
{
protected $routes = array();
/**
* Добавить роутер
*
* @param string $name - имя роутера
* @param string $path - путь
* @param string $action - имя действия
* @param array $params - массив параметров
*/
function add($name, $path, $action, $params = array())
{
$this->routes[$name] = new Route($path, $action, $params);
}
/**
* Установить декоратор для роута (действия), отличный от стандартного
*
* @param string $name - имя роута (действия)
* @param string $decorator - имя декоратора
*/
function setDecorator($name, $decorator)
{
if (isset($this->routes[$name])) {
$this->routes[$name]->decorator = $decorator.DECORATOR_POSTFIX;
}
}
/**
* Найти роутер соответствующий заданному пути
*
* @param stirng $path - путь
* @return Route - роутер
*/
function proccess($path)
{
$path = explode('/', $path);
foreach ($this->routes as $name => $route) {
if ($route->match($path)) {
$route->action .= ACTION_POSTFIX;
Env::setParams($route->params);
return $route;
}
}
throw new Exception(E_404);
}
}
/**
* Роутер
*
*/
final class Route
{
const URL_VARIABLE = '&';
protected $path;
public $decorator = DEFAULT_DECORATOR;
public $action;
public $params;
function __construct($path, $action, $params=array())
{
$this->path = $path;
$this->action = $action;
$this->params = $params;
}
/**
* Проверяет соответствие роутера и пути
*
* @param string $path - путь для сравнения
* @return boolean - соответствует или нет
*/
function match($path_arr)
{
$parts = explode('/', $this->path);
$cnt = count($parts);
if (end($parts) == self::URL_VARIABLE) {
$cnt--;
} elseif ($cnt != count($path_arr)) {
return false;
}
for ($i=0; $i<$cnt; $i++) {
if (substr($parts[$i], 0, 1) == self::URL_VARIABLE) {
$this->params[substr($parts[$i], 1)] = $path_arr[$i];
} elseif ($parts[$i] != $path_arr[$i]) {
return false;
}
}
return true;
}
}
?>

93
Sublimer.class.php

@ -0,0 +1,93 @@
<?php
/**
* Простейший шаблонизатор.
* Зато быстрый.
*
*/
final class Sublimer
{
const GLOBAL_KEY = 0;
public $vars = array();
public $path = '';
public $template = '';
protected $head_array = array();
/**
* Конструктор
*
* @param string $path - путь до шаблонов
*/
public function __construct($path = '')
{
$this->setPath($path);
$this->vars[self::GLOBAL_KEY] = array();
}
/**
* Присвоить переменную только
*
* @param string $name - имя переменной
* @param mixed $value - значение переменной
* @param string $template - шаблон, если не указан, то переменная глобальная
*/
public function assign($name, $value, $template = self::GLOBAL_KEY)
{
$this->vars[$template][$name] = $value;
}
/**
* Очистить стек переменных
* @param boolean $with_local - включая локальные переменные
*/
public function clear($template = self::GLOBAL_KEY)
{
$this->vars[$template] = array();
}
/**
* Обработать шаблон
*
* @param string $template - относительный путь до шаблона
* @return string - обработанное содержимое шаблона
*/
public function fetch($template)
{
$this->template = $template; //дабы экстракт не перезатер нам переменную. Это важно! $this то он не перезатрет :)
extract($this->vars[self::GLOBAL_KEY]);
if (isset($this->vars[$this->template])) {
extract($this->vars[$this->template], EXTR_OVERWRITE);
}
ob_start();
if (!(include $this->path.'/'.$this->template) == 'OK') {
throw new MJException('Template '.$this->path.'/'.$this->template.' not found');
}
return ob_get_clean();
}
/**
* Установать путь до шаблонов
*
* @param string $path - путь до шаблонов
*/
public function setPath($path)
{
$this->path = $path;
}
//Функции для вызова из шаблонов.
protected function addHead($str)
{
$this->head_array[] = $str;
}
protected function getHead()
{
return $this->head_array;
}
}
?>

90
User.class.php

@ -0,0 +1,90 @@
<?php
/**
* Класс пользователя
*
*/
class User
{
static private $user = false;
static function login($login, $password)
{
if (empty($login)) {
return false;
}
if (! self::$user = self::getByLogin($login) ) {
return false;
}
if (self::$user->password != md5($password)) {
return false;
}
self::setSession();
return true;
}
static function logout()
{
Env::setCookie(session_name(), '', 0);
Env::setCookie('login', '', 0);
Env::setCookie('login_hash', '', 0);
if (session_id()) {
session_destroy();
}
}
static function process()
{
if (Env::getCookie(session_name())) { //есть сессия
session_start();
} elseif (Env::getCookie('login') && Env::getCookie('login_hash')) {
self::remember();
}
}
static function setSession()
{
Env::setCookie('login', self::$user->login, TIME_NOW + LOGIN_COOKIE_TTL);
Env::setCookie('login_hash', self::getHash(), TIME_NOW + LOGIN_COOKIE_TTL);
session_start();
$_SESSION['user'] = self::$user;
}
static function remember()
{
if (! self::$user = self::getByLogin(Env::getCookie('login'))) {
self::logout();
}
if (Env::getCookie('login_hash') == self::getHash()) {
self::setSession();
} else {
self::logout();
}
}
static function getHash()
{
return md5(self::$user->id.'hckrz'.self::$user->login.'mst'.self::$user->password.'dai');
}
static function getInfo()
{
return Env::Session('user', self::$user);
}
static function isGuest()
{
return ! (bool) Env::Session('user');
}
static function getByLogin($login)
{
return Load::model('UserData')->getByLogin($login);
}
}
?>
Loading…
Cancel
Save