Переделка для composer autoload

This commit is contained in:
origami11 2017-02-09 14:57:40 +03:00
parent ad69746347
commit b5641db607
100 changed files with 14 additions and 325 deletions

22
src/adapter.php Normal file
View file

@ -0,0 +1,22 @@
<?php
/**
* Интерфейс к массиву и обьекту как к коллекции
*/
class Adapter
{
protected $adaptee;
public function __construct ($adaptee)
{
$this->adaptee = $adaptee;
}
public function get($name)
{
if (is_array ($this->adaptee)) {
return $this->adaptee[$name];
} else {
return $this->adaptee->$name;
}
}
}

7
src/arr.php Normal file
View file

@ -0,0 +1,7 @@
<?php
class Arr {
static function get($data, $key, $default = null) {
return isset($data[$key]) ? $data[$key] : $default;
}
}

100
src/collection.php Normal file
View file

@ -0,0 +1,100 @@
<?php
/**
* Коллекция
*
* package core
*/
class Collection implements ArrayAccess
{
/**
* Holds collective request data
*
* @var array
*/
protected $data = array();
/**
* Преобразование массива в коллекцию
*
* @param array $data
*/
public function import(array $data)
{
$this->data = array_merge($this->data, $data);
return true;
}
/**
* Преобразование коллекции в массив
*/
public function export()
{
return $this->data;
}
/**
* Store "request data" in GPC order.
*
* @param string $key
* @param mixed $value
*
* @return void
*/
public function set($key, $value)
{
$this->data[$key] = $value;
}
/**
* Read stored "request data" by referencing a key.
*
* @param string $key
*
* @return mixed
*/
public function get($key, $default = null)
{
return isset($this->data[$key]) ? $this->data[$key] : $default;
}
public function getInt($key, $default = 0)
{
return intval($this->get($key, $default));
}
public function getString($key, $default = '')
{
return ((string) $this->get($key, $default));
}
public function getNat($key, $default = 1)
{
$result = intval($this->get($key, $default));
return (($result > 0) ? $result : $default);
}
public function clear()
{
$this->data = array();
}
public function offsetSet($key, $value)
{
$this->data[$key] = $value;
}
public function offsetExists($key)
{
return isset($this->data[$key]);
}
public function offsetUnset($key)
{
unset($this->data[$key]);
}
public function offsetGet($key)
{
return isset($this->data[$key]) ? $this->data[$key] : null;
}
}

8
src/config.php Normal file
View file

@ -0,0 +1,8 @@
<?php
function loadConfig($filename) {
if (@include($filename)) {
return $settings;
}
throw new Exception("config $filename not found");
}

View file

@ -0,0 +1,94 @@
<?php
class Connection_HttpConnection
{
const POST = "POST";
const GET = "GET";
private $param = array(); // Параметры запроса
public $data = null; // Содержание
public $url; // Адресс
public $method = self::GET; // Метод
public $port = 80;
public $host = "";
public $proxy_host = false;
public $proxy_port = false;
/**
* Возвращает заголовок соединения
*/
public function getHeader()
{
$result = $this->method . " " . $this->url . " HTTP/1.1\r\n";
$result .= "Host: ". $this->host ."\r\n";
foreach ($this->param as $key => $value) {
$result .= $key . ": " . $value . "\r\n";
}
$result .= "Connection: Close\r\n\r\n";
$result .= $this->data;
return $result;
}
/**
* Установка параметров запроса
* @parma string $name
* @parma string $value
*/
public function setParameter($name, $value)
{
$this->param[$name] = $value;
}
/**
* Метод запроса GET или POST
*/
public function setMethod($method)
{
$this->method = $method;
}
public function setUrl($url)
{
$this->url = $url;
$this->host = parse_url($this->url, PHP_URL_HOST);
}
public function getUrl()
{
return $this->url;
}
/**
* Содержание запроса
*/
public function setContent($data)
{
$this->setParameter ("Content-length", strlen($data));
$this->data = $data;
}
/**
* Посылает запрос и возвращает страницу
*/
public function getPage()
{
$host = ($this->proxy_host) ? $this->proxy_host : $this->host;
$port = ($this->proxy_port) ? $this->proxy_port : $this->port;
$socket = fsockopen($host, $port, $errno, $errstr, 30);
if (! $socket) {
return null; // Exception
} else {
$header = $this->getHeader();
fwrite($socket, $header);
$result = null;
while (! feof($socket)) {
$result .= fgets($socket, 128);
}
fclose($socket);
return $result;
}
return null;
}
}

View file

@ -0,0 +1,82 @@
<?php
/**
* Обрабатывает HTTP ответ
*/
class Connection_HttpConnectionResponse
{
private $offset;
private $param = array ();
private $code;
public function __construct($response)
{
$this->offset = 0;
$this->response = $response;
$this->parseMessage();
}
/**
* Обработка HTTP ответа
*/
private function parseMessage()
{
$http = explode(" ", $this->getLine());
$this->version = $http[0];
$this->code = $http[1];
$line = $this->getLine();
while ($offset = strpos($line, ":")) {
$this->param[substr($line, 0, $offset)] = trim(substr($line, $offset + 1));
$line = $this->getLine();
}
if (isset($this->param['Transfer-Encoding']) && $this->param['Transfer-Encoding'] == 'chunked') {
//$this->data = substr($this->response, $this->offset);
$line = hexdec($this->getLine());
$chunk = array();
while ($line > 0) {
$chunk [] = substr($this->response, $this->offset, $line);
$this->offset += $line;
$line = hexdec($this->getLine());
}
$this->data = implode("", $chunk);
} else {
$this->data = substr($this->response, $this->offset);
}
}
/**
* Обработка строки HTTP ответа
*/
private function getLine()
{
$begin = $this->offset;
$offset = strpos($this->response, "\r\n", $this->offset);
$result = substr($this->response, $begin, $offset - $begin);
$this->offset = $offset + 2;
return $result;
}
/**
* Значение параметра HTTP ответа
*/
public function getParameter($name)
{
return $this->param[$name];
}
public function getData()
{
return $this->data;
}
/**
* Состояние
*/
public function getCode()
{
return $this->code;
}
}

File diff suppressed because it is too large Load diff

423
src/controller/action.php Normal file
View file

@ -0,0 +1,423 @@
<?php
/**
*
* @package Core
*/
/**
* Переименовать контроллер !! (StubController, CrudController, PageController, BaseController) ModelController
* Возможно нужен еще класс с мета действиями как для actionIndex <= metaActionIndex либо с классам для этих действий
* Есть класс для управлениями действиями а есть сами действия в виде классов или функций !!
*/
class Controller_Model extends Controller_Action
{
public $schema = array();
public $schemaSearch = array();
/**
* FIXME: Лучше $this->table->setHeader
*/
public $tableSchema = null;
public $formSchema = array();
public $menu;
public $path;
public $table;
public function __construct()
{
$this->path = new PathMenu();
$this->menu = new PageMenu();
$this->table = new ListTable();
}
/**
*/
function setUp()
{
$this->table->addMenuItem($this->aUrl('delete'), 'удалить', false, 'all', 'warning');
//$this->table->addMenuItem($this->nUrl('form'), 'редактировать', 'edit-24.png');
}
function saveParameters($args, $list)
{
foreach ($list as $item) {
$args->session()->set(array($this, $item), $args->get($item));
}
}
protected function getJSONList(/*Mapper*/ $model, Collection $request)
{
$result = array();
$this->saveParameters($request, array('size','page','desc', 'key'));
$result['list'] = $model->findAll($request, $request->get('ref'));
$result['size'] = $model->getCount($request, $request->get('ref'));
return json::encode($result);
}
/**
* Удаление сторк из таблицы
*/
public function actionDelete(HttpRequest $request)
{
$model = $this->getModel($this->useModel);
// Почему table_item ???
$list = ($request->get('table_item')) ? $request->get('table_item'): $request->get('id');
$model->deleteList($list);
return $this->getJSONList($model, $request);
}
/**
* Ответ на запрос по поиску
*/
public function actionSearch(HttpRequest $request)
{
$model = $this->getModel($this->useModel);
$model->addFilter($model->requestToSQL($request, $this->formSchema));
return $this->getJSONList($model, $request);
}
/**
* Список элементов
*/
public function actionList(HttpRequest $request)
{
$model = $this->getModel($this->useModel);
return $this->getJSONList($model, $request);
}
private function setFormSchema()
{
require_once 'core/mapper/uimapper.php';
$model = $this->getModel($this->useModel);
$ui = new UIMapper($model);
$this->formSchema = $ui->getFormSchema();
}
/**
* Сохранение формы
*/
function beforeSave(/*Model*/ $item, Collection $request)
{
if (empty($this->formSchema)) {
$this->setFormSchema();
}
// Сделать отображение Формы в обьект и обратно <-- Убрать в beforeSave
foreach ($this->formSchema as $key => $conv) {
list($value, $type) = $conv;
$item->$value = call_user_func(array('Cast', 'to_' . $type), $request->get($key)); // Здесть нужно преобразовывать тип значения
}
}
/**
* Обновление формы
*/
function formUpdate(TForm $form, Collection $request)
{
}
/**
* Загрузка формы
*/
function beforeLoad(/*Model*/ $item, TForm $form)
{
if (empty($this->formSchema)) {
$this->setFormSchema();
}
// Вставка значений из данных в форму
// Отображение обьекта в поля формы
$form->fill($item, $this->formSchema);
}
// Проверка ввода
protected function validate($validator, $request)
{
}
/**
* Действие для проверки формы
*/
public function actionValidate($request)
{
require_once "core/validator/validator.php";
$validator = new Validator();
$validator->addRuleList($this->schema);
// Действия до проверки формы
$this->validate($validator, $request); // <--|
$validator->validate($request); // --|
// Проверка формы
if (!$validator->isValid()) {
return json::encode($validator->getErrorMsg());
}
return json::encode(true);
}
/**
* Инициализация формы
*/
protected function formSetup($form, $id = null, $ref = null)
{
if (empty($this->schema)) {
$model = $this->getModel($this->useModel);
$ui = new UIMapper($model);
$schema = $ui->getEditSchema();
$form->addFieldList($schema);
} else {
$form->addFieldList($this->schema);
}
}
/**
* Добавление пользователя
*/
public function actionAdd(HttpRequest $request)
{
require_once "core/validator/validator.php";
// {{{ тоже может быть один ref или несколько
$ref = $request->get('ref');
$this->addParameter('ref', $ref); // Добавляет параметр в url
/// }}}
if ($this->checkPageId($request, $request->get('page'))) {
// Проверка
$validator = new Validator();
$validator->addRuleList($this->schema);
// Действия до проверки формы
$this->validate($validator, $request); // <--|
$validator->validate($request); // --|
// Проверка формы
if (!$validator->isValid()) {
$request->setAction('form');
$this->getActionPath($request);
$form = new TForm();
$this->formSetup($form, $request->get('id'), $request->get('ref')); // Инициализация формы
$form->setValues($request); // <-- Убрать в formUpdate
$this->formUpdate($form, $request);
$form->setError($validator); // Установка ошибок для формы
$tpl = $this->formPage($form, $request);
$id = $request->get('id');
if ($id) { // Редактирование
$tpl->action = forceUrl($this->nUrl('add', array('id' => $id, 'page' => $this->getPageId($request)))); // action Совйство формы
}
return $tpl /*->execute()*/;
}
// Нужен тест для формы
$model = $this->getModel($this->useModel);
$className = $model->className;
$item = new $className();
// Сохраняем значение в базе данных
$item->id = $request->get('id');
// Если таблица связана с другой таблицей
if ($request->get('ref') && $model->reference[1]) {
$ref_id = $model->reference[1];
$item->$ref_id = $request->get('ref');
}
// Подготовка к сохранению
$this->beforeSave($item, $request); // Сюдаже и истрия переходов
// nextId ??? или выход или новая форма для создания новости
$model->saveDB($item, $request);
}
// Для страницы со списком id -> идентефикатор родительской таблицы !!??
// $request->set('id', $request->get('ref'));
if ($request->get('apply')) {
$request->setAction('form');
return $this->forward('actionForm', $request);
}
return $this->forward('actionIndex', $request);
}
/**
* Заголовок
*/
private function setTitlePath($ref)
{
if ($ref) {
$model = $this->getModel($this->useModel);
if (is_array($model->reference) && $model->reference[0]) {
$refmodel = $this->getModel($model->reference[0]);
try {
$parent = $refmodel->findById($ref);
$this->path->addTitle($parent->getTitle()); // Заголовок к подписям путей
} catch (Exception $e) {
// Не найден заголовок потому что неправильно определен родительский элемент
}
}
}
}
/**
* Форма для редактирования
*/
public function actionForm(HttpRequest $request)
{
$this->getActionPath($request);
$ref = $request->get('ref');
$this->addParameter('ref', $ref); // Добавляет параметр в url
$this->setTitlePath($ref);
$model = $this->getModel($this->useModel);
$form = new TForm(); // Показываем форму
$form->header = 'Редактирование записи';
$this->formSetup($form, $request->get('id'), $request->get('ref')); // Инициализация формы
$list = $request->get('table_item');
$id = ($list[0]) ? $list[0] : $request->get('id');
$tpl = $this->formPage ($form, $request);
if ($id) { // Редактирование
$form->action = forceUrl($this->nUrl('add', array('id' => $id, 'page' => $this->getPageId($request)))); // action Свойство формы
$item = $model->findById($id);
// Загрузка формы
$this->beforeLoad($item, $form);
///
}
return $tpl;
}
/**
*/
function tableSetup($table, $id = null, $ref = null)
{
// FIXME: После замены везде $tableSchema -> table->setHeader удалить!
if ($this->tableSchema) {
$table->setHeader($this->tableSchema);
} else {
// Настройка таблицы отображения по схеме данных
require_once 'core/mapper/uimapper.php';
$model = $this->getModel($this->useModel);
$ui = new UIMapper($model);
$schema = $ui->getTableSchema();
$schema[0]['action'] = $table->getFirstItem();
$table->setHeader($schema);
}
}
/**
*/
public function actionIndex(HttpRequest $request)
{
$this->getActionPath($request, 'index');
// Такое мета действие наверное можно вынести в отдельный класс
return $this->metaActionIndex($request, array($this, 'tableSetup'), $this->aUrl('list'));
}
/**
* Страница по умолчанию
*/
public function metaActionIndex(HttpRequest $request, $setup, $list)
{
// может быть одно ref или несколько
// {{{ история переходов
$ref = null;
if ($request->get('ref')) {
$ref = $request->get('ref');
} else if ($request->session()->get('ref')) {
$ref = $request->session()->get('ref');
}
$request->session->set('ref', $ref);
$this->addParameter('ref', $ref);
// }}}
$this->setTitlePath($ref);
$tpl = $this->getView('list');
// Помошники действий
$this->callHelpers($request);
// Таблица
if ($request->session()->get(strtolower(get_class($this)))) {
$session = $request->session()->get(strtolower(get_class($this)));
if (isset($session['view'])) {
$this->table->setView($session['view']);
}
$this->table->setData('state', array(
'page' => $session['page'],
'size' => $session['size'],
'desc' => $session['desc']));
$this->table->setData('sorter', $session['key']);
if (isset($session['desc'])) {
$this->table->setData('desc', $session['desc']);
}
}
call_user_func($setup, $this->table, $request->get('id'), $ref);// --> Эквивалент formSetup
$this->table->setAction($list);
//
$tpl->menu_path = $this->path->getItems();
// Поиск
$search = new SearchDialog();
$search->setTitle('Поиск');
$search->setAction($this->aUrl('search'));
$search->setFriend($this->table);
$search->addFields($this->schemaSearch);
// Настройки
$setup = new SetupDialog();
$setup->setTitle('Настройки');
$setup->setAction($this->nUrl('setup'));
$setup->setFriend($this->table);
// Меню
$this->menu->addMenuItem('?menu=toggle&id=' . $search->getName(), 'поиск', 'actions/system-search'); // Стандартный размер для иконок 22-24px
$this->menu->addMenuItem('?menu=toggle&id=' . $setup->getName(), 'настройки', 'categories/applications-system');
// Добавление компонентов
$this->addChild('menu', $this->menu);
$this->addChild('search', $search);
$this->addChild('setup', $setup);
$this->addChild('table', $this->table);
//
return $tpl;
}
/**
*/
public function actionSetup($request)
{
$left = explode(",", $request->get('left'));
$right = explode(",", $request->get('right'));
$$request->session()->set(strtolower(get_class($this)),
array('view' => array('left' => $left, 'right' => $right)));
return $this->forward('actionIndex', $request);
}
/**
*/
private function formPage($form, $request)
{
$view = $this->getView('form');
$view->setView('form', $form);
$view->action = forceUrl($this->nUrl('add', array('page' => $this->getPageId($request)))); // Действие для формы
$view->menu_path = $this->path->getItems();
$view->back = $this->path->getPrev();
return $view;
}
// Тоже убрать в метод Controller_Model
function getActionPath(HttpRequest $request/*, $action = false*/)
{
require_once 'state.php';
$this->_getActionPath()->getPath($this, ($action) ? $action : $request->getAction());
}
}

View file

@ -0,0 +1,183 @@
<?php
class FileNotFountException extends Exception
{
}
/**
* Класс компонента
*/
class Controller_Component
{
static $_uid = 1;
public $uid; // UID компонента создается при создании страницы, вставки компонента, или это статическое свойство
public $viewPath;
public $registry; // Registry->getInstance
public $template;
function __construct()
{
self::$_uid ++;
$this->uid = self::$_uid;
}
function getUID()
{
return 'component:'. $this->uid;
}
public function getView($name)
{
require_once "core/view/compositeview.php";
//
$template = ($this->template) ? $this->template : $this->_registry->readKey(array('system', 'template'));
// Загружать шаблон по умолчанию если не найден текущий
if (is_dir(Path::join($this->viewPath, 'templates', $template))) {
$template_file = Path::join($this->viewPath, 'templates', $template, $name);
} else {
$template_file = Path::join($this->viewPath, 'templates', 'modern', $name);
}
$tpl = new View_Composite($template_file);
$tpl->script = $_script = Path::join(WWW_PATH, 'js');
$tpl->media = $_media = Path::join(TEMPLATE_WEB, $template);
$tpl->component = $_template = Path::join(COMPONENTS_WEB, strtolower(get_class($this)), 'templates', 'modern');
$tpl->setAlias(array(
'${media}' => $_media,
'${script}' => $_script,
'${template}' => $_template));
$tpl->loadImports(Path::skipExtension($template_file) . ".import");
return $tpl;
}
public function setParameters($view)
{
}
/**
* @param $name Имя модели
*/
private function getModelPath($name)
{
return Path::join (CMS_PATH, "model", $name . ".php");
}
/**
* Создает модель
* @param string $name
* @return model
*/
public function getModel($name)
{
require_once 'core/mapper/mapper.php';
require_once ($this->getModelPath ($name));
$modelName = $name . "Mapper";
$model = new $modelName ();
$model->db = $this->db;
return $model;
}
public function options($key, $val, $res) {
$result = array();
while($res->next()) {
$result[] = array('value' => $res->getInt($key), 'name' => $res->getString($val));
}
return $result;
}
public function optionsPair($list) {
$result = array();
foreach ($list as $key => $value) {
$result [] = array('value' => $key, 'name' => $value);
}
return $result;
}
/* В дальнейшем нужно зменить на методы
+ Методы могут быть и javascript
*/
protected $editUrl;
function setEditUrl($url)
{
$this->editUrl = $url;
}
function getEditUrl()
{
return $this->editUrl;
}
}
/**
* TALES для подключения компонентов
* component:name?param1=value1&param2=value2
*/
class Component_Tales implements PHPTAL_Tales
{
static public function component($expression, $nothrow = false)
{
return "phptal_component('" . $expression . "')";
}
}
function loadComponent($name, $db, $registry)
{
$path = Path::join(COMPONENTS, $name, $name . ".php");
// echo COMPONENTS, '<br />';
// echo $path;
if (file_exists($path)) {
require_once ($path);
$component = new $name();
$component->db = $db;
$component->_registry = $registry;
$component->viewPath = COMPONENTS."/".$name."/";
return $component;
}
throw new FileNotFountException();
}
/**
* Функция подключения компонента
*/
global $componentList;
$componentList = array();
function phptal_component ($real_expression, $offset = 0) {
global $db, $registry, $componentList; // Нужно както передавать параметры
$expression = htmlspecialchars_decode($real_expression);
$url = parse_url($expression);
parse_str($url['query'], $arguments);
$name = $url['path'];
$component = loadComponent($name, $db, $registry);
$req = new HttpRequest();
$params = new Collection();
$params->import(array_merge($_GET, $arguments));
$component->params = $params;
$componentList [] = array(
'uid' => $component->getUID(), 'params' => $expression, 'name' => $name, 'offset' => $offset,
'size' => strlen($real_expression),
/* Вместо ссылки на редактирование нужно передавать список методов для работы с компонентом
edit (редактирование содержание), new (новое содержание), шаблон коменнента ... вместе с иконками этих методов
! Компоненты могут содержать другие компоненты
*/
'editurl' => $component->getEditUrl(),
'newurl' => ''
);
unset($req['active_page']);
$component->template = $params->get('template', false);
return $component->execute($params, $req);
}
/* Регистрация нового префикса для подключения компонента */
$registry = PHPTAL_TalesRegistry::getInstance();
$registry->registerPrefix('component', array('Component_Tales', 'component'));

View file

@ -0,0 +1,341 @@
<?php
require_once 'core/functions.php';
function forceUrl($name)
{
if (is_callable($name)) {
return call_user_func($name);
}
return $name;
}
/**
* Контроллер страниц
* @package core
*/
class Controller_Controller
{
const TEMPLATE_EXTENSION = ".html"; // Расширение для шаблонов
const ACTION_PREFIX = "action"; // Префикс для функций действий
public $jsPath; // Глобальный путь к скриптам
public $themePath; // Глобальный путь к текущей теме
// Параметры устанавливаются при создании контроллера
public $name; // Имя модуля
public $viewPath = null; // Путь к шаблонам контроллера
public $db; // Соединение с базой данных
// Фильтры
public $access; // Обьект хранит параметры доступа
public $logger; // Обьект для ведения лога
private $factory; // Ссылка на обьект создания модели
private $helpers = array(); // Помошники для действий
public $param = array(); // Параметры для ссылки
public $_registry; // Ссылка на реестр
public $_shortcut;
public function __construct ()
{
//
}
public function setUp ()
{
// override this
}
public function loadConfig($name) {
$filename = Shortcut::getUrl('config', $name);
if (file_exists($filename)) {
include($filename);
} else {
throw new Exception('Невозможно загрузить файл настроек ' . $name);
}
return $settings;
}
public function getConnection()
{
return $this->db;
}
public function installPath($name)
{
return Path::join(CMS_PATH, "modules", $name, "install");
}
public function addSuggest($view, $name)
{
$suggest = array();
$file = Path::join($this->viewPath, 'help', $name . '.suggest');
if (file_exists($file) && include($file)) {
$view->addScriptRaw("add_suggest(".json::encode($suggest).");\n");
}
}
function findIcon($icon, $size)
{
return Path::join($this->iconPath, $size . 'x' . $size, $icon . '.png');
}
/**
* Создает представление
* @param string $file
* @return template
*/
public function getView($name)
{
require_once "core/view/compositeview.php";
$file = $name . self::TEMPLATE_EXTENSION;
// Список возможных директорий для поиска файла шаблона
$theme = $this->_registry->readKey(array('system', 'theme'));
$icon_theme = $this->_registry->readKey(array('system', 'icon_theme'));
$list = array(
Path::join($this->viewPath, TEMPLATES) => Path::join(WWW_PATH, "modules", $this->name, TEMPLATES),
PHPTAL_TEMPLATE_REPOSITORY => "");
// Поиск файла для шаблона
foreach($list as $ospath => $path) {
$template = Path::join($ospath, $file);
if(file_exists($template)) { break; }
}
$tpl = new View_Composite($template);
$tpl->icons = $this->iconPath; // Путь к файлам текущей темы
$tpl->media = $this->themePath; // Путь к файлам текущей темы
$tpl->script = $this->jsPath; // Путь к файлам скриптов
$tpl->template = $path; // Путь к файлам текущего шаблона
$tpl->setAlias(array(
'${icons}' => $this->iconPath,
'${media}' => $this->themePath,
'${script}' => $this->jsPath,
'${template}' => $path));
$tpl->loadImports(Path::skipExtension($template) . ".import");
$this->addSuggest($tpl, $name);
return $tpl;
}
public function getModel($name)
{
if (!$this->factory) {
$this->factory = new ModelFactory($this->db, $this->_registry, $this->_shortcut);
}
return $this->factory->getModel($name);
}
/**
* Выбор действия
* Т.к действия являются методами класса то
* 1. Можно переопределить действия
* 2. Использовать наследование чтобы добавить к старому обработчику новое поведение
* @param $request Обьект запроса
*/
public function execute1(HTTPRequest $request)
{
$action = self::ACTION_PREFIX . ucfirst($request->getAction());
if (method_exists($this, $action)) {
return $this->forward($action, $request);
} else {
return $this->forward("actionIndex", $request);
}
}
public function execute(HTTPRequest $request)
{
$result = $this->execute1($request);
if ($result) {
$this->view = $result;
}
return $this->render();
}
public function forward($action, HTTPRequest $args)
{
// Действия до вызова основного обработчика
/*foreach($this->_aspect as $aspect) {
if (isset($aspect->before[$action])) {
call_user_func ($aspect->before[$action], $action, $args);
}
}*/
return call_user_func(array($this, $action), $args);
}
/**
* Страница по умолчанию
*/
public function actionIndex(HttpRequest $request)
{
return "";
}
public function postUrl($name, $param)
{
return "?" . http_build_query(
array_merge(array('module' => strtolower(get_class($this)), "action" => $name),
$this->param, $param));
}
/**
* Генерация ссылки c учетом прав пользователя на ссылки
*
* @parma string $name Действие
* @parma string $param Дополнительные параметры
*/
public function nUrl($name, array $param = array())
{
if (!$this->access || $this->access->checkAction($name)) {
return lcurry(array($this, 'postUrl'), $name, $param);
}
return null;
}
public function fUrl($name, array $param = array())
{
return forceUrl($this->nUrl($name, $param));
}
/**
* Добавляет параметр для всех ссылок создаваемых функцией nUrl, aUrl
*/
public function addParameter($name, $value)
{
if ($value) {
$this->param [$name] = $value;
}
}
/**
* Генерация ссылки на действие контроллера
* Ajax определяется автоматически mode = ajax используется для смены layout
*/
public function aUrl($name, array $param = array())
{
return $this->nUrl($name, array_merge(array('mode' => 'ajax'), $param)); // FIXME
}
/**
* Добавление помошника контроллера
*/
public function addHelper($class)
{
$this->helpers [] = $class;
}
/**
* Вызов помошников контроллера
*/
public function callHelpers(HttpRequest $request)
{
$action = self::ACTION_PREFIX . $request->getAction();
foreach ($this->helpers as $helper) {
if (method_exists($helper, $action)) {
return call_user_func(array($helper, $action), $request, $this);
} else {
return $helper->actionIndex($request, $this); // Вместо return response ???
}
}
}
/**
* Загрузка файла класса
*/
public function loadClass($path, $setup = null)
{
if (file_exists($path)) {
require_once ($path);
$class = pathinfo($path, PATHINFO_FILENAME);
return new $class($setup);
}
return null;
}
public function loadSettings($path)
{
$result = new Settings($path);
$result->read();
return $result->export();
}
// Для Widgets
public $view = null;
public $childNodes = array();
public $childViews = array();
public function setView($name)
{
$this->view = $this->getView($name);
}
/**
* Установка заголовка для отображения
*/
public function setTitle($title)
{
$this->view->setTitle($title);
}
/**
* Добавление widget к отображению
*/
public function addChild(/*Widget*/ $section, $node)
{
$this->childNodes[$section] = $node;
}
/**
* Добавление дочернего отображения к текущему отображению
*/
public function addView(/*CompositeView*/ $section, $node)
{
$this->childViews[$section] = $node;
}
/**
* Генерация содержания
* Путаница c execute и render
*/
public function render()
{
foreach ($this->childNodes as $name => $node) {
$node->make($this);
$this->view->setView($name, $node->view);
}
foreach ($this->childViews as $name => $node) {
$this->view->setView($name, $node);
}
return $this->view;
}
function getPageId($request)
{
$pageId = time();
$request->session()->set('page', $pageId);
return $pageId;
}
function checkPageId($request, $page)
{
$_page = $request->session()->get('page');
$result = ($_page && $_page == $page);
$request->session()->clean('page');
return $result;
}
function redirect($action) {
header('location: ' . $this->fUrl($action));
exit();
}
}
class Controller_Action extends Controller {}

92
src/controller/front.php Normal file
View file

@ -0,0 +1,92 @@
<?php
require_once 'core/controller/controller.php';
require_once 'core/controller/installer.php';
/**
* Первичный контроллер контроллер страниц
* @package core
*/
class Controller_Front extends Controller
{
protected $shortcut; // Ярлык к модулю
protected $_param; // Параметр по которому выбирается модуль
protected $default; // Значение параметра по умолчанию
protected $installer;
public function __construct(Settings $_registry, $_shortcut)
{
require_once 'core/database_pdo.php';
parent::__construct();
$registry = $_registry;
$this->_registry = $_registry;
$this->_shortcut = $_shortcut;
$this->db = Database::getConnection($registry->readKey(array('system', 'dsn')));
$this->installer = new Installer($_registry);
}
/**
* Создает экземпляр модуля и выполняет действия для него
* @param string $name Имя модуля
* @param request $request Имя модуля
* @return string
*/
public function loadModule($name, Collection $request)
{
$this->installer->setUp($this->db, array($this, 'installPath'));
$this->installer->doUpdates($name); // ModuleLoader (1)
$moduleFile = Shortcut::getUrl($this->shortcut, $name); // ModuleLoader (2)
$module = $this->loadClass($moduleFile);
if ($module) {
// Инициализация модуля
// $module->viewPath = dirname($moduleFile);
$module->viewPath = Shortcut::getUrl('modulepath', $name);
$module->name = $name;
$module->param = $this->param;
//
$module->_registry = $this->_registry;
$module->_shortcut = $this->_shortcut;
$module->iconPath = $this->iconPath; // -> Registry
$module->themePath = $this->themePath; // -> Registry
$module->jsPath = $this->jsPath; // -> Registry
$module->db = $this->db;
// Не для всех приложений нужно вести лог действий
// Ведение лога
$logger = $this->loadClass(FRAMEWORK_PATH . '/core/filter/actionlogger.php', $module);
$logger->before = $this->loadSettings(Shortcut::getUrl('logger', $name));
// Управление доступом
$module->access = $this->loadClass(FRAMEWORK_PATH . '/core/filter/actionaccess.php', $logger);
$module->access->access = $this->loadSettings(Shortcut::getUrl('access', $name));
$module->setUp();
return $module->access->execute($request);
}
return null; // throw new FileNotFoundException();
}
public function setParameter($shortcut, $param, $name)
{
$this->shortcut = $shortcut;
// Параметр
$this->_param = $param;
$this->default = $name;
}
private function getParameter(Collection $list)
{
return ($list->get($this->_param)) ? $list->get($this->_param): $this->default;
}
public function execute(HTTPRequest $request)
{
return $this->loadModule($this->getParameter($request), $request);
}
}

View file

@ -0,0 +1,87 @@
<?php
class Controller_Installer
{
protected $db;
protected $installPath;
public $_registry;
public function __construct(Settings $_registry)
{
$this->_registry = $_registry;
}
public function setUp($db, $installPath)
{
$this->db = $db;
$this->installPath = $installPath;
}
function getSetupFile($name)
{
return Path::join(call_user_func($this->installPath, $name), "setup.php");
}
// Проверка версии обновления
function isChanged($name) // Информация о модулях
{
$item = $this->_registry->readKey(array($name));
if ($item) {
$setup = $this->getSetupFile($name);
if (file_exists($setup) && (filemtime($setup) > $item['time'])) {
return true;
}
return false;
}
return true;
}
function installSQL(array $sql, $version_new, $version_old, $name)
{
require_once "core/setup.php";
foreach ($sql as $version => $install) {
if (version_compare($version, $version_new, "<=") && version_compare($version, $version_old, ">")) {
// this->installPath this->db
$file = Path::join(call_user_func($this->installPath, $name), "sql", $install);
Setup::batchSQL($this->db, $file);
}
}
}
// Устанавливает обновления если есть
function doUpdates($name, $force = false) // Установка модуля
{
$setup = $this->getSetupFile($name);
if (file_exists($setup) && ($this->isChanged($name) || $force)) {
$registry = $this->_registry;
$settings = new Settings($setup);
$settings->read();
$item = $registry->readKey(array($name));
$version_new = $settings->get('version');
if ($item) {
$version_old = $item['version'];
} else {
$version_old = "0.0";
$registry->writeKey(array($name), array());
}
if (version_compare($version_old, $settings->get('version'), "!=")) {
$sql = $settings->get('sql');
if (is_array($sql)) {
$this->installSQL($sql, $version_new, $version_old, $name);
}
}
// Обновление версии меню
$registry->writeKey(array($name), $settings->get('settings'));
$registry->writeKey(array($name),
array('version' => $version_new,
'time' => filemtime($setup)));
$registry->write();
}
}
}

79
src/controller/state.php Normal file
View file

@ -0,0 +1,79 @@
<?php
class Controller_State
{
public $action = '';
public $states = array();
public $titles = array();
public function __construct($action)
{
$this->action = $action;
}
static function make($action)
{
return new State($action);
}
public function addTitle($name, $url = array())
{
$this->titles [] = array($name, $url);
return $this;
}
public function addState(State $state)
{
$this->states [$state->getAction()] = $state;
return $this;
}
public function getAction()
{
return $this->action;
}
function checkAction($action, &$list)
{
if ($this->action == $action) {
array_push($list, $this);
return true;
} else {
foreach ($this->states as $state) {
if ($state->checkAction($action, $list)) {
array_push($list, $this);
return true;
}
}
}
return false;
}
function makeTitle($module)
{
foreach ($this->titles as $item) {
$module->path->addMenuItem($module->nUrl($this->action, $item[1]), $item[0]);
}
}
function getPath($module, $action)
{
$list = array();
if ($this->checkAction($action, $list)) {
foreach (array_reverse($list) as $item) {
$item->makeTitle($module);
}
} else {
$this->makeTitle($module);
}
}
}
/*
$path = State::make('index')
->addState(State::make('form'))
->addState(State::make('view'));
$path->getPath(0, 'form');
*/

26
src/data/areas.php Normal file
View file

@ -0,0 +1,26 @@
<?php
$settings = array
(
0 => '',
1 => 'Город Ярославль',
2 => 'Большесельский район',
3 => 'Борисоглебский район',
4 => 'Брейтовский район',
5 => 'Гаврилов-Ямский район',
6 => 'Даниловский район',
7 => 'Любимский район',
8 => 'Мышкинский район',
9 => 'Некоузский район',
10 => 'Некрасовский район',
11 => 'Переславский район',
12 => 'Первомайский район',
13 => 'Пошехонский район',
14 => 'Ростовский район',
15 => 'Рыбинский район',
16 => 'Тутаевский район',
17 => 'Угличский район',
18 => 'Ярославский район',
19 => 'Город Переславль',
20 => 'Город Рыбинск'
);

14
src/data/city-short.php Normal file
View file

@ -0,0 +1,14 @@
<?php
$settings = array(
0 => '',
1 => 'г.',
2 => 'с.',
3 => 'п.',
4 => 'д.',
5 => 'пгт.',
6 => 'р.п.',
7 => 'ст.',
8 => 'а.'
);

14
src/data/city.php Normal file
View file

@ -0,0 +1,14 @@
<?php
// Массив типов поселений
$settings = array(
// 0 => '',
1 => 'город',
2 => 'село',
3 => 'поселок',
4 => 'деревня',
5 => 'поселок городского типа',
6 => 'рабочий поселок',
7 => 'станица',
8 => 'аул'
);

196
src/data/mime.php Normal file
View file

@ -0,0 +1,196 @@
<?php
/**
* http://www.w3schools.com/media/media_mimeref.asp
*/
$settings = array (
"" => "application/octet-stream",
"323" => "text/h323",
"acx" => "application/internet-property-stream",
"ai" => "application/postscript",
"aif" => "audio/x-aiff",
"aifc" => "audio/x-aiff",
"aiff" => "audio/x-aiff",
"asf" => "video/x-ms-asf",
"asr" => "video/x-ms-asf",
"asx" => "video/x-ms-asf",
"au" => "audio/basic",
"avi" => "video/x-msvideo",
"axs" => "application/olescript",
"bas" => "text/plain",
"bcpio" => "application/x-bcpio",
"bin" => "application/octet-stream",
"bmp" => "image/bmp",
"c" => "text/plain",
"cat" => "application/vnd.ms-pkiseccat",
"cdf" => "application/x-cdf",
"cer" => "application/x-x509-ca-cert",
"class" => "application/octet-stream",
"clp" => "application/x-msclip",
"cmx" => "image/x-cmx",
"cod" => "image/cis-cod",
"cpio" => "application/x-cpio",
"crd" => "application/x-mscardfile",
"crl" => "application/pkix-crl",
"crt" => "application/x-x509-ca-cert",
"csh" => "application/x-csh",
"css" => "text/css",
"dcr" => "application/x-director",
"der" => "application/x-x509-ca-cert",
"dir" => "application/x-director",
"dll" => "application/x-msdownload",
"dms" => "application/octet-stream",
"doc" => "application/msword",
"dot" => "application/msword",
"dvi" => "application/x-dvi",
"dxr" => "application/x-director",
"eps" => "application/postscript",
"etx" => "text/x-setext",
"evy" => "application/envoy",
"exe" => "application/octet-stream",
"fif" => "application/fractals",
"flr" => "x-world/x-vrml",
"gif" => "image/gif",
"gtar" => "application/x-gtar",
"gz" => "application/x-gzip",
"h" => "text/plain",
"hdf" => "application/x-hdf",
"hlp" => "application/winhlp",
"hqx" => "application/mac-binhex40",
"hta" => "application/hta",
"htc" => "text/x-component",
"htm" => "text/html",
"html" => "text/html",
"htt" => "text/webviewhtml",
"ico" => "image/x-icon",
"ief" => "image/ief",
"iii" => "application/x-iphone",
"ins" => "application/x-internet-signup",
"isp" => "application/x-internet-signup",
"jfif" => "image/pipeg",
"jpe" => "image/jpeg",
"jpeg" => "image/jpeg",
"jpg" => "image/jpeg",
"js" => "application/x-javascript",
"latex" => "application/x-latex",
"lha" => "application/octet-stream",
"lsf" => "video/x-la-asf",
"lsx" => "video/x-la-asf",
"lzh" => "application/octet-stream",
"m13" => "application/x-msmediaview",
"m14" => "application/x-msmediaview",
"m3u" => "audio/x-mpegurl",
"man" => "application/x-troff-man",
"mdb" => "application/x-msaccess",
"me" => "application/x-troff-me",
"mht" => "message/rfc822",
"mhtml" => "message/rfc822",
"mid" => "audio/mid",
"mny" => "application/x-msmoney",
"mov" => "video/quicktime",
"movie" => "video/x-sgi-movie",
"mp2" => "video/mpeg",
"mp3" => "audio/mpeg",
"mpa" => "video/mpeg",
"mpe" => "video/mpeg",
"mpeg" => "video/mpeg",
"mpg" => "video/mpeg",
"mpp" => "application/vnd.ms-project",
"mpv2" => "video/mpeg",
"ms" => "application/x-troff-ms",
"mvb" => "application/x-msmediaview",
"nws" => "message/rfc822",
"oda" => "application/oda",
"p10" => "application/pkcs10",
"p12" => "application/x-pkcs12",
"p7b" => "application/x-pkcs7-certificates",
"p7c" => "application/x-pkcs7-mime",
"p7m" => "application/x-pkcs7-mime",
"p7r" => "application/x-pkcs7-certreqresp",
"p7s" => "application/x-pkcs7-signature",
"pbm" => "image/x-portable-bitmap",
"pdf" => "application/pdf",
"pfx" => "application/x-pkcs12",
"pgm" => "image/x-portable-graymap",
"pko" => "application/ynd.ms-pkipko",
"pma" => "application/x-perfmon",
"pmc" => "application/x-perfmon",
"pml" => "application/x-perfmon",
"pmr" => "application/x-perfmon",
"pmw" => "application/x-perfmon",
"pnm" => "image/x-portable-anymap",
"pot," => "application/vnd.ms-powerpoint",
"ppm" => "image/x-portable-pixmap",
"pps" => "application/vnd.ms-powerpoint",
"ppt" => "application/vnd.ms-powerpoint",
"prf" => "application/pics-rules",
"ps" => "application/postscript",
"pub" => "application/x-mspublisher",
"qt" => "video/quicktime",
"ra" => "audio/x-pn-realaudio",
"ram" => "audio/x-pn-realaudio",
"ras" => "image/x-cmu-raster",
"rgb" => "image/x-rgb",
"rmi" => "audio/mid",
"roff" => "application/x-troff",
"rtf" => "application/rtf",
"rtx" => "text/richtext",
"scd" => "application/x-msschedule",
"sct" => "text/scriptlet",
"setpay" => "application/set-payment-initiation",
"setreg" => "application/set-registration-initiation",
"sh" => "application/x-sh",
"shar" => "application/x-shar",
"sit" => "application/x-stuffit",
"snd" => "audio/basic",
"spc" => "application/x-pkcs7-certificates",
"spl" => "application/futuresplash",
"src" => "application/x-wais-source",
"sst" => "application/vnd.ms-pkicertstore",
"stl" => "application/vnd.ms-pkistl",
"stm" => "text/html",
"svg" => "image/svg+xml",
"sv4cpio" => "application/x-sv4cpio",
"sv4crc" => "application/x-sv4crc",
"swf" => "application/x-shockwave-flash",
"t" => "application/x-troff",
"tar" => "application/x-tar",
"tcl" => "application/x-tcl",
"tex" => "application/x-tex",
"texi" => "application/x-texinfo",
"texinfo" => "application/x-texinfo",
"tgz" => "application/x-compressed",
"tif" => "image/tiff",
"tiff" => "image/tiff",
"tr" => "application/x-troff",
"trm" => "application/x-msterminal",
"tsv" => "text/tab-separated-values",
"txt" => "text/plain",
"uls" => "text/iuls",
"ustar" => "application/x-ustar",
"vcf" => "text/x-vcard",
"vrml" => "x-world/x-vrml",
"wav" => "audio/x-wav",
"wcm" => "application/vnd.ms-works",
"wdb" => "application/vnd.ms-works",
"wks" => "application/vnd.ms-works",
"wmf" => "application/x-msmetafile",
"wps" => "application/vnd.ms-works",
"wri" => "application/x-mswrite",
"wrl" => "x-world/x-vrml",
"wrz" => "x-world/x-vrml",
"xaf" => "x-world/x-vrml",
"xbm" => "image/x-xbitmap",
"xla" => "application/vnd.ms-excel",
"xlc" => "application/vnd.ms-excel",
"xlm" => "application/vnd.ms-excel",
"xls" => "application/vnd.ms-excel",
"xlt" => "application/vnd.ms-excel",
"xlw" => "application/vnd.ms-excel",
"xof" => "x-world/x-vrml",
"xpm" => "image/x-xpixmap",
"xwd" => "image/x-xwindowdump",
"z" => "application/x-compress",
"zip" => "application/zip",
);

35
src/data/okato.php Normal file
View file

@ -0,0 +1,35 @@
<?php
// В ОКАТО приняты следующие сокращения:
// http://www.consultant.ru/online/base/?req=doc;base=LAW;n=62484
$settings = array (
"р" => "район",
"г" => "город",
"пгт" => "поселок городского типа",
"рп" => "рабочий поселок",
"кп" => "курортный поселок",
"к" => "кишлак",
"пс" => "поселковый совет",
"сс" => "сельсовет",
"смн" => "сомон",
"вл" => "волость",
"дп" => "дачный поселковый совет",
"п" => "поселок сельского типа",
"нп" => "населенный пункт",
"п. ст" => "поселок при станции",
"ж/д ст" => "железнодорожная станция",
"с" => "село",
"м" => "местечко",
"д" => "деревня",
"сл" => "слобода",
"ст" => "станция",
"ст-ца" => "станица",
"х" => "хутор",
"у" => "улус",
"рзд" => "разъезд",
"клх" => "колхоз",
"им" => "имени",
"свх" => "совхоз",
"зим" => "зимовье",
);

91
src/data/regions.php Normal file
View file

@ -0,0 +1,91 @@
<?php
$settings = array (
0 => '',
2 => 'Алтайский край',
3 => 'Амурская область',
4 => 'Архангельская область',
5 => 'Астраханская область',
6 => 'Белгородская область',
7 => 'Брянская область',
8 => 'Владимирская область',
9 => 'Волгоградская область',
10 => 'Вологодская область',
11 => 'Воронежская область',
12 => 'г. Москва',
13 => 'г. Санкт-Петербург',
14 => 'Еврейская автономная область',
84 => 'Забайкальский край',
15 => 'Ивановская область',
16 => 'Иркутская область',
17 => 'Кабардино-Балкарская Республика',
18 => 'Калининградская область',
19 => 'Калужская область',
20 => 'Камчатский край',
21 => 'Карачаево-Черкесская Республика',
22 => 'Кемеровская область',
23 => 'Кировская область',
28 => 'Костромская область',
29 => 'Краснодарский край',
30 => 'Красноярский край',
31 => 'Курганская область',
32 => 'Курская область',
33 => 'Ленинградская область',
34 => 'Липецкая область',
35 => 'Магаданская область',
36 => 'Московская область',
37 => 'Мурманская область',
38 => 'Ненецкий автономный округ',
39 => 'Нижегородская область',
40 => 'Новгородская область',
41 => 'Новосибирская область',
42 => 'Омская область',
43 => 'Оренбургская область',
44 => 'Орловская область',
45 => 'Пензенская область',
46 => 'Пермский край',
47 => 'Приморский край',
48 => 'Псковская область',
49 => 'Республика Адыгея',
50 => 'Республика Алтай',
51 => 'Республика Башкортостан',
52 => 'Республика Бурятия',
25 => 'Республика Дагестан',
26 => 'Республика Ингушетия',
53 => 'Республика Калмыкия',
54 => 'Республика Карелия',
55 => 'Республика Коми',
56 => 'Республика Марий Эл',
57 => 'Республика Мордовия',
58 => 'Республика Саха(Якутия)',
59 => 'Республика Северная Осетия-Алания',
60 => 'Республика Татарстан',
61 => 'Республика Тыва',
62 => 'Республика Хакасия',
63 => 'Ростовская область',
64 => 'Рязанская область',
65 => 'Самарская область',
66 => 'Саратовская область',
67 => 'Сахалинская область',
68 => 'Свердловская область',
69 => 'Смоленская область',
70 => 'Ставропольский край',
71 => 'Таймырский (Долгано-Ненецкий) автономный округ',
72 => 'Тамбовская область',
73 => 'Тверская область',
74 => 'Томская область',
75 => 'Тульская область',
76 => 'Тюменская область',
77 => 'Удмуртская Республика',
78 => 'Ульяновская область',
80 => 'Хабаровский край',
81 => 'Ханты-Мансийский автономный округ',
82 => 'Челябинская область',
83 => 'Чеченская Республика',
85 => 'Чувашская Республика',
86 => 'Чукотский автономный округ',
87 => 'Эвенкийский автономный округ',
88 => 'Ямало-Ненецкий автономный округ',
89 => 'Ярославская область'
);

32
src/data/states.php Normal file
View file

@ -0,0 +1,32 @@
<?php
$settings = array (
0 => array('title' => ''),
1 => array(
'title' => 'Центральный федеральный округ',
'short' => 'ЦФО',
'regions' => array(6, 7, 8, 11, 12, 15, 19, 28, 32, 34, 36, 44, 64, 69, 72, 73, 75, 89)),
2 => array(
'title' => 'Южный федеральный округ',
'short' => 'ЮФО',
'regions' => array(5, 9, 17, 21, 29, 49, 25, 26, 53, 59, 63, 70, 83)),
3 => array(
'title' => 'Северо-западный федеральный округ',
'short' => 'СЗФО',
'regions' => array(4, 10, 13, 18, 33, 37, 38, 40, 48, 54, 55)),
4 => array(
'title' => 'Дальневосточный федеральный округ',
'short' => 'ДФО',
'regions' => array(3, 14, 20, 35, 47, 58, 67, 80, 86)),
5 => array(
'title' => 'Сибирский федеральный округ',
'short' => 'СФО',
'regions' => array(2, 16, 22, 30, 41, 42, 50, 52, 61, 62, 71, 74, 84, 86, 87)),
6 => array(
'title' => 'Уральский федеральный округ',
'short' => 'УФО',
'regions' => array(31, 68, 76, 81, 82, 88)),
7 => array(
'title' => 'Приволжский федеральный округ',
'short' => 'ПФО',
'regions' => array(23, 39, 43, 45, 46, 51, 56, 57, 60, 65, 66, 77, 78, 85)));

408
src/database.php Normal file
View file

@ -0,0 +1,408 @@
<?php
/**
* @package system.db
* Класс оболочка для PDO для замены Creole
*/
class Database extends PDO
{
public function __construct($dsn, $username = false, $password = false)
{
parent::__construct($dsn, $username, $password);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDODatabaseStatement', array()));
}
public function getDSN()
{
return $this->dsn;
}
public function isPostgres(){
return ($this->dsn["phptype"] == "pgsql");
}
/**
* Создает соединение с базой данных
*/
static function getConnection(array $dsn)
{
if ($dsn['phptype'] == 'pgsql' || $dsn['phptype'] == 'mysql') {
$port = (isset($dsn['port'])) ? "port={$dsn['port']};" : "";
$connection = new Database("{$dsn['phptype']}:host={$dsn['hostspec']}; $port dbname={$dsn['database']}", $dsn['username'], $dsn['password']);
$connection->query('SET client_encoding = "UTF-8"');
}
if ($dsn['phptype'] == 'sqlite') {
$connection = new Database("{$dsn['phptype']}:{$dsn['database']}");
}
$connection->dsn = $dsn;
return $connection;
}
public function executeQuery($query)
{
$stmt = $this->prepare($query);
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$stmt->execute();
$stmt->cache = $stmt->fetchAll();
return $stmt;//$sth->fetchAll();
}
public function prepareStatement($query)
{
return new DatabaseStatement($query, $this);
}
// Для совместимости со старым представлением баз данных CIS
/**
* Извлекает из базы все элементы по запросу
*/
public function fetchAllArray($query,$values=null)
{
$sth = $this->prepare($query);
$prep = $this->prepareValues($values);
$sth->execute($prep);
return $sth->fetchAll(PDO::FETCH_ASSOC);
}
/**
* Извлекает из базы первый элемент по запросу
*/
public function fetchOneArray($query,$values=null)
{
$sth = $this->prepare($query);
$prep = $this->prepareValues($values);
$sth->execute($prep);
return $sth->fetch(PDO::FETCH_ASSOC);
}
private function assignQuote($x, $y)
{
return $x . "=" . $this->quote($y);
}
private function prepareValues($values)
{
if (!$values) {
return null;
}
$pg = $this->isPostgres();
$prep = array();
foreach ($values as $key => $value) {
$result = null;
if(is_bool($value)) {
if ($pg) {
$result = $value ? 'true' : 'false';
} else {
$result = $value ? 1 : 0;
}
} else {
$result = $value;
}
$prep[":" . $key] = $result;
}
return $prep;
}
/**
* Создает INSERT запрос
*/
function insertQuery($table, array $values, $return_id = false, $index = null)
{
$prep = $this->prepareValues($values);
$sql = "INSERT INTO $table (" . implode(",", array_keys($values))
. ") VALUES (" . implode(",", array_keys($prep)). ")";
if($return_id){
if ($this->isPostgres()){
$sql = $sql." RETURNING $index";
}
}
$stmt = $this->prepare($sql);
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$stmt->execute($prep);
$result = $stmt->fetch();
if ($return_id) {
if ($this->isPostgres()) {
return $result[$index];
} else {
$result = $this->fetchOneArray("SELECT $index AS lastid FROM $table WHERE OID = last_insert_rowid()");
return $result['lastid'];
}
}
}
/**
* Создает UPDATE запрос
*/
function updateQuery($table, array $values, $cond)
{
return $this->query("UPDATE $table SET " . implode(",",
array_map(array($this, 'assignQuote'), array_keys($values), array_values($values))) . " WHERE $cond");
}
function getIdGenerator() {
return new IdGenerator($this);
}
/**
* Замечание: Только для Postgres SQL
* @param string $seq Имя последовательности для ключа таблицы
* @return int Идентефикатор следующей записи
*/
function getNextId($seq)
{
$result = $this->fetchOneArray("SELECT nextval('$seq')");
return $result['nextval'];
}
function close()
{
return null;
}
}
class IdGenerator {
private $db;
function __construct($db) {
$this->db = $db;
}
function isBeforeInsert() {
return false;
}
function isAfterInsert() {
return true;
}
function getId($seq) {
$result = $this->db->fetchOneArray("SELECT nextval('$seq')");
return $result['nextval'];
// $result = $this->db->fetchOneArray("SELECT last_insert_rowid() AS nextval");
// return $result['nextval'];
}
}
class PDODatabaseStatementIterator implements Iterator
{
private $result;
private $pos = 0;
private $fetchmode;
private $row_count;
private $rs;
/**
* Construct the iterator.
* @param PgSQLResultSet $rs
*/
public function __construct($rs)
{
$this->result = $rs;
$this->row_count = $rs->getRecordCount();
}
function rewind()
{
$this->pos = 0;
}
function valid()
{
return ($this->pos < $this->row_count);
}
function key()
{
return $this->pos;
}
function current()
{
if (!isset($this->result->cache[$this->pos])) {
$this->result->cache[$this->pos] = $this->result->fetch(PDO::FETCH_ASSOC);
}
return $this->result->cache[$this->pos];
}
function next()
{
$this->pos++;
}
function seek ( $index )
{
$this->pos = $index;
}
function count ( ) {
return $this->row_count;
}
}
class PDODatabaseStatement extends PDOStatement implements IteratorAggregate
{
protected $cursorPos = 0;
public $cache = array();
public $fields;
function getIterator()
{
return new PDODatabaseStatementIterator($this);
}
protected function __construct() {
}
function rewind()
{
$this->cursorPos = 0;
}
public function seek($rownum)
{
if ($rownum < 0) {
return false;
}
// PostgreSQL rows start w/ 0, but this works, because we are
// looking to move the position _before_ the next desired position
$this->cursorPos = $rownum;
return true;
}
function valid()
{
return ( true );
}
public function first()
{
if($this->cursorPos !== 0) { $this->seek(0); }
return $this->next();
}
function next()
{
if ($this->getRecordCount() > $this->cursorPos) {
if (!isset($this->cache[$this->cursorPos])) {
$this->cache[$this->cursorPos] = $this->fetch(PDO::FETCH_ASSOC);
}
$this->fields = $this->cache[$this->cursorPos];
$this->cursorPos++;
return true;
} else {
$this->fields = null;
return false;
}
}
function key() {
return $this->cursorPos;
}
function current()
{
return $this->result->fetch(PDO::FETCH_ASSOC);
}
function getRow()
{
return $this->fields;
}
function getInt($name)
{
return intval($this->fields[$name]);
}
function getBlob($name)
{
return $this->fields[$name];
}
function getString($name)
{
return $this->fields[$name];
}
function getBoolean($name)
{
return (bool)$this->fields[$name];
}
function get($name)
{
return $this->fields[$name];
}
function getRecordCount()
{
return count($this->cache);
}
}
/**
* Класс оболочка для PDOStatement для замены Creole
*/
class DatabaseStatement
{
protected $limit = null;
protected $offset = null;
protected $statement = null;
protected $binds = array();
protected $conn;
protected $query;
function __construct($query, $conn) {
$this->query = $query;
$this->conn = $conn;
}
function setInt($n, $value)
{
$this->binds [] = array($n, $value, PDO::PARAM_INT);
}
function setString($n, $value)
{
$this->binds [] = array($n, $value, PDO::PARAM_STR);
}
function setBlob($n, $value)
{
$this->binds [] = array($n, $value, PDO::PARAM_LOB);
}
function setLimit($limit)
{
$this->limit = $limit;
}
function setOffset($offset)
{
$this->offset = $offset;
}
function executeQuery()
{
if ($this->limit) {
$this->query .= " LIMIT {$this->limit} OFFSET {$this->offset}";
}
$stmt = $this->conn->prepare($this->query);
foreach ($this->binds as $bind) {
list($n, $value, $type) = $bind;
$stmt->bindValue($n, $value, $type);
}
$stmt->setFetchMode(PDO::FETCH_ASSOC);
$stmt->execute();
$stmt->cache = $stmt->fetchAll();
return $stmt;
}
}

86
src/error.php Normal file
View file

@ -0,0 +1,86 @@
<?php
// Report simple running errors
define('ERROR_FILE', 'phedor@edu.yar.ru');
define('ERROR_TYPE', 1);
ini_set('error_reporting', 0);
ini_set('display_errors', 0);
error_reporting(0);
function get_error_name($id) {
$names = array(1 => 'E_ERROR',
2 => 'E_WARNING',
4 => 'E_PARSE',
8 => 'E_NOTICE',
16 => 'E_CORE_ERROR',
32 => 'E_CORE_WARNING',
64 => 'E_COMPILE_ERROR',
128 => 'E_COMPILE_WARNING',
256 => 'E_USER_ERROR',
512 => 'E_USER_WARNING',
1024 => 'E_USER_NOTICE',
2048 => 'E_STRICT',
4096 => 'E_RECOVERABLE_ERROR',
8192 => 'E_DEPRECATED',
16384 => 'E_USER_DEPRECATED',
30719 => 'E_ALL');
return $names[$id];
}
function send_error($description) {
$message = "request: " . "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'] . "\n";
$message .= "from: " . $_SERVER['REMOTE_ADDR'] . "\n";
if(isset($_SERVER['HTTP_REFERER'])) $message .= "referer: " . $_SERVER['HTTP_REFERER'] . "\n";
$message .= "post:" . var_export($_POST, true) . "\n";
$message .= "description: " . $description;
// error_log($message, ERROR_TYPE, ERROR_FILE);
return false;
}
function error_handler($errno, $errstr, $errfile, $errline) {
$message = "request: " . "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'] . "\n";
$message .= "from: " . $_SERVER['REMOTE_ADDR'] . "\n";
if(isset($_SERVER['HTTP_REFERER'])) $message .= "referer: " . $_SERVER['HTTP_REFERER'] . "\n";
$message .= "post:" . var_export($_POST, true) . "\n";
$message .= "type: " . get_error_name($errno) . "\n"
. "error: " . $errstr . "\n"
. "line: " . $errline . "\n"
. "file: " . $errfile;
// error_log($message, ERROR_TYPE, ERROR_FILE);
return false;
}
function shutdown() {
$error = error_get_last();
$message = "request: " . "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'] . "\n";
$message .= "from: " . $_SERVER['REMOTE_ADDR'] . "\n";
if(isset($_SERVER['HTTP_REFERER'])) $message .= "referer: " . $_SERVER['HTTP_REFERER'] . "\n";
if (is_array($error)) {
foreach ($error as $info => $string) {
$message .= "{$info}: {$string}\n";
}
}
error_log($message, ERROR_TYPE, ERROR_FILE);
}
function exception_handler($exception) {
$message = "request: " . "http://".$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'] . "\n";
$message .= "from: " . $_SERVER['REMOTE_ADDR'] . "\n";
if(isset($_SERVER['HTTP_REFERER'])) $message .= "referer: " . $_SERVER['HTTP_REFERER'] . "\n";
$message .=
"file: " . $exception->getFile() . "\n"
. "line: " . $exception->getLine() . "\n"
. "message: " . $exception->getMessage() . "\n"
. "trace: " . $exception->getTraceAsString();
error_log($message, ERROR_TYPE, ERROR_FILE);
return true;
}
set_exception_handler('exception_handler');
set_error_handler("error_handler");
//register_shutdown_function('shutdown');

80
src/file.php Normal file
View file

@ -0,0 +1,80 @@
<?php
require_once 'core/sort.php';
//Становиться похоже на работу файлов через SPL возможно стоит реализовать на базе его
class FileRecord
{
public $file;
protected $src;
protected $parent;
function __construct(array $file, $src, $parent = false)
{
$this->file = $file;
$this->filename = $src;
$this->parent = $parent;
}
function get($name) {
return isset($this->file[$name]) ? $this->file[$name] : null;
}
function fileStat()
{
$type = is_dir($this->filename);
return array(
'name' => ($this->parent) ? ".." : $this->getName(),
'type' => $type,
'extension' => ($type) ? 'folder' : pathinfo($this->filename, PATHINFO_EXTENSION),
'date' => date("d.m.Y H:i", $this->getTime()),
'access' => 0,
'size' => ($type) ? "" : $this->getSizeString(),
'state' => isset($this->file['state']) ? $this->file['state'] : 'unknown',
'title' => $this->getTitle(),
/*'author' => $this->file['author'],
'description' => $this->file['description'],
'keywords' => $this->file['keywords'],*/
);
}
function isExpected()
{
if (isset($this->file['state'])) {
return ($this->file['state'] == 'expected');
}
return false;
}
function getSizeString()
{
$size = $this->getSize();
foreach (array('б ', 'Kб', 'Mб') as $suffix) {
if (($size / 1024) <= 1) {
return round($size, 0) . ' ' . $suffix;
}
$size /= 1024;
}
return round($size, 0) . ' GB';
}
function getSize()
{
return ($this->isExpected()) ? 0 : filesize($this->filename);
}
function getTime()
{
return ($this->isExpected()) ? 0 : filemtime($this->filename);
}
function getName()
{
return pathinfo($this->filename, PATHINFO_BASENAME);
}
function getTitle()
{
return isset($this->file['title']) ? $this->file['title'] : $this->getName();
}
}

View file

@ -0,0 +1,34 @@
<?php
/**
* Фильтр действий
*/
class ActionAccess
{
public $access = array();
function __construct($processor)
{
$this->processor = $processor;
}
/**
* Проверка доступных действий для пользователя
* !! Реализация класса проверки действий не должна быть внутри Контроллера!!!
* Информация о доступе может быть в файле, базе данных и т.д.
*/
function checkAction($action)
{
// Импликация !! http://ru.wikipedia.org/wiki/Импликация
return (!isset($this->access[$action]) || in_array(UserAccess::$access, $this->access[$action]));
}
function execute(HTTPRequest $request)
{
$action = $request->getAction();
if(! $this->checkAction($action)) {
$request->set('action', 'index');
}
return $this->processor->execute($request);
}
}

View file

@ -0,0 +1,26 @@
<?php
require_once 'core/path.php';
class ActionLogger
{
public $before = array ();
public $file;
public $action;
public $processor;
function __construct($processor)
{
$this->processor = $processor;
$this->file = fopen(Shortcut::getUrl('access.log'), "a");
}
function execute(HTTPRequest $request)
{
$action = $request->getAction();
if(in_array($action, $this->before)) {
fwrite($this->file, "time: " . date("r", time()) . " query: ". json::encode(array_merge($_POST, $_GET)) . " by: " . UserAccess::$name . "\n");
}
return $this->processor->execute($request);
}
}

28
src/filter/filterbase.php Normal file
View file

@ -0,0 +1,28 @@
<?php
/**
* Попытка реализовать фильтр для запросов
*/
class Filter_Filter
{
public $processor;
public function __construct($processor)
{
$this->processor = $processor;
}
public function execute(HttpRequest $request)
{
return $this->processor->execute($request);
}
public function getView($name)
{
return $this->processor->getView($name);
}
public function getConnection()
{
return $this->processor->getConnection();
}
}

105
src/filter/filterlogin.php Normal file
View file

@ -0,0 +1,105 @@
<?php
/**
* Фильтр для проверки авторизации
*
* action: login(password, login)
* action: logout()
*/
// В класс авторизации передавать обьект для управления пользователем
// Вынести в отдельный файл
class Filter_LoginFilter extends Filter_Filter
{
const SESSION_BROWSER_SIGN_SECRET = '@w3dsju45Msk#';
const SESSION_BROWSER_SIGN_KEYNAME = 'session.app.browser.sign';
public $mode = 'ajax';
/**
* Проверка авторизации
* @return Boolean Авторизовани пользователь или нет
*/
public function isLoggin(HttpRequest $request)
{
// Авторизация
session_start();
$db = $this->getConnection();
UserAccess::setUp($db); // Соединение
switch ($request->getAction()) {
// Авторизация по постоянному паролю
case 'login':
$login = $request->get('login');
$password = $request->get('password');
$result = UserAccess::getUserByLogin($login); // Поиск по логину
if ($result) {
if (md5($password) == $result->getString('password')) { // password
$this->enter($db, $result);
return true;
}
}
$request->set('error', true);
break;
case 'logout': // Выход
session_destroy();
break;
// Вход по временному паролю
case 'enter':
$login = $request->get('login');
$password = $request->get('sid');
$result = UserAccess::getUserByLogin($login); // Поиск по логину
if ($result) {
$temp = md5($result->getString('password') . $result->getString('login') . $result->getString('sid'));
if ($password == $temp) {
$this->enter($db, $result);
return true;
}
}
break;
default:
$hash = $this->getBrowserSign();
// Если $hash не совпадает $_SESSION['hash'] то удаляем сессию
if (isset($_SESSION ['access']) && isset($_SESSION[self::SESSION_BROWSER_SIGN_SECRET])) {
if ($hash == $_SESSION[self::SESSION_BROWSER_SIGN_SECRET]) {
UserAccess::getUserById($_SESSION ['access']); // Поиск по идентификатору
return true;
} else {
session_destroy();
}
}
}
return false;
}
private function getBrowserSign()
{
$rawSign = self::SESSION_BROWSER_SIGN_SECRET;
$signParts = array('HTTP_USER_AGENT', 'HTTP_ACCEPT_ENCODING');
$rawSign = '';
foreach ($signParts as $signPart) {
$rawSign .= '::' . (isset($_SERVER[$signPart]) ? $_SERVER[$signPart] : 'none');
}
return md5($rawSign);
}
private function enter($db, $result)
{
$db->executeQuery("UPDATE users SET sid = '' WHERE id_user = " . $result->getInt('id_user'));
$_SESSION ["group"] = $result->getInt('access');
$_SESSION ["access"] = $result->getInt('id_user'); // id_user
$_SESSION [self::SESSION_BROWSER_SIGN_SECRET] = $this->getBrowserSign();
$_SESSION ["time"] = time();
}
public function execute(HttpRequest $request)
{
if (!$this->isLoggin($request)) {
// Параметры при неправильной авторизации
// Действия по умолчанию !! Возможно переход на форму регистрации
$request->set('module', 'login');
$request->set('mode', $this->mode);
}
return $this->processor->execute($request);
}
}

72
src/filter/useraccess.php Normal file
View file

@ -0,0 +1,72 @@
<?php
require_once 'filterbase.php';
require_once 'filterlogin.php';
// Класс должен быть в библиотеке приложения
class UserAccess
{
const LIFE_TIME = 1800; // = 30min * 60sec;
static $fullname;
static $name;
static $access;
static $password;
static $id;
static $db;
protected function __construct()
{
}
public static function setUp($db)
{
self::$db = $db;
}
public static function getUserByQuery($stmt)
{
global $GROUPS;
$result = $stmt->executeQuery();
if ($result->next()) {
self::$access = $GROUPS[$result->getString('access')];
self::$name = $result->getString('login');
self::$id = $result->getInt('id_user');
self::$password = $result->getString('password');
self::$fullname = implode(' ', array(
$result->getString('surname'),
$result->getString('firstname'),
$result->getString('patronymic')));
return $result;
}
return null;
}
public static function getUserByLogin($login)
{
$stmt = self::$db->prepareStatement("SELECT * FROM users WHERE login = ?");
$stmt->setString(1, $login);
$result = self::getUserByQuery($stmt);
if ($result) {
$time = time();
$id = self::$id;
self::$db->executeQuery("UPDATE users SET lasttime = $time WHERE id_user = $id"); // Время входа
}
return $result;
}
public static function getUserById($id)
{
$stmt = self::$db->prepareStatement("SELECT * FROM users WHERE id_user = ?");
$stmt->setInt(1, $_SESSION ['access']);
$result = self::getUserByQuery($stmt);
if ($result) {
$lasttime = $result->getInt('lasttime');
$time = time();
if ($time - $lasttime > self::LIFE_TIME) return null; // Вышло время сессии
$id = self::$id;
$stmt = self::$db->executeQuery("UPDATE users SET lasttime = $time WHERE id_user = $id"); // Время последнего обращения входа
}
return $result;
}
}

178
src/form/form.php Normal file
View file

@ -0,0 +1,178 @@
<?php
/**
* Элемент формы
* @package core
*/
class TField
{
public $name;
public $label; // Метка поля
public $value; // Значение поля
public $type; // Каждому типу элемента соответствует макрос TAL
public $error_msg = null;
public $error = false;
public $require = false;
public function __construct ($input)
{
$this->deafult = null;
if (isset($input['validate'])) {
$this->require = strpos($input['validate'], 'require') !== false;
}
// Инициализация свойст обьетка
foreach (array('label', 'name', 'type') as $name) {
$this->$name = $input[$name];
}
}
function setValue($value)
{
$this->value = $value;
}
}
/**
* Поле ввода Input
* @package core
*/
class TInput extends TField {
}
/**
* Выбор из одного элемента
*/
class TSelect1 extends TField
{
public $options = array ();
public function __construct ($input) {
parent::__construct($input);
$this->options = $input['options'];
}
function setValue($value)
{
// Установить selected у options
$this->value = $value;
}
}
/**
* Поле с датой
* @package core
*/
class TDate extends TField
{
}
/* *
* Текстовое поле
* @package core
*/
class TTextArea extends TField
{
}
/**
* Поле для ввода пароля
* @package core
*/
class TSecret extends TField
{
}
class TUpload extends TField
{
}
/**
* Форма для ввода
* @package core
*/
class TForm
{
public $field = array ();
public $action = "";
public $method = 'post';
protected $replace;
protected $before;
public function __construct ()
{
$this->constructor = array (
'input' => 'TInput',
'date' => 'TDate',
'textarea' => 'TTextArea',
'select' => 'TSelect',
'select1' => 'TSelect1',
'secret' => 'TSecret',
'upload' => 'TUpload'
);
}
/**
* Добавляет одно поле ввода на форму
*/
public function addField (array $init)
{
assert (isset($init['type']));
assert (isset($init['name']));
$constructor = $this->constructor[$init['type']];
$el = new $constructor ($init);
$el->type = $init['type'];
$this->field [$init['name']] = $el;
return $el;
}
/**
* Добавляет спсок полей для формы
* @param array $list
*/
public function addFieldList (array $list)
{
foreach ($list as $init) {
$this->addField ($init);
}
}
/**
* Устанавливает ошибки после проверки
*/
function setError (Validator $validator)
{
foreach ($validator->getErrorMsg() as $name => $error)
{
$this->field[$name]->error = true;
$this->field[$name]->error_msg = $error;
}
}
/**
* Устанавливает значения из масива
*/
function setValues (Collection $request) {
foreach ($this->field as $key => $el) {
$value = $request->getRawData ($this->method, $key);
$this->field[$key]->setValue($value);
}
}
/**
* Заполняет форму данными из обьекта
* @param object $data
* @param array $schema Связь между элементами формы и свойствами обьекта
*/
public function fill ($data, array $schema)
{
foreach ($schema as $key => $value) {
$this->field [$value]->setValue($data->$value->getString ());
}
}
public function set($name, $value)
{
$this->field[$name]->setValue($value);
}
}

43
src/form/viewstate.php Normal file
View file

@ -0,0 +1,43 @@
<?php
/**
* http://www.alternateinterior.com/2006/09/a-viewstate-for-php.html
* Управление состоянием между страницами
*/
class ViewState // extends Collection
{
private $values = array();
function set($name, $value)
{
$this->values[$name] = $value;
}
function get()
{
$args = func_get_args();
$result = $this->values;
foreach ($args as $name) {
if (!isset($result[$name])) {
return null;
}
$result = $result[$name];
}
return $result;
}
function saveState()
{
return base64_encode(serialize($this->values));
}
function restoreState($value)
{
$this->values = unserialize(base64_decode($value));
}
function export()
{
return $this->values;
}
}

52
src/formats/dot.php Normal file
View file

@ -0,0 +1,52 @@
<?php
/**
* Генерация файлов Grpahviz dot
*/
class Dot
{
static function getHeader ()
{
$header =
"digraph G {\n"
. "\toverlap=false; splines=true;\n"
. "\tfontname = \"Verdana\"\n"
. "\tfontsize = 8\n"
. "\tnode [\n"
. "\t\tfontname = \"Verdana\"\n"
. "\t\tfontsize = 8\n"
. "\t\tshape = \"record\"\n"
. "\t]\n"
. "\tedge [\n"
. "\t\tfontname = \"Verdana\"\n"
. "\t\tfontsize = 8\n"
. "\t]\n";
return $header;
}
static function getFooter ()
{
$footer = "}\n";
return $footer;
}
function assocToDot (array $array, array $label)
{
$result = array (self::getHeader());
// Метки
foreach ($label as $value) {
$result [] = "\t\"{$value[0]}\" [ label = \"{$value[1]}\" ] \n";
}
// Ассоциации
foreach ($array as $key => $value) {
foreach ($value as $n) {
$result [] = "\t\"$key\" -> \"$n\"\n";
}
}
$result [] = self::getFooter();
return implode("", $result);
}
}
?>

85
src/formats/helix.php Normal file
View file

@ -0,0 +1,85 @@
<?php
define ('URL_CLIP', 0);
define ('URL_DIRECTORY', 1);
class HAuthorize {
public $password;
public $uuid;
public $uuid_writeable;
public function __construct () {
$this->uuid = "*";
$this->uuid_writeable = 1;
}
public function getString () {
return "{$this->password};{$this->uuid};{$this->uuid_writeable}";
}
public function setPassword ($username, $realm, $password) {
$this->password = md5 ("$username:$realm:$password");
}
}
class HPermission {
public $url;
public $url_type;
public $permission_type;
public $expires;
public $debitted_time;
public function __construct ($url, $url_type) {
$this->url = $url;
$this->url_type = $url_type;
$this->expires = "*"; // MM/DD/YYYY:HH:MM:SS
$this->permission_type = 0;
$this->debitted_time = 0;
}
public function getString () {
return "{$this->url};{$this->url_type};{$this->permission_type};{$this->expires};{$this->debitted_time}";
}
}
class HFile {
public $authorize;
public $permission = array ();
public function addAuthorize ($name, $realm, $password) {
$this->authorize = new HAuthorize ();
$this->authorize->setPassword ($name, $realm, $password);
}
public function addPermission ($url, $url_type) {
$this->permission[] = new HPermission ($url, $url_type);
}
public function write ($name) {
$file = fopen ($name, 'w');
fwrite ($file, $this->getString ());
fclose ($file);
}
public function writeFTP ($ftp, $path) {
$file = tmpfile ();
fwrite ($file, $this->getString ());
fseek ($file, 0);
ftp_fput ($ftp, $path, $file, FTP_BINARY);
fclose ($file);
}
public function getString () {
$result = array ();
$result[] = $this->authorize->getString ();
$result[] = "\n";
foreach ($this->permission as $p) {
$result[] = $p->getString ();
$result[] = "\n";
}
return implode ("", $result);
}
}

346
src/functions.php Normal file
View file

@ -0,0 +1,346 @@
<?php
/**
* Функциональное программирование в PHP
* package functional
*/
/**
* Эмуляция каррированой функции
*/
class __right {
protected $params;
protected $fn;
public function __construct($params) {
$this->fn = array_shift($params);
$this->params = $params;
}
function apply() {
$params = func_get_args();
array_splice($params, count($params), 0, $this->params);
return call_user_func_array($this->fn, $params);
}
}
class __left {
protected $params;
protected $fn;
public function __construct($params) {
$this->fn = array_shift($params);
$this->params = $params;
}
function apply() {
$params = func_get_args();
array_splice ($params, 0, 0, $this->params);
return call_user_func_array ($this->fn, $params);
}
}
define('__', '_ARGUMENT_PLACE_');
class __partial {
protected $params;
protected $fn;
public function __construct($params) {
$this->fn = array_shift($params);
$this->params = $params;
}
function apply() {
$params = func_get_args();
$result = array();
for($i = 0, $j = 0; $i < count($this->params); $i++) {
if ($this->params[$i] == __) {
$result [] = $params[$j];
$j++;
} else {
$result [] = $this->params[$i];
}
}
return call_user_func_array ($this->fn, $result);
}
}
function partial() {
$closure = new __partial(func_get_args());
return array($closure, 'apply');
}
/**
* Композиция функций
*/
class __compose {
protected $fns;
function __construct($list) {
$this->fns = array_reverse($list);
}
function apply () {
$params = func_get_args ();
$result = call_user_func_array($this->fns[0], $params);
for ($i = 1; $i < count($this->fns); $i++) {
$result = call_user_func($this->fns[$i], $result);
}
return $result;
}
}
/**
* Композиция функций
* @param mixed $a
* @param mixed $b
*
* @return array[int]mixed
*/
function compose() {
$closure = new __compose(func_get_args());
return array($closure, 'apply');
}
/**
* Карирование справа
*
* @return array[int]mixed
*/
function rcurry() {
$closure = new __right(func_get_args ());
return array($closure, 'apply');
}
/**
* Карирование слева
*
* @return array[int]mixed
*/
function lcurry() {
$closure = new __left(func_get_args ());
return array($closure, 'apply');
}
/**
* Разделение массива на два по условию
* @param mixed $pred Условие по которому разделяется массив
* @param array $lst
*
* @return array[int]mixed
*/
function partition($pred, $lst) {
$left = array ();
$right = array ();
foreach ($lst as $n) {
if (call_user_func($pred, $n) !== false) {
$left [] = $n;
} else {
$right [] = $n;
}
}
return array ($left, $right);
}
/**
* @param array $value
* @param string $name
*
* @return mixed
*/
function __key($value, $name) {
return $value[$name];
}
function identity($value) {
return $value;
}
/**
* @param array $a
* @param array $b
* @param $key
*
* @return int
*/
function __cmp($a, $b, $key) {
if ($a[$key] == $b[$key]) {
return 0;
}
return ($a[$key] > $b[$key]) ? -1 : 1;
}
function __cmp_less($a, $b, $key) {
if ($a[$key] == $b[$key]) {
return 0;
}
return ($a[$key] < $b[$key]) ? -1 : 1;
}
// Сравнение по ключу массиве
function __index($n, $key, $row) {
return ($row[$key] == $n);
}
function __div($x, $y) {
return $x / $y;
}
function __self($name, $o) {
return call_user_func(array($o, $name));
}
function concat(/* $args ...*/) {
$args = func_get_args();
return implode($args);
}
function __empty($x) {
return empty($x);
}
// Отрицание
function __not($x) {
return !$x;
}
// Не равно
function __neq($x, $y) {
return $x != $y;
}
// Равно
function __eq($x, $y) {
return $x == $y;
}
/**
* Извлекает из многомерого массива значения с определенным ключом
* @example key_values('a', array(1 => array('a' => 1, 'b' => 2))) => array(1)
*
* @return mixed
*/
function key_values($key, /*array|ArrayIterator*/ $array) {
$result = array();
foreach($array as $item) {
$result[] = $item[$key];
}
return $result;
}
function assoc_key_values($key, $value, $array) {
$result = array();
foreach ($array as $item) {
$result[$item[$key]] = $item[$value];
}
return $result;
}
function assoc_key($key, $array) {
$result = array();
foreach ($array as $item) {
$result[$item[$key]] = $item;
}
return $result;
}
function _get($key, $value, $array) {
foreach ($array as $item) {
if ($item[$key] == $value) return $item;
}
return null;
}
function _get_key($key, $value, $array) {
foreach ($array as $name => $item) {
if ($item[$key] == $value) return $name;
}
return null;
}
/**
* Логическа операция && ко всем элементам массива
* @return bool
*/
function every(array $array, $callback) {
foreach ($array as $key => $value) {
if (call_user_func($callback, $value) === false) {
return false;
}
}
return true;
}
/**
* Логическа операция || ко всем элементам массива
* @param array $array
* @param mixed $callback
*
* @return mixed
*/
function some(array $array, $callback) {
assert(is_callable($callback));
foreach ($array as $key => $value) {
if (call_user_func($callback, $value) === true) {
return $key;
}
}
return false;
}
function span($length, array $array) {
assert(is_int($length));
$result = array();
for($i = 0; $i < count($array); $i += $length) {
$result [] = array_slice($array, $i, $length);
}
return $result;
}
function array_ref($data, $n) {
return $data[$n];
}
function call() {
$args = func_get_args();
$name = array_shift($args);
return call_user_func_array($name, $args);
}
/**
* Поиск элемента в массиве
* @param function $cb сравнение с элементом массива
* @param array $hs массив в котором ищется значение
*
* @return int|string ключ найденого элемента в массиве
*/
function array_usearch($cb, array $hs, $strict = false) {
foreach($hs as $key => $value) if (call_user_func_array($cb, array($value, $key, $strict))) return $key;
}
if (!function_exists('hash_key')) {
/**
* Преобразует ключи элементов для многомерного массива
* @return mixed
*/
function hash_key ($key_name,/*. array .*/ $array) {
$result = array();
foreach($array as $value) {
$result[$value[$key_name]] = $value;
}
return $result;
};
}
function array_merge1($x, $y) {
$result = $x;
foreach ($y as $k => $v) {
$result [$k] = $v;
}
return $result;
}

12
src/geometry/point.php Normal file
View file

@ -0,0 +1,12 @@
<?php
class Point
{
public $left, $top;
function __construct ($left = 0, $top = 0)
{
$this->left = $left;
$this->top = $top;
}
}

View file

@ -0,0 +1,51 @@
<?php
require_once "core/geometry/point.php";
class Rectangle
{
public $left, $top, $width, $height;
function __construct($left = 0, $top = 0, $width = 0, $height = 0)
{
$this->left = $left;
$this->top = $top;
$this->width = $width;
$this->height = $height;
}
function __get($name)
{
switch ($name) {
case 'right': return $this->left + $this->width;
case 'bottom': return $this->top + $this->height;
}
}
function __set($name, $value)
{
switch ($name) {
case 'right': $this->width = $value - $this->left;
case 'bottom': $this->height = $value - $this->top;
}
}
/**
* Смещает прямоугольник на заданное положение
*/
function addPoint(Point $point)
{
$result = clone $this;
$result->left += $point->left;
$result->top += $point->top;
return $result;
}
/**
* Координаты точки при выравнивании прямоугольника относительно текущего
*/
function alignment(Rectangle $base)
{
return new Point((($base->left + $base->right) - ($this->left + $this->right)) / 2, $base->bottom - $this->height);
}
}

128
src/httprequest.php Normal file
View file

@ -0,0 +1,128 @@
<?php
/**
* Неверный запрос
*/
class WrongRequestException extends Exception
{
}
// HTTPRequest = ArrayAccess
class HttpRequest extends Collection implements ArrayAccess
{
/**
* Constructor
* Stores "request data" in GPC order.
*/
public function __construct()
{
$list = array (
'data' => $_REQUEST,
'get' => $_GET,
'post' => $_POST,
'cookie' => $_COOKIE);
$ajax = $this->isAjax();
foreach ($list as $key => $value) {
$data = new SafeCollection();
$data->import($value);
parent::set($key, $data);
}
$data = new Collection();
$data->import($GLOBALS['_FILES']);
parent::set('files', $data);
}
function get($key, $default = null)
{
return parent::get('data')->get($key, $default);
}
function session($value = false)
{
if ($value) {
$this->session = $value;
}
return $this->session;
}
function set($key, $value)
{
return parent::get('data')->set($key, $value);
}
function _get($key)
{
return parent::get($key);
}
function export()
{
return parent::get('data')->export();
}
/* Array Acces Interface */
function offsetExists($offset)
{
}
function offsetGet($offset)
{
}
function offsetSet($offset, $value)
{
}
function offsetUnset($offset)
{
}
function clear()
{
return parent::get('data')->clear();
}
/**
* Allow access to data stored in GET, POST and COOKIE super globals.
*
* @param string $var
* @param string $key
* @return mixed
*/
public function getRawData($var, $key)
{
$data = parent::get(strtolower($var));
if ($data) {
return $data->get($key);
}
return null;
}
public function setRawData($var, $key, $val)
{
$data = parent::get(strtolower($var));
if ($data) {
return $data->set($key, $val);
}
}
public function setAction($name)
{
$this->setRawData('get', 'action', $name);
}
public function getAction()
{
$result = $this->getRawData('get', 'action');
return ($result) ? $result : 'index';
}
public function isAjax()
{
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest');
}
}

93
src/layout/layout.php Normal file
View file

@ -0,0 +1,93 @@
<?php
require_once 'core/functions.php';
// Переместить в фильтры!!
/**
* Выбор макета страницы.
* Выбор оформления страницы осуществляется если было совпадение с каким либо условием
*/
class LayoutManager extends Filter
{
// Массив условий с их макетами
protected $condition = array();
/**
* Функция которая добавляет условие для проверки параметров $_GET
* @param $get array() | true Ассоциативный массив ключей и значений для $_GET
*
* @example
* addConditionGet(array('module' => 'personal'), 'personal')
* addConditionGet(array('module' => 'login'), 'login')
*/
public function addConditionGet($get, Filter $layout)
{
$this->addCondition(rcurry(array($this, 'checkGet'), $get), $layout);
}
/**
* Условие для аякс запросов. Тоже самое что и addConditionGet но еще проверяется является ли запрос ajax
*/
public function addConditionXHR($get, Filter $layout)
{
$this->addCondition(rcurry(array($this, 'checkXHR'), $get), $layout);
}
public function checkGet($request, $get)
{
if (is_array($get)) {
foreach ($get as $key => $value) {
if ($request->get($key) != $value) {
return false;
}
}
}
return true;
}
public function checkXHR($request, $get)
{
return $request->isAjax() && $this->checkGet($request, $get);
}
/**
* Добавляет есловие в общем виде
* @parma $get function(HttpRequest) Функция
* @parma $layout Layout Макет
*/
public function addCondition($get, Filter $layout)
{
$this->condition [] = array($get, $layout);
}
/**
* Выбирает и применяет макет для страницы
*/
public function execute(HttpRequest $request)
{
foreach ($this->condition as $condition) {
if (call_user_func($condition[0], $request)) {
$layout = $condition[1];
$view = $layout->execute($request);
if ($view instanceof View_Composite) {
echo $view->render();
} else {
echo $view;
}
return null;
}
}
}
}
/**
* Самый простой макет
*/
class LayoutNone extends Filter
{
function execute(HttpRequest $request)
{
return $this->processor->execute($request);
}
}

216
src/mail.php Normal file
View file

@ -0,0 +1,216 @@
<?php
/**
* Класс для работы с почтой
* http://en.wikipedia.org/wiki/MIME
*/
class Mail
{
public $from;
public $to;
public $subject;
public $content;
public $copy;
private $encoding;
private $notify= false;
protected $attachment = array ();
protected $uniqid;
protected $type = "text/plain";
function __construct() {
$this->setEncoding("UTF-8");
$this->uniqid = strtoupper(uniqid(time())); // Идентефикатор разделителя
// $this->mime
}
/**
* Установка отправителя
*/
function from($name)
{
$this->from = $name;
}
/**
* Установка получателя
*/
function to($name) // recipient
{
$this->to = $name;
}
/**
* Установка получателей копии
*/
function copy($name) // recipient cc
{
$this->copy = $name;
}
function notify($notify)
{
$this->notify = $notify;
}
/**
* Тема письма
*/
function subject($subject)
{
$this->subject = $subject;
}
/**
* Текст письма
*/
function setContent($text)
{
$this->content = $text;
}
function setType($type)
{
$this->type = $type;
}
/**
* Кодировка текста в письме
*/
function setEncoding($encoding)
{
$this->encoding = $encoding;
}
/**
* Добавление вложения из файла
*/
function addAttachment($filename, $name = false)
{
assert(is_string($filename));
if(file_exists($filename)) { // assert ??
$file = fopen($filename, "rb");
$data = fread($file, filesize($filename));
$this->attachment [] = ($name) ? array($data, $name) : array($data, basename($filename));
}
}
/**
* Добавление вложения из строки с указанием имени файла
*/
function addAttachmentRaw($data, $name)
{
assert(is_string($name));
$this->attachment [] = array($data, $name);
}
function quote($var, $val)
{
return ";" . PHP_EOL . "\t" . $var . "=\"" . $val . "\"";
}
/**
* Общий формат тегов MIME
* http://tools.ietf.org/html/rfc2045
*/
function mimeTag($name, $value, array $args = array())
{
assert (is_string($name));
assert (is_string($value));
return $name . ": " . $value . implode("", array_map(array($this, 'quote'), array_keys($args), array_values($args))) . PHP_EOL;
}
/**
*
* http://tools.ietf.org/html/rfc2047
*/
function encodedWord($text, $encoding = 'B')
{
return "=?{$this->encoding}?$encoding?".base64_encode($text)."?=";
}
/**
* Тело сообщения
*/
function getMessage()
{
$message = "--".$this->uniqid . PHP_EOL;
$message .= $this->mimeTag("Content-Type", $this->type, array ('charset' => $this->encoding));
$message .= $this->mimeTag("Content-Transfer-Encoding", "8bit");
$message .= PHP_EOL . $this->content . PHP_EOL . PHP_EOL;
/**
* Вложения
* http://tools.ietf.org/html/rfc2046#section-5.1.3
*/
foreach ($this->attachment as $value) {
list($data, $name) = $value;
$message .= "--" . $this->uniqid . PHP_EOL;
$message .= $this->mimeTag("Content-Type", "application/octet-stream", array ('name' => $name));
$message .= $this->mimeTag("Content-Transfer-Encoding", "base64");
$message .= $this->mimeTag("Content-Disposition", "attachment", array ('filename' => $name));
$message .= PHP_EOL . chunk_split(base64_encode($data)) . PHP_EOL;
}
return $message;
}
/**
* Заголовок сообщения
*/
function getHeader()
{
$head = $this->mimeTag("MIME-Version", "1.0");
$head .= $this->mimeTag("From", $this->from);
$head .= $this->mimeTag("X-Mailer", "CIS Tool");
$head .= $this->mimeTag("Reply-To", $this->from);
if ($this->notify) {
$head .= $this->mimeTag("Disposition-Notification-To", "\"" . $this->notify . "\" <" . $this->from . ">");
}
$head .= $this->mimeTag("Content-Type", "multipart/mixed", array ("boundary" => $this->uniqid));
if ($this->copy) {
$head .= $this->mimeTag("BCC", $this->copy);
}
$head .= PHP_EOL;
return $head;
}
function getSubject()
{
return $this->encodedWord($this->subject);
}
/**
* Вывод строки для сохранения в формате .eml
*/
function eml()
{
return "To: " . $this->to . PHP_EOL . "Subject: {$this->getSubject()}" . PHP_EOL . $this->getHeader() . $this->getMessage();
}
/**
* Отправка почты
*/
function send()
{
$result = mail($this->to, $this->getSubject(), $this->getMessage(), $this->getHeader());
if(! $result) {
throw new Exception('Невозможно отправить почту');
// require_once "core/path.php";
// file_put_contents(Path::resolveFile("data/email/send.eml"), $this->eml());
}
return $result;
}
function clear() {
foreach ($this->attachment as $key => &$value) {
unset($this->attachment[$key]);
}
$this->attachment = array();
}
}

28
src/mapper/factory.php Normal file
View file

@ -0,0 +1,28 @@
<?php
class ModelFactory
{
static $shortcut = "model";
public function __construct (Connection $db)
{
$this->db = $db;
}
/**
* Создает модель
* @param string $name
* @return model
*/
public function getModel ($name)
{
require_once 'core/mapper/mapper.php'; // ????
require_once (Shortcut::getUrl(self::$shortcut, $name));
$modelName = $name . "Mapper";
$model = new $modelName();
$model->db = $this->db;
$model->factory = $this;
return $model;
}
}

267
src/mapper/mapper.php Normal file
View file

@ -0,0 +1,267 @@
<?php
require_once 'core/primitive.php';
/**
* Использовать интерфейсы чтобы определить какие действия можно совершать с обьектом и таким образом
* Строить набор действий Action и отображений View для обьекта
* Т.к отображение данных может быть не только на таблицу баз данных
* И возможно реализованны все интерфейсы
*/
interface IDataList
{
function findAll (Collection $request, $id = null);
function findById ($id);
function getCount (Collection $request, $id);
}
interface IDataSave
{
function saveTo ($o);
function updateTo ($o);
}
/**
* Отображение таблицы базы данных в обьекты
* Mapper -> DataMapper implements IDataList, IDataSave, IDataSort, IDataDelete ...
*
* @package core
*/
class DataMapper implements IDataList
{
/* Хранить метаданные в статическом свойстве класса для ототбражения ?!, + Скрипт для генерации метаданных!!
*/
public $factory;
public $className; /* Класс на который будет отображаться строчка таблицы */
public $filter = ""; /* */
public $schema = array (); /* Соответствие между свойством обьекта и столбцом, добавить тип для записей !! */
public $database; /* Таблица */
public $index; /* Индексный столбец */
public $db; /* Соединение с базой данных */
public $reference = array (null, null);
public function __construct ()
{
}
public function setRange ($stmt, $page, $size)
{
$stmt->setLimit ($size);
$stmt->setOffset (($page-1) * $size);
}
public function listSQL (array $list)
{
return implode ($list, ",");
}
/**
* Поиск строки в таблице по идентификатору
* @param $id Значение идентификатора
* @return Обьект класса $className
*/
public function findById ($id)
{
// Строки запроса преобразовать в методы (getSQLSelect ...)
// Query::from($this->database)->where ($this->index, "=", $id)->select();
$stmt = $this->db->prepareStatement ("SELECT * FROM ".$this->database." WHERE ".$this->index." = ?");
$stmt->setInt (1, $id);
$rs = $stmt->executeQuery ();
$rs->next ();
return $this->mapOne ($rs);
}
/**
* Преобразует parseResult в обьект
*/
public /* private */ function mapOne ($rs)
{
$result = new $this->className ();
foreach ($this->schema as $key => $value) {
list($value) = $value;
$result->$value->setRes($rs, $key);
}
return $result;
}
public /* private */ function mapAll ($rs)
{
$result = array ();
// Преобразование SQL в обьект
while ($rs->next ()) {
$result[] = $this->mapOne ($rs);
}
return $result;
}
private function clean ($value) {
return strtolower(trim(iconv('utf-8', 'cp1251', $value)));
}
/**
* Преобразует параметры формы в SQL запрос WHERE
* @param array $params
* @param array $schema
* @return string
*/
public function requestToSQL (Collection $request, array $schema)
{
$result = array ();
foreach ($schema as $key => $value) {
$param = $request->get ($key);
if ($param) {
array_push ($result, "lower (".$value.") LIKE '".$this->clean ($param)."%'");
}
}
if (empty ($result)) return null;
return implode ($result, " AND ");
}
/**
* Удаление строк из таблицы с заданными индексами
* @param $list array Массив идентефикаторов
*/
public function deleteList (array $list)
{
// Query::from($this->database)->where($this->index, "in", $list)->delete();
$sql = "DELETE FROM " . $this->database . " WHERE " . $this->index . " IN (" . $this->listSQL ($list) . ")";
return $this->db->executeQuery ($sql);
}
public static function findKey (array $schema, $sort)
{
foreach ($schema as $key => $item) {
list($item) = $item;
if ($item == $sort) {
return $key;
}
}
return $sort;
}
public function getOrder (Collection $request)
{
$order = "";
$sort = $request->get('key');
$desc = ($request->get('desc') == 0)? 'DESC' : 'ASC';
if ($sort) {
$sort = self::findKey ($this->schema, $sort);
$order = " ORDER BY $sort $desc";
}
return $order;
}
/**
* Извлекает список записей из базы данных
*/
public function findAll (Collection $request, $id = null)
{
$name0 = $this->database;
$foreign = $this->reference[1];
// Переписать используя Query !!!
if ($foreign && $id) {
$filter = ($this->filter)?$filter = " AND ".$this->filter: "";
$sql = "SELECT t1.* FROM $name0 as t1 WHERE t1.$foreign = $id " . $filter.self::getOrder($request);
} else {
$filter = ($this->filter)?$filter = " WHERE " . $this->filter: "";
$sql = "SELECT * FROM $name0 " . $filter . self::getOrder($request);
}
$stmt = $this->db->prepareStatement ($sql);
$page = $request->get('page');
$limit = $request->get('size');
if ($page && $limit) {
$this->setRange($stmt, $page, $limit);
}
return $this->mapAll($stmt->executeQuery());
}
public function getCount (Collection $request, $id)
{
$name0 = $this->database;
$foreign = $this->reference[1];
// Переписать используя Query !!!
if ($foreign && $id) {
$filter = ($this->filter)?$filter = " AND " . $this->filter: "";
$sql = "SELECT count(t1.*) as length FROM $name0 as t1 WHERE t1.$foreign = $id " . $filter;
} else {
$filter = ($this->filter)?$filter = " WHERE " . $this->filter: "";
$sql = "SELECT count(*) as length FROM $name0 " . $filter;
}
$rs = $this->db->executeQuery($sql);
$rs->next();
return $rs->getInt('length');
}
/**
* Добавление записи в базу данных
* @param $o Обьект для записи в базу данных
*/
public function saveTo (Model $o)
{
$keys = array ();
$values = array ();
foreach ($this->schema as $key => $value) {
list($value) = $value;
if ($key != $this->index) {
$keys[] = $key;
$values[] = "'".$o->$value."'";
}
}
$stmt = $this->db->prepareStatement ("INSERT INTO ".$this->database." (".implode ($keys, ",").") VALUES (".implode ($values, ",").")");
$stmt->executeQuery ();
}
/**
* Обновляет запись в базе данных
* @param $o Обьект для обновления
*/
public function updateTo (Model $o)
{
$keys_values = array ();
foreach ($this->schema as $key => $value) {
list($value) = $value;
if ($key != $this->index && !($o->$value instanceof FKey)) {
$keys_values[] = $key." = '".$o->$value."'";
}
}
// Для всех должен быть идентефикатор id
$stmt = $this->db->prepareStatement ("UPDATE ".$this->database." SET ".implode($keys_values, ",")." WHERE ".$this->index." = ".$o->id);
$stmt->executeQuery ();
}
function saveDB (Model $o)
{
if ($o->id) {
$this->updateTo($o);
} else {
$this->saveTo($o);
}
}
function getModel($name)
{
require_once 'core/Mapper/Factory.php';
if (!$this->factory) {
$this->factory = new ModelFactory($this->db);
}
return $this->factory->getModel($name);
}
}
/**
*
*/
class Model
{
public function __construct ()
{
foreach (get_class_vars (get_class ($this)) as $key => $value) {
$this->$key = new Primitive ();
}
}
// __get, __set методы. В метаданных хранится тип свойств, проверять при присваивании!!
}
?>

78
src/mapper/pathmapper.php Normal file
View file

@ -0,0 +1,78 @@
<?php
/**
* Отображение списка папок с настройками на обьект
*/
class PathMapper
{
private $count;
private $base = null;
public $reference = null;
function plane (&$target, Collection $array)
{
$vars = get_class_vars(get_class($target));
foreach ($vars as $name => $value) {
$target->$name = $array->get($name);
}
}
function basePath ()
{
if ( ! $this->base) $this->base = $this->getBasePath();
return $this->base;
}
function findAll (Collection $request, $id = null)
{
require_once "core/settings.php";
require_once "core/path.php";
$this->reference = $id;
$base = $this->basePath ();
$path = new Path($base);
$list = $path->getContent();
$result = array ();
$this->count = 0;
foreach ($list as $name) {
if (is_dir (Path::join($this->basePath (), $name))
&& $module = $this->findById ($name)) {
$this->count++;
$result [] = $module;
}
}
return $result;
}
function findById ($name)
{
$file = Path::join($this->basePath(), $this->getSettingsFile ($name));
if (file_exists($file)) {
$settings = new Settings($file);
$settings->read();
return $this->settingsMap($name, $settings);
}
return null;
}
/**
* Число папок
*/
function getCount ()
{
return $this->count;
}
/**
* Удаление списка папок
*/
function deleteList(array $list)
{
foreach ($list as $name)
Path::delete(Path::join($this->getBasePath(), $name));
}
}
?>

View file

@ -0,0 +1,113 @@
<?php
/**
Copyright (c) 2007 Brady Mulhollem
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**/
class Simple_BB_Code{
//General Tags
var $tags = array('b' => 'strong','i' => 'em','u' => 'span style="text-decoration:underline"','quote' => 'blockquote','s' => 'span style="text-decoration: line-through"', 'list' => 'ul','\*' => 'li');
//Tags that must be mapped to diffierent parts
var $mapped = array('url' => array('a','href',true),'img' => array('img','src',false));
//Tags with atributes
var $tags_with_att = array('color' => array('font','color'),'size' => array('font','size'),'url' => array('a','href'));
//Gotta have smilies
var $smilies = array(':)' => 'smile.gif',':(' => 'frown.gif');
//Config Variables
//Convert new line charactes to linebreaks?
var $convert_newlines = true;
//Parse For smilies?
var $parse_smilies = true;
//auto link urls(http and ftp), and email addresses?
var $auto_links = true;
//Internal Storage
var $_code = '';
function Simple_BB_Code($new=true,$parse=true,$links=true){
$this->convert_newlines = $new;
$this->parse_smilies = $parse;
$this->auto_links = $links;
}
function parse($code){
$this->_code = $code;
$this->_strip_html();
$this->_parse_tags();
$this->_parse_mapped();
$this->_parse_tags_with_att();
$this->_parse_smilies();
$this->_parse_links();
$this->_convert_nl();
return $this->_code;
}
function _strip_html(){
$this->_code = strip_tags($this->_code);
}
function _convert_nl(){
if($this->convert_newlines){
$this->_code = nl2br($this->_code);
}
}
function _parse_tags(){
foreach($this->tags as $old=>$new){
$ex = explode(' ',$new);
$this->_code = preg_replace('/\['.$old.'\](.+?)\[\/'.$old.'\]/is','<'.$new.'>$1</'.$ex[0].'>',$this->_code);
}
}
function _parse_mapped(){
foreach($this->mapped as $tag=>$data){
$reg = '/\['.$tag.'\](.+?)\[\/'.$tag.'\]/is';
if($data[2]){
$this->_code = preg_replace($reg,'<'.$data[0].' '.$data[1].'="$1">$1</'.$data[0].'>',$this->_code);
}
else{
$this->_code = preg_replace($reg,'<'.$data[0].' '.$data[1].'="$1">',$this->_code);
}
}
}
function _parse_tags_with_att(){
foreach($this->tags_with_att as $tag=>$data){
$this->_code = preg_replace('/\['.$tag.'=(.+?)\](.+?)\[\/'.$tag.'\]/is','<'.$data[0].' '.$data[1].'="$1">$2</'.$data[0].'>',$this->_code);
}
}
function _parse_smilies(){
if($this->parse_smilies){
foreach($this->smilies as $s=>$im){
$this->_code = str_replace($s,'<img src="'.$im.'">',$this->_code);
}
}
}
function _parse_links(){
if($this->auto_links){
$this->_code = preg_replace('/([^"])(http:\/\/|ftp:\/\/)([^\s,]*)/i','$1<a href="$2$3">$2$3</a>',$this->_code);
$this->_code = preg_replace('/([^"])([A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4})/i','$1<a href="mailto:$2">$2</a>',$this->_code);
}
}
function addTag($old,$new){
$this->tags[$old] = $new;
}
function addMapped($bb,$html,$att,$end=true){
$this->mapped[$bb] = array($html,$att,$end);
}
function addTagWithAttribute($bb,$html,$att){
$this->tags_with_att[$bb] = array($html,$att);
}
function addSmiley($code,$src){
$this->smilies[$code] = $src;
}
}
?>

24
src/numbers.php Normal file
View file

@ -0,0 +1,24 @@
<?php
class Numbers
{
static function roman($i)
{
return 0;
}
static function decimal($i)
{
return $i;
}
static function prefix($prefix, array $array)
{
$result = array();
for ($i = 0; $i < count($array); $i++) {
$result [] = call_user_func($prefix, $i + 1) . '. ' . $array[$i];
}
return $result;
}
}

317
src/path.php Normal file
View file

@ -0,0 +1,317 @@
<?php
/*.
require_module 'standard';
.*/
/**
* Класс для работы с папками и путями
* Для итерации над файлами возможно лучше использовать SPL
*
* @package utils
*/
class Path
{
const SEPARATOR = "/";
protected $path;
public function __construct ($path)
{
assert(is_string($path));
$this->path = $this->fromString($path);
$this->optimize();
}
static function factory($path) {
return new Path($path);
}
/**
* Возвращает расширение файла
*
* @param string $fileName Полное имя файла
*
* @return string
*/
static function getExtension ($fileName)
{
assert(is_string($fileName));
return pathinfo($fileName, PATHINFO_EXTENSION);
}
/**
* Полное имя файла без расширения
*
* @param string $fileName Имя файла
*
* @return string
*/
static function skipExtension ($fileName)
{
assert(is_string($fileName));
$path = pathinfo($fileName);
if ($path['dirname'] == ".") {
return $path['filename'];
} else {
return self::join($path['dirname'], $path['filename']);
}
}
/**
* Возвращает имя файла без расширения
*
* @param string $fileName Полное имя файла
*
* @return string
*/
static function getFileName ($fileName)
{
assert(is_string($fileName));
return pathinfo($fileName, PATHINFO_FILENAME);
}
/**
* Список файлов в директории
*
* @param array $allow массив расширений для файлов
* @param array $ignore массив имен пааок которые не нужно обрабатывать
*
* @return array
*/
public function getContent ($allow = null, $ignore = array())
{
$ignore = array_merge(array (".", "..", $ignore));
return self::fileList($this->__toString(), $allow, $ignore);
}
// Использовать SPL ???
protected function fileList ($base, &$allow, &$ignore)
{
$result = array ();
if ($handle = opendir($base)) {
while (false !== ($file = readdir($handle))) {
if (! in_array ($file, $ignore)) {
$isDir = is_dir (Path::join ($base, $file));
if ($isDir || ($allow == null) || in_array (self::getExtension($file), $allow)) {
$result[] = $file;
}
}
}
closedir($handle);
}
return $result;
}
protected function fileListAll (&$result, $base, &$allow, &$ignore)
{
$files = self::fileList($base, $allow, $ignore);
foreach ($files as $name) {
$fullname = self::join($base, $name);
if (is_dir($fullname)) {
self::fileListAll($result, $fullname, $allow, $ignore);
} else {
array_push ($result, $fullname);
}
}
}
/**
* Список файлов в директориии и ее поддиректорий
*
* @param array $allow массив расширений разрешеных для файлов
* @param array $ignore массив имен пааок которые не нужно обрабатывать
*
* @return array
*/
function getContentRec ($allow = null, $ignore = array())
{
$result = array ();
$ignore = array_merge(array (".", ".."), $ignore);
self::fileListAll($result, $this->__toString(), $allow, $ignore);
return $result;
}
/**
* Рекурсивно копирует директорию
*
* @param string $source Папка из которой копируется
* @param string $target Папка в которую копируется
*/
public static function copy ($source, $target)
{
if (is_dir($source)) {
if (! file_exists($target)) mkdir ($target);
$path = new Path($source);
$files = $path->getContent();
foreach ($files as $file) {
$entry = self::join($source, $file);
if (is_dir($entry)) {
self::copy($entry, Path::join($target, $file));
} else {
copy($entry, Path::join($target, $file));
}
}
}
}
/**
* Рекурсивно удаляет директорию
*
* @param string $path Папка
*/
public static function delete ($path)
{
assert(is_string($path));
if (is_dir($path)) {
foreach(glob($path . '/*') as $sf) {
if (is_dir($sf) && !is_link($sf)) {
self::delete($sf);
} else {
unlink($sf);
}
}
rmdir($path);
}
}
/**
* Преобразует строку путя в массив
*
* @param string $path Путь
*
* @return array
*/
public function fromString ($path)
{
assert(is_string($path));
$list = preg_split("/[\/\\\\]/", $path);
return $list;
}
/**
* Преобразует относительный путь в абсолютный
*/
public function optimize ()
{
$result = array(current($this->path));
while (next($this->path) !== false) {
$n = current ($this->path);
switch ($n) {
case "": break;
case ".": break;
case "..": if (count($result) > 0) array_pop($result); break;
default:
array_push($result, $n);
}
}
reset($this->path);
$this->path = $result;
}
/**
* Преобразует путь в строку
*
* @return string
*/
public function __toString ()
{
$result = implode($this->path, self::SEPARATOR);
return $result;
}
/**
* Проверяет является ли папка родительской для другой папки
*
* @parma Path $path
*
* @return boolean
*/
public function isParent ($path)
{
if (count($path->path) > count($this->path)) {
for ($i = 0; $i < count($this->path); $i++) {
if ($path->path[$i] != $this->path[$i]) {
return false;
}
}
return true;
}
return false;
}
/**
* Находит путь относительно текущего путя
*
* @param string $name Полный путь к файлу
*
* @return string Относительный путь к файлу
*/
public function relPath ($name)
{
$path = new Path ($name);
foreach ($this->path as $n) {
array_shift($path->path);
}
return $path->__toString();
}
public function append ($path)
{
$base = $this->__toString();
return self::join($base, $path);
}
/**
* Создает недастающие папки для записи файла
*
* @param string $dst Полное имя файла
*
* @return void
*/
static function prepare ($dst)
{
$path_dst = pathinfo($dst, PATHINFO_DIRNAME);
if (! file_exists($path_dst)) {
mkdir($path_dst, 0700, true);
}
}
/**
* Подбирает новое временное имя для файла
*
* @param string $dst Предпологаемое имя файла
*
* @return string Новое имя файла
*/
static function resolveFile ($dst)
{
$i = 0;
$file = self::skipExtension($dst);
$suffix = self::getExtension($dst);
$temp = $dst;
while (file_exists($temp)) {
$i ++;
$temp = $file . "." . $i . "." . $suffix;
}
return $temp;
}
/**
* Обьединяет строки в путь соединяя необходимым разделителем
*
* @return string
*/
static function join ()
{
$args = func_get_args();
return implode(self::SEPARATOR, $args);
}
}

103
src/primitive.php Normal file
View file

@ -0,0 +1,103 @@
<?php
/**
* Класс преобразования типа значения поля класса в тип поля таблицы
* @package core
*/
class Primitive
{
public $value;
public $name;
public function __construct ($name = "")
{
$this->name = $name;
}
// Преобразование из внешнего вормата
public function setString ($value)
{
$this->value = $value;
}
// Преобразование из формата базы данных
public function setRes ($res, $key)
{
$this->value = $res->getString ($key);
}
public function __toString ()
{
return ((string) $this->value);
}
// Преобразование во внешний формат
public function getString ()
{
return $this->__toString ();
}
}
/**
* Отображение поля таблицы в целое число
* @package core
*/
class Int4 extends Primitive {
public function setRes ($res, $key) {
$this->value = $res->getInt ($key);
}
public function setString ($value) {
$this->value = ((int) $value);
}
}
/* Foreign key */
class FKey extends Int4 {}
/**
* Отображение поля таблицы в дату - время
*/
class Date extends Primitive
{
public function setRes ($res, $key)
{
$this->value = $res->getInt ($key);
}
public function setString ($value)
{
$this->value = 0;
if ($tmp = explode("/",$value,3)) {
if ($tmp[1] && $tmp[0] && $tmp[2]) {
if (checkdate($tmp[1], $tmp[0], $tmp[2])) {
$this->value = mktime(0, 0, 0, $tmp[1], $tmp[0], $tmp[2]);
}
}
}
}
public function getString ()
{
return date ("d/m/Y", $this->value);
}
}
/**
*
*/
class Password extends Primitive
{
public function setRes ($res, $key)
{
$this->value = $res->getString($key);
}
public function setString ($value)
{
$this->value = md5($value);
}
public function getString ()
{
return $this->value;
}
}

9
src/query/meta.php Normal file
View file

@ -0,0 +1,9 @@
<?php
/**
* Класс метаданных таблицы
*/
class Meta
{
}

205
src/query/query.php Normal file
View file

@ -0,0 +1,205 @@
<?php
require_once 'table.php';
require_once 'meta.php';
/**
* Класс для составления запроса
*/
class Query
{
static $alias = 0;
public $table;
public $filter = array ();
public $calc = array ();
public $order = array ();
/**
* Создает пустой зарос
*/
static function create()
{
return new Query();
}
/**
* Метаданные таблицы
* @param string $name Имя таблицы
*/
function getTableMeta($name)
{
}
/**
* Создает новый запрос
* @param string $name Имя таблицы
*
* @return Query
*/
public function from($name)
{
$result = clone $this;
$table = new Table();
$table->type = 'simple';
$table->name = $name;
$table->alias = "t" . self::$alias++;
$result->table = $table;
return $result;
}
/**
* Добавляет к запросу условие
* @param string $field Имя поля таблицы
* @param string $op Имя оператора
* @param $value Значение поля
*
* @return Query
*/
public function filter($field, $op, $value)
{
$result = clone $this;
$result->filter [] = $result->table->alias ."." . $field . $op . "'" . $value . "'";
return $result;
}
/**
* @param string $e Выражение
*/
public function calc($e)
{
$result = clone $this;
$result->calc [] = $e;
return $result;
}
public function joinOn($first, $e)
{
$result = $this->compose($first, array ('filter', 'calc', 'order'));
$e = strtr($e, array ("a." => $this->table->alias . ".",
"b." => $first->table->alias . "."));
$table = new Table ();
$table->type = 'join';
$table->name = $this->table->name;
$table->alias = $this->table->alias;
$table->join = $first->table;
$table->condition = $e;
$result->table = $table;
return $result;
}
/**
* Обьединяет параметры двух заросов
* @param Query $first
* @pram array $list Список параметров
*
* @return Query
*/
private function compose(Query $first, array $list)
{
$result = new Query();
foreach ($list as $name) {
$result->$name = array_merge($this->$name, $first->$name);
}
return $result;
}
/**
* Заковычивает значение
*/
public function quote ($value)
{
return "'" . $value . "'";
}
/**
* Компиляция таблицы
* @param Table $table
*
* @return string Часть строки SQL
*/
private function table(Table $table)
{
if ($table->type == 'simple') {
return $table->name ." AS ".$table->alias;
}
if ($table->type == 'join') {
return $table->name ." AS ".$table->alias. " JOIN " . $this->table ($table->join) . " ON " . $table->condition;
}
return "";
}
/**
* Компиляция WHERE
*
* @return string Часть строки SQL
*/
private function where ()
{
return implode(" AND ", $this->filter);
}
/**
* Компиляция запроса в SELECT SQL
*
* @return string SQL выражение
*/
public function select()
{
return "SELECT "
. ((!empty($this->calc))? implode (",", $this->calc): "*")
. " FROM " . $this->table($this->table)
. ((!empty ($this->filter))? " WHERE " . $this->where() : "");
}
/**
* Компиляция в DELETE
*/
public function delete()
{
return "DELETE FROM " . $this->table->name
. ((!empty ($this->filter))? " WHERE " . $this->where() : "");
}
private function add_prefix($prefix, $array) {
$result = array();
foreach ($array as $value) {
$result [] = $prefix . $value;
}
return $result;
}
/**
* Компиляция в UPDATE
* TODO: Разные типы запросов Query->update(); Query->select(), Query->insert();!!
* Возвраащают statement
*/
public function update($values)
{
$pairs = array ();
foreach ($values as $key => $value) { //
$pairs [] = $key . "=" . $this->quote ($value);
}
return "UPDATE " . $this->table->name . " SET "
. implode(",", $pairs)
. ((!empty($this->filter))? " WHERE " . $this->where() : "");
}
/**
* Компиляция в INSERT
*/
public function insert($values)
{
return "INSERT INTO ". $this->table->name . "("
. implode(",", array_keys($values))
. ") VALUES (" . implode(",", array_map(array($this, 'quote'), array_values($values))) . ")";
}
}

14
src/query/table.php Normal file
View file

@ -0,0 +1,14 @@
<?php
/**
* Класс таблицы
*/
class Table
{
public $type = 'simple';
public $name;
public $alias;
public $join;
public $condition;
}

22
src/registry.php Normal file
View file

@ -0,0 +1,22 @@
<?php
/**
* http://www.patternsforphp.com/wiki/Registry
* http://www.patternsforphp.com/wiki/Singleton
* http://www.phppatterns.com/docs/design/the_registry?s=registry
*/
class Registry extends Settings
{
static $instance = null;
/**
*/
static public function getInstance ()
{
if (self::$instance == null) {
self::$instance = new Registry();
}
return self::$instance;
}
}

82
src/search/htmlhelper.php Normal file
View file

@ -0,0 +1,82 @@
<?php
/**
* Извлекает текст из HTML документа
*/
function stripText($document)
{
$search = array("'<script[^>]*?>.*?</script>'si" => "", // strip out javascript
"'<[\/\!]*?[^<>]*?>'si" => "", // strip out html tags
"'([\r\n])[\s]+'" => "\\1", // strip out white space
"'&(quot|#34|#034|#x22);'i" => "\"", // replace html entities
"'&(amp|#38|#038|#x26);'i" => "&", // added hexadecimal values
"'&(lt|#60|#060|#x3c);'i" => ">",
"'&(gt|#62|#062|#x3e);'i" => "<",
"'&(nbsp|#160|#xa0);'i" => " ",
"'&(iexcl|#161);'i" => chr(161),
"'&(cent|#162);'i" => chr(162),
"'&(pound|#163);'i" => chr(163),
"'&(copy|#169);'i" => chr(169),
"'&(reg|#174);'i" => chr(174),
"'&(deg|#176);'i" => chr(176));
$text = preg_replace(array_keys($search), array_values($search), $document);
return $text;
}
/**
* Разделение текста на массив слов
*/
function tokenize ($document)
{
$array = preg_split("/[\W]+/", $document);
return $array;
}
/**
* Ищет один из символов с конца строки
*
* @param string $haystack
* @param array $needle Массив символов для поиска
* @param int $offset Смещение от начала строки
*
* @return int Позицию первого совпадения
*/
function indexRight ($haystack, $needle, $offset = 0)
{
if ((bool)$offset === false) $offset = 0;
while ($offset >= 0) {
if (in_array ($haystack[$offset], $needle)) {
return $offset;
}
$offset --;
}
return false;
}
/**
* Ищет один из символов с начала строки
*
* @param string $haystack
* @param array $needle Массив символов для поиска
* @param int $offset Смещение от начала строки
*
* @return int Позицию первого совпадения
*/
function indexLeft ($haystack, $needle, $offset = 0)
{
if ($offset < 0) return false;
while ($offset < strlen($haystack)) {
if ((is_callable($needle) && call_user_func ($needle, $haystack[$offset]))
|| (is_array ($needle) && in_array ($haystack[$offset], $needle))) {
return $offset;
}
$offset ++;
}
return false;
}
function not_ctype_alpha ($ch)
{
return !ctype_alpha($ch);
}

84
src/search/index.php Normal file
View file

@ -0,0 +1,84 @@
<?php
require_once 'core/search/htmlhelper.php';
require_once 'core/search/stemmer.php';
require_once 'core/path.php';
/**
* Индексирование файлов
*/
class Index
{
const ARRAY_FILE = 0;
const ARRAY_TEXT = 1;
public $index = array ();
public $text = array ();
protected $count = 0;
function getTitle ($content) {
$title = "'<title[^>]*?>(.*?)</title>'si";
preg_match($title, $content, $matches);
if(isset($matches[1])) {
return $matches[1];
}
return "";
}
// Выбираем основу слова
function clean ($word)
{
return Stemmer::russian(strtolower($word));
}
function process ($base, $files)
{
$path = new Path($base);
// Список документов
foreach ($path->getContentRec($files) as $file) {
$content = file_get_contents ($file);
$text = stripText($content);
// $title = self::getTitle ($content);
$title = pathinfo($file, PATHINFO_BASENAME);
// echo $file, "\n";
// Список слов в документе
$list = tokenize($text);
foreach ($list as $word) {
$preword = self::clean($word);
if (isset($this->index[$preword])) {
$index = $this->index[$preword];
if ( ! in_array ($this->count, $index)) $this->index[$preword] [] = $this->count;
} else {
// Не записываем слова длинна которых меньше 2
if (strlen($preword) > 1) {
$this->index[$preword] = array ($this->count);
}
}
}
$this->text [] = array ($title, $path->relPath ($file), $text);
$this->count ++;
}
ksort($this->index);
}
/**
* Сохранение результата поиска
*/
function saveData ($file)
{
$file = fopen($file, "w");
// Количество слов и текстов
fwrite ($file, pack("SS", count($this->index), count($this->text)));
foreach ($this->index as $word => $value) {
$length = strlen($word);
array_unshift ($value, "SSa*S*", $length, count($value), $word);
fwrite($file, call_user_func_array ('pack', $value));
}
foreach ($this->text as $text) {
fwrite($file, pack("SSSa*a*a*",
strlen($text[0]), strlen($text[1]), strlen($text[2])
, $text[0], $text[1], $text[2]));
}
}
}

91
src/search/lexer.php Normal file
View file

@ -0,0 +1,91 @@
<?php
/**
* Разбирвет строку запроса на токены
*/
class Lexer
{
const TOKEN_NOT = 1;
const TOKEN_OR = 2;
const TOKEN_LPAREN = 3;
const TOKEN_RPAREN = 4;
const TOKEN_AND = 5;
const TOKEN_WORD = 6;
const TOKEN_EOL = 7;
protected $src;
private $offset = 0;
public $token;
public function __construct ()
{
}
function setSource ($src)
{
$this->src = $src;
$this->offset;
}
private function skipSpace ()
{
while (!$this->isEOL() && $this->getChar() == " ") {
$this->offset++;
}
}
private function getChar ()
{
return $this->src [$this->offset];
}
/**
* Проверяет на конец строки
*/
private function isEOL () {
return $this->offset >= strlen($this->src);
}
/**
* Односимвольный токен
*/
private function easyToken () {
$ch = $this->getChar ();
switch ($ch) {
case '~': $token = array(self::TOKEN_NOT, $ch); break;
case '|': $token = array(self::TOKEN_OR, $ch); break;
case '(': $token = array(self::TOKEN_LPAREN, $ch); break;
case ')': $token = array(self::TOKEN_RPAREN, $ch); break;
case '&': $token = array(self::TOKEN_AND, $ch); break;
default:
$this->offset++;
$token = $this->getToken();
}
$this->offset++;
return $token;
}
/**
* Возвращает следующий токен
*/
public function getToken ()
{
$this->skipSpace ();
if ($this->isEOL()) {
return array(self::TOKEN_EOL, "");
}
if (ctype_alpha($this->getChar())) {
$start = $this->offset;
while (!$this->isEOL() && ctype_alpha($this->getChar())) {
$this->offset ++;
}
return array(self::TOKEN_WORD, substr ($this->src, $start, $this->offset-$start));
}
return $this->easyToken();
}
public function nextToken ()
{
$this->token = $this->getToken();
}
}

97
src/search/search.php Normal file
View file

@ -0,0 +1,97 @@
<?php
require_once 'core/search/lexer.php';
require_once 'core/functions.php';
/**
* Поиск в индексе
*/
class Search
{
private $lexer;
private $index;
function __construct ($index)
{
$this->lexer = new Lexer();
$this->index = $index;
$this->op = array ($this, 'Op');
$this->binary = array ($this, 'binaryOp');
$this->union = array ($this, 'union');
$this->intersection = lcurry($this->op, 'array_uintersect', $this->union);
$this->notQuery = lcurry ($this->binary, Lexer::TOKEN_NOT,
lcurry($this->op, 'array_udiff', 'array_udiff'), array ($this, 'easyQuery'));
$this->orQuery = lcurry ($this->binary, Lexer::TOKEN_OR,
lcurry($this->op, $this->union, $this->union), $this->notQuery);
$this->andQuery = lcurry ($this->binary, Lexer::TOKEN_AND, $this->intersection, $this->orQuery);
}
function union ($a, $b, $callback)
{
return array_merge($a, $b);
}
function Eq ($a, $b)
{
return $a == $b;
}
function Op ($files, $words, $a, $b) {
return array (
'words' => call_user_func ($words, $a['words'], $b['words'], array ($this, 'eq')),
'files' => call_user_func ($files, $a['files'], $b['files'], array ($this, 'eq'))
);
}
public function getQuery ($source)
{
$this->lexer->setSource ($source);
$this->lexer->nextToken();
return $this->topQuery();
}
function topQuery ()
{
$result = call_user_func ($this->andQuery);
while ($this->lexer->token[0] == Lexer::TOKEN_LPAREN) {
$result = call_user_func ($this->intersection, $result, call_user_func ($this->andQuery));
}
return $result;
}
function easyQuery ()
{
$result = null;
if ($this->lexer->token[0] == Lexer::TOKEN_LPAREN) {
$this->lexer->nextToken ();
$result = $this->topQuery ();
if ($this->lexer->token[0] == Lexer::TOKEN_RPAREN) {
$this->lexer->nextToken ();
}
return $result;
} else {
$result = call_user_func ($this->index, $this->lexer->token[1]);
$this->lexer->nextToken ();
return $result;
}
}
/**
* @param int $type Тип лексемы
* @param function $op Функция при совпадении типа лексемы при запросе
* @param function $next Следующий обработчик запроса
*/
function binaryOp ($type, $op, $next)
{
$result = call_user_func($next);
while ($this->lexer->token[0] == $type) {
$this->lexer->nextToken();
$result = call_user_func($op, $result, call_user_func ($next));
}
return $result;
}
}

101
src/search/searcher.php Normal file
View file

@ -0,0 +1,101 @@
<?php
require_once 'core/search/search.php';
require_once 'core/search/htmlhelper.php';
require_once 'core/search/stemmer.php';
class Searcher {
/* protected */ public $index;
protected $text;
protected $search;
public function __construct ()
{
// Может передавать обьект метод по умлочанию getWordStat??
$this->search = new Search (array ($this, 'getWord'));
}
/**
* Читает содержимое индексного файла
*
* @param string $file Имя файла
*/
function setSource ($fileName)
{
$file = fopen($fileName, "r");
$words = fread($file, 4);
$all = unpack("Swords/Stexts", $words);
for ($i = 0; $i < $all['words']; $i++) {
$pos = fread($file, 4);
$size = unpack("Sword/Sindex", $pos);
$word = fread($file, $size['word']);
$index = unpack("S*", fread($file, $size['index']*2));
$this->index[$word] = $index;
}
for ($i = 0; $i < $all['texts']; $i++) {
$pos = fread($file, 6);
$size = unpack("Stitle/Surl/Stext", $pos);
//
$title = fread($file, $size['title']);
$url = fread($file, $size['url']);
$text = fread($file, $size['text']);
$this->text [] = array ($title, $url, $text);
}
}
// По слову возвращаем список файлов и слово
public function getWord ($word)
{
$preword = Stemmer::russian($word); // Index?? -> clean
if (isset($this->index[$preword])) { // Search??
return array ('files' => $this->index[$preword], 'words' => array ($preword));
}
return array ('files' => array (), 'words' => array ());
}
/**
* Список документов в которых встечается слово
*
*/
function getResult (&$query)
{
$result = array ();
$word = $query['words'];
$list = $query['files'];
//
foreach ($list as $n) {
$result [] = array (
'title' => $this->text[$n][0],
'file' => $this->text[$n][1],
'text' => self::getSlice ($word[0], $this->text[$n][2]));
}
return $result;
}
/**
* Часть документа в котором встречается слово
*
* @param $word Слово
* @param $text Текст содержащий слово
*/
function getSlice ($word, $text)
{
$pos = stripos($text, $word);
$offset = max(max ($pos-100, indexRight($text, array ("."), $pos) + 1), 0);
$real = substr($text, $pos, strlen($word)) ;
return substr($text, $offset, $pos - $offset)
. "<span style='color: red'>" . $real . "</span>" . substr ($text, $pos + strlen($word), 100);
}
/**
* Поиск по запросу
*
*/
function search ($query)
{
$result = $this->search->getQuery($query);
return $this->getResult($result);
}
}

180
src/search/stemmer.php Normal file
View file

@ -0,0 +1,180 @@
<?php
/*
* PHP5 implementation of Martin Porter's stemming algorithm for Russian language.
* Written on a cold winter evening close to the end of 2005 by Dennis Kreminsky (etranger at etranger dot ru)
* Use the code freely, but don't hold me responsible if it breaks whatever it might break.
*
*/
define ('CHAR_LENGTH', '1'); // all Russian characters take 2 bytes in UTF-8, so instead of using (not supported by default) mb_*
// string functions, we use the standard ones with a dirty char-length trick.
// Should you want to use WIN-1251 (or any other charset), convert this source file to that encoding
// and then change CHAR_LENGTH to the proper value, which is likely to be '1' then.
//
class Stemmer {
static public function russian($word)
{
$a = self::rv($word);
$start = $a[0];
$rv = $a[1];
$rv = self::step1($rv);
$rv = self::step2($rv);
$rv = self::step3($rv);
$rv = self::step4($rv);
return $start.$rv;
}
static private function rv($word)
{
$vowels = array('а','е','и','о','у','ы','э','ю','я');
$flag = 0;
$rv = '';
$start = '';
for ($i=0; $i<strlen($word); $i+=CHAR_LENGTH) {
if ($flag==1)
$rv .= substr($word, $i, CHAR_LENGTH);
else
$start .= substr($word, $i, CHAR_LENGTH);
if (array_search(substr($word,$i,CHAR_LENGTH), $vowels) !== false)
$flag=1;
}
return array($start,$rv);
}
static function substitute ($word, &$suffix_list)
{
foreach ($suffix_list as $suffix) {
if (self::has_suffix($word, $suffix)) {
$word = self::cut_suffix($word, $suffix);
}
}
return $word;
}
static function has_suffix ($word, $suffix)
{
return substr($word, -(strlen($suffix))) == $suffix;
}
static function has_aya ($word, $suffix)
{
return (substr($word,-strlen($suffix)-CHAR_LENGTH,CHAR_LENGTH)=='а' || substr($word,-strlen($suffix)-CHAR_LENGTH,CHAR_LENGTH)=='я');
}
static function cut_suffix ($word, $suffix)
{
return substr($word, 0, strlen($word) - strlen($suffix));
}
static private function step1($word)
{
$perfective1 = array('в', 'вши', 'вшись');
foreach ($perfective1 as $suffix) {
if (self::has_suffix($word, $suffix) && self::has_aya ($word, $suffix)) {
return self::cut_suffix($word, $suffix);
}
}
$perfective2 = array('ив','ивши','ившись','ывши','ывшись');
foreach ($perfective2 as $suffix) {
if (self::has_suffix($word, $suffix)) {
return self::cut_suffix($word, $suffix);
}
}
$reflexive = array('ся', 'сь');
$word = self::substitute($word, $reflexive);
$adjective = array('ее','ие','ые','ое','ими','ыми','ей','ий','ый','ой','ем','им','ым','ом','его','ого','ему','ому','их','ых','ую','юю','ая','яя','ою','ею');
$participle2 = array('ем','нн','вш','ющ','щ');
$participle1 = array('ивш','ывш','ующ');
foreach ($adjective as $suffix) {
if (self::has_suffix($word, $suffix)) {
$word = self::cut_suffix($word, $suffix);
foreach ($participle1 as $suffix)
if (self::has_suffix($word, $suffix) && self::has_aya ($word, $suffix))
$word = self::cut_suffix($word, $suffix);
return self::substitute($word, $participle2);
}
}
$verb1 = array('ла','на','ете','йте','ли','й','л','ем','н','ло','но','ет','ют','ны','ть','ешь','нно');
foreach ($verb1 as $suffix)
if (self::has_suffix($word, $suffix) && self::has_aya ($word, $suffix))
return self::cut_suffix($word, $suffix);
$verb2 = array('ила','ыла','ена','ейте','уйте','ите','или','ыли','ей','уй','ил','ыл','им','ым','ен','ило','ыло','ено','ят','ует','уют','ит','ыт','ены','ить','ыть','ишь','ую','ю');
foreach ($verb2 as $suffix)
if (self::has_suffix($word, $suffix))
return self::cut_suffix($word, $suffix);
$noun = array('а','ев','ов','ие','ье','е','иями','ями','ами','еи','ии','и','ией','ей','ой','ий','й','иям','ям','ием','ем','ам','ом','о','у','ах','иях','ях','ы','ь','ию','ью','ю','ия','ья','я');
foreach ($noun as $suffix) {
if (self::has_suffix($word, $suffix))
return self::cut_suffix($word, $suffix);
}
return $word;
}
static private function step2($word)
{
if (substr($word,-CHAR_LENGTH,CHAR_LENGTH) == 'и')
$word = substr($word, 0, strlen($word)-CHAR_LENGTH);
return $word;
}
static private function step3($word)
{
$vowels = array('а','е','и','о','у','ы','э','ю','я');
$flag = 0;
$r1 = '';
$r2 = '';
for ($i=0; $i<strlen($word); $i+=CHAR_LENGTH)
{
if ($flag==2)
$r1.=substr($word, $i, CHAR_LENGTH);
if (array_search(substr($word,$i,CHAR_LENGTH), $vowels) !== false)
$flag=1;
if ($flag=1 && array_search(substr($word,$i,CHAR_LENGTH), $vowels) === false)
$flag=2;
}
$flag=0;
for ($i=0; $i<strlen($r1); $i+=CHAR_LENGTH)
{
if ($flag==2)
$r2.=substr($r1, $i, CHAR_LENGTH);
if (array_search(substr($r1,$i,CHAR_LENGTH), $vowels) !== false)
$flag=1;
if ($flag=1 && array_search(substr($r1,$i,CHAR_LENGTH), $vowels) === false)
$flag=2;
}
$derivational=array('ост', 'ость');
foreach ($derivational as $suffix)
if (substr($r2,-(strlen($suffix))) == $suffix)
$word=substr($word, 0, strlen($r2)-strlen($suffix));
return $word;
}
static private function step4($word)
{
if (substr($word,-CHAR_LENGTH*2)=='нн')
$word=substr($word, 0, strlen($word)-CHAR_LENGTH);
else
{
$superlative=array('ейш', 'ейше');
foreach ($superlative as $suffix)
if (substr($word,-(strlen($suffix))) == $suffix)
$word = substr($word, 0, strlen($word) - strlen($suffix));
if (substr($word,-CHAR_LENGTH*2) == 'нн')
$word = substr($word, 0, strlen($word) - CHAR_LENGTH);
}
// should there be a guard flag? can't think of a russian word that ends with ейшь or ннь anyways, though the algorithm states this is an "otherwise" case
if (substr($word,-CHAR_LENGTH,CHAR_LENGTH) == 'ь')
$word=substr($word, 0, strlen($word)-CHAR_LENGTH);
return $word;
}
}

36
src/session.php Normal file
View file

@ -0,0 +1,36 @@
<?php
class Session
{
function get($key)
{
if (isset($_SESSION[$key])) {
return $_SESSION[$key];
}
return null;
}
function set($key, $value)
{
if (is_array($key)) {
$_SESSION[strtolower(get_class($key[0]))][$key[1]] = $value;
} else {
$_SESSION[$key] = $value;
}
}
function clean($key)
{
unset($_SESSION[$key]);
}
function start()
{
session_start();
}
function stop()
{
session_destroy();
}
}

165
src/settings.php Normal file
View file

@ -0,0 +1,165 @@
<?php
/**
* Класс реестра
* Реестр организован как ассоциативный многомерный массив
* array( 'name1' => parameters1, 'name2' => parameters1, ... )
*
* name1, name2 ... - Имена модулей
* parameters1, parameters1 - Массивы с параметрами модуля
* Имя необходимо чтобы потом легко было удалить ненужные ветки дерева
*/
class Settings extends Collection
{
protected $file;
public function __construct ($file = null)
{
$this->file = $file;
}
/**
* Чтение настроек из файла
*
* @param File $file
*
* @return Boolean
*/
public function read()
{
if ( !file_exists ($this->file)) return false;
// Не include_once т.к читать настройки можно несколько раз
include($this->file);
if (!is_array($settings)) {
throw new Exception($this->file);
}
return $this->import($settings);
}
/**
* Запись ключа в реестр (Реестр это могомерный массив)
*/
public function writeKey(array $key, $value)
{
// assert(count($key) >= 1);
$data = &$this->data;
while (count($key) > 1) {
$name = array_shift($key);
$data = &$data[$name];
}
// assert(count($key) == 1);
$name = array_shift($key);
if (is_array($value)) {
if (! isset($data[$name])) $data[$name] = array();
$this->merge($data[$name], $value);
} else {
$data[$name] = $value;
}
}
/**
* Обновляет массив в соответствии со значением
*/
protected function merge(array &$data, $value)
{
foreach ($value as $key => $subvalue) {
if (is_array($subvalue)) {
if (! isset($data[$key])) $data[$key] = array();
$this->merge($data[$key], $subvalue);
} else {
$data[$key] = $subvalue;
}
}
}
/**
* Чтение ключа из реестра
* @param $args Путь к значению ключа
*/
public function readKey(array $key)
{
return $this->readKeyData($key, $this->data);
}
protected function readKeyData(array $key, $data)
{
// assert(count($key) >= 1);
while (count($key) > 1) {
$name = array_shift($key);
if (isset($data[$name])) {
$data = $data[$name];
} else {
return null;
}
}
// assert(count($key) == 1);
$name = array_shift($key);
if (isset($data[$name])) {
return $data[$name];
} else {
return null;
}
}
/**
* Чтение ключа из реестра (Собирает все ключи с определенным значением во всех модулях)
* @param $key Путь к значению ключа внутри модуля
*/
public function readKeyList()
{
$key = func_get_args();
$result = array();
foreach ($this->data as $name => $value) {
$output = $this->readKeyData($key, $value);
if ($output) {
$result[$name] = $output;
}
}
return $result;
}
public function removeKey($name)
{
unset($this->data[$name]);
}
public function removeNode(array $key)
{
$data = &$this->data;
while (count($key) > 1) {
$name = array_shift($key);
$data = &$data[$name];
}
$name = array_shift($key);
unset($data[$name]);
}
public function getOwner()
{
return array_keys($this->data);
}
/**
* Запись настроек в файл (Может переименовать в store)
*
* @param File $file
*
* @return void
*/
public function write($file = false)
{
$result = var_export ($this->data, true);
file_put_contents (($file) ? $file : $this->file, "<?php\n\$settings = ".$result.";\n?>");
}
/**
* Список модулей
*/
public function getModules()
{
return array_keys($this->data);
}
}

54
src/setup.php Normal file
View file

@ -0,0 +1,54 @@
<?php
/**
* Обработка файлов для установки
*/
class Setup
{
/**
* Содержимое PHP файла
*/
static function fileContent($file, array $tpl)
{
ob_start();
include $file;
$result = ob_get_contents();
ob_clean();
return $result;
}
/**
* Копирует файлы шаблонной директории
*/
static function copyTemplatePath($srcPath, $dstPath, array $tpl, $tplFile = 'tpl')
{
$out = new Path($srcPath);
$path = new Path($dstPath);
$files = $path->getContentRec(null, array(".svn"));
foreach ($files as $file) {
if (Path::getExtension($file) == $tplFile) {
// Шаблон
$dst = $out->append($path->relPath (Path::skipExtension($file)));
Path::prepare($dst);
file_put_contents($dst, self::fileContent($file, $tpl));
} else {
// Обычный файл
$dst = $out->append($path->relPath ($file));
Path::prepare($dst);
copy($file, $dst);
}
}
}
/**
* Выполнение Списка SQL команд
*/
static function batchSQL(Connection $conn, $file)
{
$stmtList = SQLStatementExtractor::extractFile ($file);
foreach ($stmtList as $stmt) {
$conn->executeQuery ($stmt);
}
}
}

57
src/shortcut.php Normal file
View file

@ -0,0 +1,57 @@
<?php
/**
* Класс для короткого доступа к файлам / папкам
*/
class Shortcut
{
static $instance = null;
public $variables = array();
public $list = array();
// Singleton pattern
static public function getInstance ()
{
if (self::$instance == null) {
self::$instance = new Shortcut();
}
return self::$instance;
}
/**
* Добавляет ярлык с именем $prefix
* Путь может содержать переменные
*/
public function addUrl($prefix, $path)
{
$this->list [$prefix] = $path;
}
/**
*
*/
public function addVar($name, $value)
{
$this->variables['$' . $name] = $value;
}
/**
* Возвращает путь по имени ярлыка
*/
static function getUrl ($prefix, $name = null, $name1 = false)
{
$shortcut = self::getInstance();
$names = $shortcut->variables;
if ($name) {
$names['$name'] = $name;
}
if ($name1) {
$names['$name1'] = $name1;
}
if (isset($shortcut->list[$prefix])) {
return strtr($shortcut->list[$prefix], $names);
}
return false;
}
}

58
src/sort.php Normal file
View file

@ -0,0 +1,58 @@
<?php
class SortRecord
{
function __construct($key, $mode, $order)
{
$this->key = $key;
$this->order = ((boolean)($order) === false) ? 1 : -1;
$this->mode = $mode;
}
function compare($a, $b)
{
if($a[$this->key] == $b[$this->key]) {
return 0;
}
return ($a[$this->key] > $b[$this->key]) ? $this->order : -$this->order;
}
function compareKeys($a, $b)
{
if($a->{$this->key} == $b->{$this->key}) {
return 0;
}
return ($a->{$this->key} > $b->{$this->key}) ? $this->order : -$this->order;
}
function sort(&$list)
{
return usort($list, array($this, 'compare'));
}
function sortKeys(&$list)
{
return usort($list, array($this, 'compareKeys'));
}
function group(&$list, $key, $types)
{
$groups = array();
foreach ($types as $name) {
$groups[$name] = array();
}
$groups['_any_'] = array();
foreach ($list as $item) {
if (isset($groups[$item[$key]])) {
$groups[$item[$key]][] = $item;
} else {
$groups['_any_'][] = $item;
}
}
$result = array();
foreach ($groups as $value) {
$result = array_merge($result, $value);
}
$list = $result;
}
}

98
src/spell.php Normal file
View file

@ -0,0 +1,98 @@
<?php
require_once 'core/search/htmlhelper.php';
/**
* Функции для проверки орфографии
* rename Spell -> SpellHelper ??
*/
class Spell
{
private $pspell;
public $autocorrect = false;
public function __construct ($language, $encoding)
{
$this->pspell = pspell_new($language, '', '', $encoding, PSPELL_NORMAL);
}
function spellCheckWord ($word)
{
$word = $word[0];
// Ignore ALL CAPS
if (preg_match('/^[A-Z0-9]*$/', $word)) return $word;
// Return dictionary words
if (pspell_check($this->pspell, $word))
return $word;
// Auto-correct with the first suggestion, color green
if ($this->autocorrect && $suggestions = pspell_suggest($this->pspell, $word))
return '<span class="spell-suggest">' . current($suggestions) . '</span>';
// No suggestions, color red
return '<span class="spell-nosuggest">' . $word . '</span>';
}
function isGoodWord ($word)
{
// Ignore ALL CAPS
if (preg_match('/^[A-Z]*$/', $word)) return true;
if (preg_match('/^[0-9]*$/', $word)) return true;
// Return dictionary words
if (pspell_check($this->pspell, $word))
return true;
return false;
}
function suggest ($string)
{
return preg_replace_callback('/\b\w+\b/', array($this, 'spellCheckWord'), $string);
}
/**
* Вызывает функцию для каждого найденного слова
*/
function eachWord ($call, $string)
{
$begin = indexLeft ($string, 'ctype_alpha', 0);
while ($begin !== false) {
$end = indexLeft ($string, 'not_ctype_alpha', $begin);
call_user_func ($call, $string, $begin, $end);
$begin = indexLeft ($string, 'ctype_alpha', $end);
}
return false;
}
/**
* Проверяет слово на соответствие со словарем
*
*/
function doWord (&$string, $begin, $end)
{
$word = substr($string, $begin, $end - $begin);
if (! $this->isGoodWord ($word)) {
$start = max(indexLeft ($string, 'not_ctype_alpha', $begin - 100), 0);
$offset = indexLeft ($string, 'not_ctype_alpha', $end + 100);
if (! $offset) {
$offset = strlen($string);
}
$length = $offset - $start;
throw new Exception ($this->suggest(substr($string, $start, $length)));
}
}
/**
* Часть текста содержащий неправильное слово
*/
function getError ($string)
{
try {
self::eachWord (array ($this, 'doWord'), $string);
} catch (Exception $e) {
return $e->getMessage();
}
return false;
}
}

69
src/tabletree.php Normal file
View file

@ -0,0 +1,69 @@
<?php
/**
* Преобразование дерева из модели Plain в массив массивов (Adjacency List)
*/
require_once 'core/functions.php';
define (SORT_DESC, 0); // descending
define (SORT_ASC, 1); // ascending
/**
* Выбирает все сроки из таблицы с уникальными значениями ключа
* @param $name Имя ключа
* @param $table Двухмерный массив
* @example
* key_unique_values ('name', array (array ('name' => 1), array ('name' => 2), array ('name' => 1)))
=> array (1, 2)
* @end example
*/
function key_unique_values ($name, $table) {
// Ищем уникальные значения для заданного ключа
$keys = array ();
foreach ($table as $row) {
if (!in_array ($row[$name], $keys))
$keys[] = $row[$name];
}
return $keys;
}
/**
* Сортировка двумерного массива по заданному ключу
* @param $array Массив
* @param $key Имя ключа по значению которого будет идти сравнение
* @return Отсортированный массив
*/
function sortOn($array, $key, $fn = '__cmp') {
usort ($array, rcurry($fn, $key));
//usort ($array, create_function ('$x,$y', 'return __cmp ($x, $y, "'.$key.'");'));
return $array;
}
/**
* Обходит таблицу как дерево
* @param $level Array Уровни вложенности
* @param $table Таблица
* @param $fn Функция которая применяется к каждой ветке дерева
* $fn ($name, $index, $rows, $cc)
* @param $name Ключ уровня
* @param $index Значение ключа уровня
* @param $rows Все столбцы текущго уровня
* @parma $cc Столбцы более низкого уровня
*/
function tableTreeWalk($level, $table, $fn) {
if (empty ($level)) return $table;
$name = array_shift ($level);
$keys = key_unique_values($name, $table);
$data = array ();
foreach ($keys as $index) {
list($rows, $table) = partition (lcurry('__index', $index, $name), $table);
// $rows = array_filter ($table, lcurry('__index', intval($index), $name));
//$rows = array_filter ($table, create_function ('$x', 'return __index ('.intval($index).', \''.$name.'\', $x);'));
$data[$index] = call_user_func ($fn, $name, $index, $rows, tableTreeWalk ($level, $rows, $fn));
}
return $data;
}

34
src/tales.php Normal file
View file

@ -0,0 +1,34 @@
<?php
/**
* Расширения для PHPTAL для отображения времени и даты
* package utils
*/
class DateTime_Tales implements PHPTAL_Tales
{
static public function date ($expression, $nothrow = false)
{
return "phptal_date(".PHPTAL_TalesInternal::path ($expression).")";
}
static public function time ($expression, $nothrow = false)
{
return "phptal_time(".PHPTAL_TalesInternal::path ($expression).")";
}
}
/* Регистрация нового префикса для подключения компонента */
$registry = PHPTAL_TalesRegistry::getInstance();
$registry->registerPrefix('date', array('DateTime_Tales', 'date'));
$registry->registerPrefix('time', array('DateTime_Tales', 'time'));
function phptal_date ($e)
{
return date ("d.m.Y", $e);
}
function phptal_time ($e)
{
return date ("H:i", $e);
}

122
src/tools/drawing.php Normal file
View file

@ -0,0 +1,122 @@
<?php
class Drawing
{
const ALIGN_LEFT = "left";
const ALIGN_TOP = "top";
const ALIGN_BOTTOM = "bottom";
const ALIGN_CENTER = "center";
const ALIGN_RIGHT = "right";
static function drawrectnagle(&$image, $left, $top, $width, $height, $rgb)
{
$color = imagecolorallocate($image, $rgb[0], $rgb[1], $rgb[2]);
$right = $left + $width;
$bottom = $top + $height;
imageline($image, $left, $top, $right, $top, $color);
imageline($image, $right,$top, $right, $bottom, $color);
imageline($image, $left, $bottom, $right, $bottom, $color);
imageline($image, $left, $top, $left, $bottom, $color);
}
/**
* http://ru2.php.net/imagettftext
*/
static function imagettftextbox(&$image, $size, $angle, $left, $top, $color, $font, $text,
$max_width, $max_height, $align, $valign)
{
// echo var_dump($font);
// echo $left,"\n", $top, "\n";
// echo $max_width,"\n", $max_height, "\n";
// self::drawrectnagle($image, $left, $top, $max_width, $max_height, array(0xFF,0,0));
$text_lines = explode("\n", $text); // Supports manual line breaks!
$lines = array();
$line_widths = array();
$largest_line_height = 0;
foreach ($text_lines as $block) {
$current_line = ''; // Reset current line
$words = explode(' ', $block); // Split the text into an array of single words
$first_word = true;
$last_width = 0;
for ($i = 0; $i < count($words); $i++) {
$item = $words[$i];
// echo "->", $font, "\n";
$dimensions = imagettfbbox($size, $angle, $font, $current_line . ($first_word ? '' : ' ') . $item);
$line_width = $dimensions[2] - $dimensions[0];
$line_height = $dimensions[1] - $dimensions[7];
$largest_line_height = max($line_height, $largest_line_height);
if ($line_width > $max_width && !$first_word) {
$lines[] = $current_line;
$line_widths[] = $last_width ? $last_width : $line_width;
$current_line = $item;
} else {
$current_line .= ($first_word ? '' : ' ') . $item;
}
if ($i == count($words) - 1) {
$lines[] = $current_line;
$line_widths[] = $line_width;
}
$last_width = $line_width;
$first_word = false;
}
if ($current_line) {
$current_line = $item;
}
}
// vertical align
if ($valign == self::ALIGN_CENTER) {
$top_offset = ($max_height - $largest_line_height * count($lines)) / 2;
} elseif ($valign == self::ALIGN_BOTTOM) {
$top_offset = $max_height - $largest_line_height * count($lines);
}
$top += $largest_line_height + $top_offset;
$i = 0;
fb($lines);
fb($line_widths);
foreach ($lines as $line) {
// horizontal align
if ($align == self::ALIGN_CENTER) {
$left_offset = ($max_width - $line_widths[$i]) / 2;
} elseif ($align == self::ALIGN_RIGHT) {
$left_offset = ($max_width - $line_widths[$i]);
}
// echo $font, "\n";
imagettftext($image, $size, $angle, $left + $left_offset, $top + ($largest_line_height * $i), $color, $font, $line);
$i++;
}
return $largest_line_height * count($lines);
}
function imagettftextSp($image, $size, $angle, $x, $y, $color, $font, $text, $spacing = 0)
{
if ($spacing == 0)
{
imagettftext($image, $size, $angle, $x, $y, $color, $font, $text);
}
else
{
$temp_x = $x;
for ($i = 0; $i < mb_strlen($text); $i++)
{
$bbox = imagettftext($image, $size, $angle, $temp_x, $y, $color, $font, $text[$i]);
$temp_x += $spacing + ($bbox[2] - $bbox[0]);
}
}
}
}

437
src/tools/exceltable.php Normal file
View file

@ -0,0 +1,437 @@
<?php
class Excel_Number
{
public $value;
function __construct($value)
{
$this->value = $value;
}
function getString()
{
return $this->value;
}
}
class Excel_DateTime
{
public $value;
function __construct($value)
{
$this->value = intval($value);
}
function getString()
{
return date('Y-m-d\TH:i:s.u', $this->value);
}
}
/**
* Клетка таблицы
*/
class TableCell
{
public $style = false;
public $value;
public $merge = false;
function __construct ($value)
{
$this->value = $value;
}
}
/**
* Ряд таблицы
*/
class TableRow
{
public $style = false;
public $cells = array();
public $height = false;
function setCell($y, $value)
{
$this->cells[$y] = new TableCell($value);
}
function setCellStyle($y, $name)
{
$this->cells[$y]->style = $name;
}
}
/**
* Таблица
*/
class ExcelTable
{
static $index;
private $name;
private $style;
protected $rows = array();
protected $splitVertical = false;
protected $splitHorizontal = false;
function __construct()
{
$this->name = "Page " . intval(self::$index ++);
}
/**
* Записать значение в клетку с заданными координатами
*/
function setCell($x, $y, $value)
{
assert(is_numeric($x) && $x > 0);
assert(is_numeric($y) && $y > 0);
if(! isset($this->rows[$x])) {
$this->rows[$x] = new TableRow();
}
$this->rows[$x]->setCell($y, $value);
}
/**
* Заполняет ряд начиная с указанного столбца значениями из массива
*/
function setRow($row, $index, array $data)
{
assert(is_numeric($index) && $index > 0);
assert(is_numeric($row) && $row > 0);
reset($data);
for ($i = $index; $i < $index + count($data); $i++) {
$this->setCell($row, $i, current($data));
next($data);
}
}
/**
* Устанавливает высоту ряда
* @param $row integer Номер ряда
* @parma $value real Высота ряда
*/
function setRowHeight ($row, $value)
{
assert(is_numeric($row) && $row > 0);
$this->rows[$row]->height = $value;
}
/**
* Устанавливает стиль ряда
* @param $row integer Номер ряда
* @parma $name string Имя стиля
*/
function setRowStyle ($row, $name)
{
assert(is_numeric($row) && $row > 0);
$this->rows[$row]->style = $name;
}
/**
* Обьединяет клетки в строке
* @param $row Номер ряда
* @param $cell Номер столбца
* @param $merge Количество клеток для обьединения
*/
function setCellMerge ($row, $cell, $merge)
{
assert(is_numeric($row) && $row > 0);
assert(is_numeric($cell) && $cell > 0);
$this->rows[$row]->cells[$cell]->merge = $merge;
}
/**
* Устанавливает стиль для клеток ряда
* @param $row integer Номер ряда
* @param $y integer Номер столбца
* @parma $name string Имя стиля
*/
function setCellStyle ($row, $y, $name)
{
if (isset($this->rows[$row]))
$this->rows[$row]->setCellStyle($y, $name);
}
/**
* Добавляет строку к таблице
*/
function addRow($index = 1, array $data = array(""))
{
assert(is_numeric($index) && $index > 0);
$offset = $this->getRows() + 1;
$this->setRow($offset, $index, $data);
return $offset;
}
/**
* Количество строк в таблице
*
* @return int
*/
function getRows()
{
return max(array_keys($this->rows));
}
/**
* Количество столбцов в строке
*
* @return int
*/
function getRowCells($row)
{
return max(array_keys($row->cells));
}
/**
* Разделяет таблицу на две части по вертикали
* @param $n integer Количество столбцов слева
*/
function splitVertical($n)
{
$this->splitVertical = $n;
}
/**
* Разделяет таблицу на две части по горизонтали
* @param $n integer Количество столбцов сверху
*/
function splitHorizontal($n)
{
$this->splitHorizontal = $n;
}
/**
* Количество столбцов в таблице
*
* @return int
*/
function getColumns()
{
return max(array_map(array($this, 'getRowCells'), $this->rows));
}
function encode($s)
{
return $s;
}
/**
* Генерация клетки таблицы (Переработать)
*/
function createCell ($ncell, XMLWriter $doc, $j, $value, $setIndex) {
$doc->startElement("Cell");
if ($ncell->style) {
$doc->writeAttribute('ss:StyleID', $ncell->style);
}
if ($ncell->merge) {
$doc->writeAttribute('ss:MergeAcross', $ncell->merge);
}
if ($setIndex) {
$doc->writeAttribute('ss:Index', $j);
}
$doc->startElement("Data");
if ($value instanceof Excel_DateTime) {
$doc->writeAttribute('ss:Type', "DateTime");
$doc->text($value->getString());
} else if ($value instanceof Excel_Number) {
$doc->writeAttribute('ss:Type', "Number");
$doc->text($value->getString());
} else {
if (is_string($value)) {
$doc->writeAttribute('ss:Type', "String");
} else {
$doc->writeAttribute('ss:Type', "Number");
}
$doc->writeCData($this->encode($value));
}
$doc->endElement();
$doc->endElement();
}
/**
* Генерация таблицы
*/
public function createTable (XMLWriter $doc) {
$doc->startElement('Worksheet');
$doc->writeAttribute('ss:Name', $this->name);
$columns = $this->getColumns();
$rows = $this->getRows();
$doc->startElement('Table');
$doc->writeAttribute('ss:ExpandedColumnCount', $columns);
$doc->writeAttribute('ss:ExpandedRowCount', $rows);
// Переписать цыкл !!!!!!!
for ($i = 1; $i <= $rows; $i++) {
$doc->startElement('Row');
if (isset($this->rows[$i])) {
if ($this->rows[$i]->style) {
$doc->writeAttribute('ss:StyleID', $this->rows[$i]->style);
}
if ($this->rows[$i]->height) {
$doc->writeAttribute('ss:Height', $this->rows[$i]->height);
}
$nrow = $this->rows[$i];
// Флаг индикатор подстановки номера столбца
$setIndex = false;
for ($j = 1; $j <= $columns; $j++) {
$value = null;
if (isset($nrow->cells[$j])) {
$value = $nrow->cells[$j]->value;
}
if (!empty($value)) {
$this->createCell($nrow->cells[$j], $doc, $j, $value, $setIndex);
$setIndex = false;
} else {
$setIndex = true;
}
}
}
$doc->endElement();
}
$doc->endElement();
$this->splitPane ($doc);
$doc->endElement();
}
protected function splitPane (XMLWriter $doc) {
$doc->startElement('WorksheetOptions');
$doc->writeAttribute('xmlns', 'urn:schemas-microsoft-com:office:excel');
$doc->writeElement('FrozenNoSplit');
if ($this->splitVertical) {
$doc->writeElement('SplitVertical', $this->splitVertical);
$doc->writeElement('LeftColumnRightPane', $this->splitVertical);
}
if ($this->splitHorizontal) {
$doc->writeElement('SplitHorizontal', $this->splitHorizontal);
$doc->writeElement('TopRowBottomPane', $this->splitHorizontal);
}
if ($this->splitHorizontal && $this->splitVertical) {
$doc->writeElement('ActivePane', 0);
} else if($this->splitHorizontal) {
$doc->writeElement('ActivePane', 2);
}
$doc->endElement();
}
}
/**
* Документ
*/
class ExcelDocument {
static $ns = "urn:schemas-microsoft-com:office:spreadsheet";
private $table = array ();
protected $styles = array();
function addTable($table) {
$this->table [] = $table;
}
/**
* Добавление стиля к документу
* @param $name string Имя стиля
* @param $values array Параметры стиля
* @param $type Тип стиля
*/
function setStyle ($name, array $values, $type = 'Interior')
{
if(!isset($this->styles[$name])) {
$this->styles[$name] = array();
}
$this->styles[$name][$type] = $values;
}
/**
* Генерация стилей
*/
private function createStyles (XMLWriter $doc) {
$doc->startElement('Styles');
foreach ($this->styles as $name => $sn) {
$doc->startElement('Style');
$doc->writeAttribute('ss:ID', $name);
foreach ($sn as $type => $s) {
// Стиль Borders - составной
if ($type == 'Borders') {
$doc->startElement('Borders');
foreach ($s as $border) {
$doc->startElement('Border');
foreach ($border as $key => $value) {
$doc->writeAttribute('ss:' . $key, $value);
}
$doc->endElement();
}
$doc->endElement();
} else {
$doc->startElement($type);
foreach ($s as $key => $value) {
$doc->writeAttribute('ss:' . $key, $value);
}
$doc->endElement();
}
}
$doc->endElement();
}
$doc->endElement();
}
/**
* Преобразует переводы строки в спец символы
*/
function clean ($s) {
assert(is_string($s));
return strtr($s, array ("\n" => '&#10;'));
}
/**
* Сохраняет таблицу в формате Office 2003 XML
* http://en.wikipedia.org/wiki/Microsoft_Office_XML_formats
*/
function save($filename)
{
$doc = new xmlWriter();
$doc->openURI($filename);
$doc->setIndent(false);
$doc->startDocument('1.0','utf-8');
$doc->startElement('Workbook');
$doc->writeAttribute('xmlns', self::$ns);
$doc->writeAttribute('xmlns:ss', self::$ns);
$this->createStyles($doc);
foreach ($this->table as $table) {
if ($table instanceof ExcelTable) {
$table->createTable($doc);
} else {
$table_data = call_user_func($table);
$table_data->createTable($doc);
unset($table_data);
}
}
$doc->endElement();
}
}

40
src/tools/image.php Normal file
View file

@ -0,0 +1,40 @@
<?php
class Core_Tools_Image
{
static function load($uri)
{
$e = strtolower(pathinfo($uri, PATHINFO_EXTENSION));
switch ($e) {
case 'png': return imagecreatefrompng($uri);
case 'jpeg': case 'jpg': return imagecreatefromjpeg($uri);
case 'gif': return imagecreatefromgif($uri);
}
}
static function fit($image, $prewidth, $preheight, $force = true)
{
$width = imagesx($image);
$height = imagesy($image);
$percent = min($prewidth / $width, $preheight / $height);
if ($percent > 1 && !$force) $percent = 1;
$new_width = $width * $percent;
$new_height = $height * $percent;
$image_p = imagecreatetruecolor($new_width, $new_height);
imagecopyresampled($image_p, $image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
return $image_p;
}
static function save($image, $uri)
{
$e = strtolower(pathinfo($uri, PATHINFO_EXTENSION));
switch ($e) {
case 'jpg': imagejpeg($image, $uri, 100); break;
case 'png': imagepng($image, $uri); break;
case 'gif': imagegif($image, $uri); break;
}
}
}

32
src/tools/password.php Normal file
View file

@ -0,0 +1,32 @@
<?php
function generatePassword($length = 9, $strength = 0) {
$vowels = 'aeuy';
$consonants = 'bdghjmnpqrstvz';
if ($strength & 1) {
$consonants .= 'BDGHJLMNPQRSTVWXZ';
}
if ($strength & 2) {
$vowels .= "AEUY";
}
if ($strength & 4) {
$consonants .= '23456789';
}
if ($strength & 8) {
$consonants .= '@#$%';
}
$password = '';
$alt = time() % 2;
for ($i = 0; $i < $length; $i++) {
if ($alt == 1) {
$password .= $consonants[(rand() % strlen($consonants))];
$alt = 0;
} else {
$password .= $vowels[(rand() % strlen($vowels))];
$alt = 1;
}
}
return $password;
}

37
src/tools/string.php Normal file
View file

@ -0,0 +1,37 @@
<?php
// from creole
function strToArray($str)
{
$str = substr($str, 1, -1); // remove { }
$res = array();
$subarr = array();
$in_subarr = 0;
$toks = explode(',', $str);
foreach($toks as $tok) {
if ($in_subarr > 0) { // already in sub-array?
$subarr[$in_subarr][] = $tok;
if ('}' === substr($tok, -1, 1)) { // check to see if we just added last component
$res[] = $this->strToArray(implode(',', $subarr[$in_subarr]));
$in_subarr--;
}
} elseif ($tok{0} === '{') { // we're inside a new sub-array
if ('}' !== substr($tok, -1, 1)) {
$in_subarr++;
// if sub-array has more than one element
$subarr[$in_subarr] = array();
$subarr[$in_subarr][] = $tok;
} else {
$res[] = $this->strToArray($tok);
}
} else { // not sub-array
$val = trim($tok, '"'); // remove " (surrounding strings)
// perform type castng here?
$res[] = $val;
}
}
return $res;
}

94
src/tools/tableview.php Normal file
View file

@ -0,0 +1,94 @@
<?php
class TableExcelView {
private $list = array();
private $data = array();
function setColumns(array $list) {
$this->list = $list;
}
function makeTable() {
$xls = new ExcelTable();
$xls->setRow(1, 1, array_keys($this->list));
foreach($this->data as $n => $item) {
$result = array();
foreach($this->list as $key => $c) {
if (is_callable($c)) {
$result [] = call_user_func($c, $item, $n);
} else {
if (is_numeric($item[$c])) {
$result [] = new Excel_Number($item[$c]);
} else {
$result [] = $item[$c];
}
}
}
$xls->addRow(1, $result);
}
return $xls;
}
function writeTable($data, $file) {
$this->data = $data;
$xls = new ExcelDocument();
$xls->addTable(array($this, 'makeTable'));
$xls->save($file);
}
}
class TableHTMLView {
private $list = array();
private $stack = array();
private $result = array();
function writeElement($name, $content) {
echo "<".$name.">";
echo $content;
echo "</".$name.">";
}
function startElement($name) {
array_push($this->stack, $name);
echo "<".$name.">";
}
function endElement() {
$name = array_pop($this->stack);
echo "</".$name.">";
}
function setColumns(array $list) {
$this->list = $list;
}
function writeTable($data) {
$this->startElement('table');
$this->startElement('thead');
$this->startElement('tr');
foreach($this->list as $key => $c) {
$this->writeElement('th', $key);
}
$this->endElement();
$this->endElement();
$this->startElement('tbody');
foreach($data as $n => $item) {
$this->startElement('tr');
foreach($this->list as $key => $c) {
if (is_callable($c)) {
$this->writeElement('td', call_user_func($c, $item, $n));
} else {
$this->writeElement('td', $item[$c]);
}
}
$this->endElement();
}
$this->endElement();
$this->endElement();
}
}

206
src/tools/templateimage.php Normal file
View file

@ -0,0 +1,206 @@
<?php
require_once 'core/tools/drawing.php';
/**
* Формат для композиции изображений
*/
class TemplateImage
{
static $listfiles = array('jpg' => 'jpeg', 'gif' => 'gif', 'png' => 'png', 'bmp' => 'wbmp');
static $listfonts = array(
'georgia' => 'georgia.ttf',
'georgiabd' => 'georgiab.ttf',
'georgiaz' => 'georgiaz.ttf',
'times' => 'times.ttf',
'timesbd' => 'timesbd.ttf',
'arial' => 'arial.ttf',
'arialbd' => 'arialbd.ttf',
'tahoma' => 'tahoma.ttf',
'calibri' => 'calibri.ttf',
'calibribd' => 'calibrib.ttf',
'calibrii' => 'calibrii.ttf',
'' => 'arial.ttf',
'dejavu' => 'DejaVuCondensedSerif.ttf',
'dejavubd' => 'DejaVuCondensedSerifBold.ttf',
'dejavuz' =>'DejaVuCondensedSerifBoldItalic.ttf',
'dejavui' => 'DejaVuCondensedSerifItalic.ttf',
'miriad' => 'MyriadPro-Cond.ttf',
'miriadbd' => 'MyriadPro-BoldCond.ttf'
);
protected $src;
protected $context = array();
protected $data = array();
protected $base = "c:\\windows\\fonts\\";
protected $image;
protected $prepare = true;
public $debug = false;
function __construct ($template = false)
{
// assert(is_string($src));
if($template) {
$this->data = $template;
}
}
/**
* Путь к изображению
*/
function resourcePath($path)
{
assert(is_string($path));
$this->resource = $path;
}
/**
* Путь у шрифтам
*/
function fontPath($path)
{
assert(is_string($path));
$this->base = $path;
}
function set($name, $value)
{
assert(is_string($name));
$this->context['['.$name.']'] = $this->encode($value);
}
function setImage($name)
{
$this->filename = $name;
$this->image = $this->imagefromfile($name);
}
function setEmptyImage($width, $height)
{
$this->image = imagecreatetruecolor($width, $height);
}
/**
* Создает изображение из файла
*/
function imagefromfile($file)
{
assert(is_string($file));
$suffix = pathinfo($file, PATHINFO_EXTENSION);
if (array_key_exists($suffix, self::$listfiles)) {
return call_user_func('imagecreatefrom' . self::$listfiles[$suffix], $file);
}
return null;
}
function getFontFile($name)
{
assert(is_string($name));
if(array_key_exists(strtolower($name), self::$listfonts)) {
return $this->base . self::$listfonts[$name];
}
return $this->base . 'arial.ttf';
}
function fontSuffix($style)
{
if($style[0] && $style[1]) return "z";
if($style[0]) return "bd";
if($style[1]) return "i";
return "";
}
function imageText($text, $value)
{
assert(is_string($text));
$text = strtr($text, $this->context);
$size = $value->fontSize;
fb('font-style');
fb($value->fontStyle);
$fontfile = $this->getFontFile($value->fontFamily . $this->fontSuffix($value->fontStyle));
$color = intval(substr($value->color, 1), 16);
if ($value->align[0]) {
$align = Drawing::ALIGN_LEFT;
} elseif ($value->align[2]) {
$align = Drawing::ALIGN_RIGHT;
} else {
$align = Drawing::ALIGN_CENTER;
}
if ($value->valign[0]) {
$valign = Drawing::ALIGN_TOP;
} elseif ($value->valign[1]) {
$valign = Drawing::ALIGN_CENTER;
} else {
$valign = Drawing::ALIGN_BOTTOM;
}
Drawing::imagettftextbox($this->image, $size, 0, $value->left, $value->top, $color, $fontfile, $text,
$value->width, $value->height,
$align, $valign);
}
/**
* Перекодировка текста
*/
function encode($text)
{
assert(is_string($text));
return iconv("WINDOWS-1251", "UTF-8", $text);
}
function setSize($new_width, $new_height)
{
$width = imagesx($this->image);
$height = imagesy($this->image);
if($new_height == false) {
$new_height = ceil($height * $new_width / $width);
}
// Resample
$image_p = imagecreatetruecolor($new_width, $new_height);
imagecopyresampled($image_p, $this->image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
// imagecopyresized($image_p, $this->image, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
$this->image = $image_p;
}
function prepare() {
if($this->prepare) {
$this->prepare = false;
foreach ($this->data as $value) {
$this->imageText($value->text, $value); // break;
}
}
}
/**
* Генерирует изображение из шаблона
*/
function render($file = null)
{
assert(is_string($file) || is_null($file));
$this->prepare();
if ($file == null) {
ob_start();
imagejpeg($this->image, $file, 100);
$result = ob_get_contents();
ob_clean();
return $result;
} else {
return imagejpeg($this->image, $file, 100);
}
}
}

14
src/tools/translit.php Normal file
View file

@ -0,0 +1,14 @@
<?php
function translit($st) {
$st = strtr($st,"абвгдеёзийклмнопрстуфхъыэ !№", "abvgdeeziyklmnoprstufh_ie__#");
$st = strtr($st,"АБВГДЕЁЗИЙКЛМНОПРСТУФХЪЫЭ", "ABVGDEEZIYKLMNOPRSTUFH_IE");
$st = strtr($st, array(
"ж"=>"zh", "ц"=>"ts", "ч"=>"ch", "ш"=>"sh",
"щ"=>"shch","ь"=>"", "ю"=>"yu", "я"=>"ya",
"Ж"=>"ZH", "Ц"=>"TS", "Ч"=>"CH", "Ш"=>"SH",
"Щ"=>"SHCH","Ь"=>"", "Ю"=>"YU", "Я"=>"YA",
"ї"=>"i", "Ї"=>"Yi", "є"=>"ie", "Є"=>"Ye"
));
return $st;
}

135
src/tree/database.php Normal file
View file

@ -0,0 +1,135 @@
<?php
//****************************************************************************
// phpDatabase 2.1
//****************************************************************************
// Author: Maxim Poltarak <maxx at e dash taller dot net>
// Category: Databases
//****************************************************************************
// The lib is FREEWARE. This means you may use it anywhere you want, you may
// do anything with it. The Author mentioned above is NOT responsible for any
// consequences of using this library.
// If you don't agree with this, you MAY NOT use the lib!
//****************************************************************************
// All improvings, feature requests, bug reports, etc. are gladly accepted.
//****************************************************************************
// Note: For best viewing of the code Tab size 4 is recommended
//****************************************************************************
class CDatabase
{
var $link;
var $db;
var $host, $user, $pass;
function CDatabase($db, $host = "localhost", $user = "", $pass = "")
{
$this->db = $db;
$this->host = $host;
$this->user = $user;
$this->pass = $pass;
$str = "host={$host} port=5432 dbname={$db} user={$user} password={$pass}";
$this->link = pg_connect($str);
}
function query($sql)
{
if (!$this->link) return 0;
return pg_query($this->link, $sql);
}
function affected_rows()
{
return pg_affected_rows($this->link);
}
function num_rows($q)
{
return pg_num_rows($q);
}
function fetch_array($q) // fetchAll
{
return pg_fetch_array($q, NULL);
}
function fetch_object($q) // fetchObject
{
return pg_fetch_object($q);
}
/* function data_seek($q, $n) {
return pg_data_seek($q, $n);
}
*/
function free_result($q)
{
return pg_free_result($q);
}
function insert_id($seq)
{
$query = "SELECT currval('$seq')";
$res = $this->query($query);
$row = pg_fetch_array($res, NULL, PGSQL_ASSOC);
pg_free_result($res);
return ($row) ? $row['currval'] : 0;
}
function error()
{
return pg_last_error($this->link);
}
function error_die($msg = '')
{
die(((empty($msg)) ? '' : $msg . ': ') . $this->error());
}
function sql2var($sql)
{
if ((empty($sql)) || (!($query = $this->query($sql)))) return false;
if ($this->num_rows($query) < 1) return false;
return $this->result2var($query);
}
function result2var($q)
{
if (!($Data = $this->fetch_array($q))) return false;
$this->free_result($q);
foreach($Data as $k => $v) $GLOBALS[$k] = $v;
return true;
}
/*function sql2array($sql, $keyField = '')
{
if ((empty($sql)) || (!($query = $this->query($sql)))) return false;
if ($this->num_rows($query) < 1) return false;
return $this->result2array($query, $keyField);
}*/
function result2array($q, $keyField = '')
{
$Result = array();
while ($Data = $this->fetch_array($q))
if (empty($keyField)) $Result[] = $Data;
else $Result[$Data[$keyField]] = $Data;
$this->free_result($q);
return $Result;
}
/*function list_tables()
{
return mysql_list_tables($this->db, $this->link);
}
function list_fields($table_name)
{
return mysql_list_fields($this->db, $table_name, $this->link);
}*/
};

636
src/tree/dbtree.php Normal file
View file

@ -0,0 +1,636 @@
<?php
//****************************************************************************
// phpDBTree 1.4
//****************************************************************************
// Author: Maxim Poltarak <maxx at e dash taller dot net>
// WWW: http://dev.e-taller.net/dbtree/
// Category: Databases
// Description: PHP class implementing a Nested Sets approach to storing
// tree-like structures in database tables. This technique was
// introduced by Joe Celko <http://www.celko.com/> and has some
// advantages over a widely used method called Adjacency Matrix.
//****************************************************************************
// The lib is FREEWARE. That means you may use it anywhere you want, you may
// do anything with it. The Author mentioned above is NOT responsible for any
// consequences of using this library.
// If you don't agree with this, you are NOT ALLOWED to use the lib!
//****************************************************************************
// You're welcome to send me all improvings, feature requests, bug reports...
//****************************************************************************
// SAMPLE DB TABLE STRUCTURE:
//
// CREATE TABLE categories (
// cat_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
// cat_left INT UNSIGNED NOT NULL,
// cat_right INT UNSIGNED NOT NULL,
// cat_level INT UNSIGNED NOT NULL,
// PRIMARY KEY(cat_id),
// KEY(cat_left, cat_right, cat_level)
// );
//
// This is believed to be the optimal Nested Sets use case. Use `one-to-one`
// relations on `cat_id` field between this `structure` table and
// another `data` table in your database.
//
// Don't forget to make a single call to clear()
// to set up the Root node in an empty table.
//
//****************************************************************************
// NOTE: Although you may use this library to retrieve data from the table,
// it is recommended to write your own queries for doing that.
// The main purpose of the library is to provide a simpler way to
// create, update and delete records. Not to SELECT them.
//****************************************************************************
//
// IMPORTANT! DO NOT create either UNIQUE or PRIMARY keys on the set of
// fields (`cat_left`, `cat_right` and `cat_level`)!
// Unique keys will destroy the Nested Sets structure!
//
//****************************************************************************
// CHANGELOG:
// 16-Apr-2003 -=- 1.1
// - Added moveAll() method
// - Added fourth parameter to the constructor
// - Renamed getElementInfo() to getNodeInfo() /keeping BC/
// - Added "Sample Table Structure" comment
// - Now it trigger_error()'s in case of any serious error, because if not you
// will lose the Nested Sets structure in your table
// 19-Feb-2004 -=- 1.2
// - Fixed a bug in moveAll() method?
// Thanks to Maxim Matyukhin for the patch.
// 13-Jul-2004 -=- 1.3
// - Changed all die()'s for a more standard trigger_error()
// Thanks to Dmitry Romakhin for pointing out an issue with
// incorrect error message call.
// 09-Nov-2004 -=- 1.4
// - Added insertNear() method.
// Thanks to Michael Krenz who sent its implementation.
// - Removed IGNORE keyword from UPDATE clauses in insert() methods.
// It was dumb to have it there all the time. Sorry. Anyway, you had
// to follow the important note mencioned above. If you hadn't, you're
// in problem.
//
//****************************************************************************
// Note: For best viewing of the code Tab size 4 is recommended
//****************************************************************************
function _case($c, $then, $else) {
return " (CASE WHEN $c THEN $then ELSE $else END) ";
}
function _between($a, $x, $y) {
return " " . $a . " BETWEEN " . $x . " AND " . $y;
}
function _inside($a, $x, $y) {
return " " . $a . " > " . $x . " AND " . $a . " < " . $y;
}
class CDBTree
{
var $db; // CDatabase class to plug to
var $table; // Table with Nested Sets implemented
var $id; // Name of the ID-auto_increment-field in the table.
// These 3 variables are names of fields which are needed to implement
// Nested Sets. All 3 fields should exist in your table!
// However, you may want to change their names here to avoid name collisions.
var $left = 'cat_left';
var $right = 'cat_right';
var $level = 'cat_level';
var $qryParams = '';
var $qryFields = '';
var $qryTables = '';
var $qryWhere = '';
var $qryGroupBy = '';
var $qryHaving = '';
var $qryOrderBy = '';
var $qryLimit = '';
var $sqlNeedReset = true;
var $sql; // Last SQL query
//************************************************************************
// Constructor
// $DB : CDatabase class instance to link to
// $tableName : table in database where to implement nested sets
// $itemId : name of the field which will uniquely identify every record
// $fieldNames : optional configuration array to set field names. Example:
// array(
// 'left' => 'cat_left',
// 'right' => 'cat_right',
// 'level' => 'cat_level'
// )
function CDBTree(&$DB, $tableName, $itemId, $seq, $fieldNames = array())
{
if (empty($tableName)) trigger_error("phpDbTree: Unknown table", E_USER_ERROR);
if (empty($itemId)) trigger_error("phpDbTree: Unknown ID column", E_USER_ERROR);
$this->seq = $seq;
$this->db = $DB;
$this->table = $tableName;
$this->id = $itemId;
if (is_array($fieldNames) && sizeof($fieldNames))
foreach($fieldNames as $k => $v) $this->$k = $v;
}
//************************************************************************
// Returns a Left and Right IDs and Level of an element or false on error
// $ID : an ID of the element
function getElementInfo($ID)
{
return $this->getNodeInfo($ID);
}
function getNodeInfo($ID)
{
$this->sql = 'SELECT ' . $this->left . ',' . $this->right . ',' . $this->level . ' FROM ' . $this->table . ' WHERE ' . $this->id . '=\'' . $ID . '\'';
// print_r($this->sql);
if (($query = $this->db->query($this->sql)) && ($this->db->num_rows($query) == 1) && ($Data = $this->db->fetch_array($query))) return array(
(int)$Data[$this->left],
(int)$Data[$this->right],
(int)$Data[$this->level]
);
else trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR); // throw new Exception();
}
//************************************************************************
// Clears table and creates 'root' node
// $data : optional argument with data for the root node
function clear($data = array())
{
// clearing table
if ((!$this->db->query('TRUNCATE ' . $this->table)) && (!$this->db->query('DELETE FROM ' . $this->table))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
// preparing data to be inserted
if (sizeof($data)) {
$fld_names = implode(',', array_keys($data)) . ',';
if (sizeof($data)) $fld_values = '\'' . implode('\',\'', array_values($data)) . '\',';
}
$fld_names.= $this->left . ',' . $this->right . ',' . $this->level;
$fld_values.= '1,2,0';
// inserting new record
$this->sql = 'INSERT INTO ' . $this->table . '(' . $fld_names . ') VALUES(' . $fld_values . ')';
if (!($this->db->query($this->sql))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
return $this->db->insert_id($this->seq);
}
//************************************************************************
// Updates a record
// $ID : element ID
// $data : array with data to update: array(<field_name> => <fields_value>)
function update($ID, $data)
{
$sql_set = '';
foreach($data as $k => $v) $sql_set.= ',' . $k . '=\'' . addslashes($v) . '\'';
return $this->db->query('UPDATE ' . $this->table . ' SET ' . substr($sql_set, 1) . ' WHERE ' . $this->id . '=\'' . $ID . '\'');
}
//************************************************************************
// Inserts a record into the table with nested sets
// $ID : an ID of the parent element
// $data : array with data to be inserted: array(<field_name> => <field_value>)
// Returns : true on success, or false on error
function insert($ID, $data)
{
if (!(list($leftId, $rightId, $level) = $this->getNodeInfo($ID))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
// preparing data to be inserted
if (sizeof($data)) {
$fld_names = implode(',', array_keys($data)) . ',';
$fld_values = '\'' . implode('\',\'', array_values($data)) . '\',';
}
$fld_names.= $this->left . ',' . $this->right . ',' . $this->level;
$fld_values.= ($rightId) . ',' . ($rightId+1) . ',' . ($level+1);
// creating a place for the record being inserted
if ($ID) {
$this->sql = 'UPDATE ' . $this->table . ' SET ' . $this->left . '=' . _case($this->left . '>' . $rightId, $this->left . '+2', $this->left) . ',' . $this->right . '=' . _case($this->right . '>=' . $rightId, $this->right . '+2', $this->right) . 'WHERE ' . $this->right . '>=' . $rightId;
if (!($this->db->query($this->sql))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
}
// inserting new record
$this->sql = 'INSERT INTO ' . $this->table . '(' . $fld_names . ') VALUES(' . $fld_values . ')';
if (!($this->db->query($this->sql))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
return $this->db->insert_id($this->seq);
}
//************************************************************************
// Inserts a record into the table with nested sets
// $ID : ID of the element after which (i.e. at the same level) the new element
// is to be inserted
// $data : array with data to be inserted: array(<field_name> => <field_value>)
// Returns : true on success, or false on error
function insertNear($ID, $data)
{
if (!(list($leftId, $rightId, $level) = $this->getNodeInfo($ID))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
// preparing data to be inserted
if (sizeof($data)) {
$fld_names = implode(',', array_keys($data)) . ',';
$fld_values = '\'' . implode('\',\'', array_values($data)) . '\',';
}
$fld_names.= $this->left . ',' . $this->right . ',' . $this->level;
$fld_values.= ($rightId+1) . ',' . ($rightId+2) . ',' . ($level);
// creating a place for the record being inserted
if ($ID) {
$this->sql = 'UPDATE ' . $this->table . ' SET ' . $this->left . '=' . _case($this->left . '>' . $rightId, $this->left . '+2', $this->left) . $this->right . '=' . _case($this->right . '>' . $rightId, $this->right . '+2', $this->right) . 'WHERE ' . $this->right . '>' . $rightId;
if (!($this->db->query($this->sql))) trigger_error("phpDbTree error:" . $this->db->error() , E_USER_ERROR);
}
// inserting new record
$this->sql = 'INSERT INTO ' . $this->table . '(' . $fld_names . ') VALUES(' . $fld_values . ')';
if (!($this->db->query($this->sql))) trigger_error("phpDbTree error:" . $this->db->error() , E_USER_ERROR);
return $this->db->insert_id($this->seq);
}
//************************************************************************
// Assigns a node with all its children to another parent
// $ID : node ID
// $newParentID : ID of new parent node
// Returns : false on error
function moveAll($ID, $newParentId)
{
if (!(list($leftId, $rightId, $level) = $this->getNodeInfo($ID))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
if (!(list($leftIdP, $rightIdP, $levelP) = $this->getNodeInfo($newParentId))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
if ($ID == $newParentId || $leftId == $leftIdP || ($leftIdP >= $leftId && $leftIdP <= $rightId)) return false;
// whether it is being moved upwards along the path
if ($leftIdP < $leftId && $rightIdP > $rightId && $levelP < $level-1) {
$this->sql = 'UPDATE ' . $this->table . ' SET '
. $this->level . '=' . _case($this->left . ' BETWEEN ' . $leftId . ' AND ' . $rightId, $this->level . sprintf('%+d', -($level-1) +$levelP) , $this->level) . ","
. $this->right . '=' . _case($this->right . ' BETWEEN ' . ($rightId+1) . ' AND ' . ($rightIdP-1) , $this->right . '-' . ($rightId-$leftId+1)
, _case($this->left . ' BETWEEN ' . ($leftId) . ' AND ' . ($rightId) , $this->right . '+' . ((($rightIdP-$rightId-$level+$levelP) /2) *2+$level-$levelP-1) , $this->right)) . ","
. $this->left . '=' . _case($this->left . ' BETWEEN ' . ($rightId+1) . ' AND ' . ($rightIdP-1) , $this->left . '-' . ($rightId-$leftId+1) , _case($this->left . ' BETWEEN ' . $leftId . ' AND ' . ($rightId) , $this->left . '+' . ((($rightIdP-$rightId-$level+$levelP) /2) *2+$level-$levelP-1) , $this->left)) . 'WHERE ' . $this->left . ' BETWEEN ' . ($leftIdP+1) . ' AND ' . ($rightIdP-1);
} elseif ($leftIdP < $leftId) {
$this->sql = 'UPDATE ' . $this->table . ' SET '
. $this->level . '=' . _case($this->left . ' BETWEEN ' . $leftId . ' AND ' . $rightId, $this->level . sprintf('%+d', -($level-1) +$levelP) , $this->level) . ","
. $this->left . '=' . _case($this->left . ' BETWEEN ' . $rightIdP . ' AND ' . ($leftId-1) , $this->left . '+' . ($rightId-$leftId+1)
, _case($this->left . ' BETWEEN ' . $leftId . ' AND ' . $rightId, $this->left . '-' . ($leftId-$rightIdP) , $this->left)) . ","
. $this->right . '=' . _case($this->right . ' BETWEEN ' . $rightIdP . ' AND ' . $leftId, $this->right . '+' . ($rightId-$leftId+1) , _case($this->right . ' BETWEEN ' . $leftId . ' AND ' . $rightId, $this->right . '-' . ($leftId-$rightIdP) , $this->right)) . 'WHERE ' . $this->left . ' BETWEEN ' . $leftIdP . ' AND ' . $rightId
// !!! added this line (Maxim Matyukhin)
. ' OR ' . $this->right . ' BETWEEN ' . $leftIdP . ' AND ' . $rightId;
} else {
$this->sql = 'UPDATE ' . $this->table . ' SET '
. $this->level . '=' . _case($this->left . ' BETWEEN ' . $leftId . ' AND ' . $rightId, $this->level . sprintf('%+d', -($level-1) +$levelP) , $this->level) . ","
. $this->left . '=' . _case($this->left . ' BETWEEN ' . $rightId . ' AND ' . $rightIdP, $this->left . '-' . ($rightId-$leftId+1)
, _case($this->left . ' BETWEEN ' . $leftId . ' AND ' . $rightId, $this->left . '+' . ($rightIdP-1-$rightId), $this->left)) . ", "
. $this->right . '=' . _case($this->right . ' BETWEEN ' . ($rightId+1) . ' AND ' . ($rightIdP-1) , $this->right . '-' . ($rightId-$leftId+1) , _case($this->right . ' BETWEEN ' . $leftId . ' AND ' . $rightId, $this->right . '+' . ($rightIdP-1-$rightId) , $this->right)) . 'WHERE ' . $this->left . ' BETWEEN ' . $leftId . ' AND ' . $rightIdP
// !!! added this line (Maxim Matyukhin)
. ' OR ' . $this->right . ' BETWEEN ' . $leftId . ' AND ' . $rightIdP;
}
return $this->db->query($this->sql) or trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
}
// Перемещение всех детей ветки в другую ветку
function moveChildren($ID, $newParentId)
{
if (!(list($leftId, $rightId, $level) = $this->getNodeInfo($ID))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
if (!(list($leftIdP, $rightIdP, $levelP) = $this->getNodeInfo($newParentId))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
if ($ID == $newParentId || $leftId == $leftIdP || ($leftIdP >= $leftId && $leftIdP <= $rightId)) return false;
// whether it is being moved upwards along the path
if ($leftIdP < $leftId && $rightIdP > $rightId && $levelP < $level-1) {
// _update($this->table, array(), )
$this->sql = 'UPDATE ' . $this->table . ' SET '
// Меняем уровень
. $this->level . '=' .
_case(_between($this->left, $leftId, $rightId),
$this->level . sprintf('%+d', -($level-1) +$levelP) , $this->level)
// Меняем границы
. $this->left . '=' .
_case(_beetween($this->left, $rightId+1, $rightIdP-1), $this->left . '-' . $rightId-$leftId+1 ,
_case(_between($this->left, $leftId, $rightId), $this->left . '+' . ((($rightIdP-$rightId-$level+$levelP) /2) *2+$level-$levelP-1) , $this->left))
. $this->right . '=' .
_case(_between($this->right, $rightId+1, $rightIdP-1), $this->right . '-' . ($rightId-$leftId+1) ,
_case(_between($this->left, $leftId, $rightId), $this->right . '+' . ((($rightIdP-$rightId-$level+$levelP) /2) *2+$level-$levelP-1) , $this->right))
. 'WHERE ' . _between($this->left, ($leftIdP+1), ($rightIdP-1));
} elseif ($leftIdP < $leftId) {
$this->sql = 'UPDATE ' . $this->table . ' SET '
. $this->level . '=' .
_case(_between($this->left, $leftId, $rightId),
$this->level . sprintf('%+d', -($level-1) +$levelP) , $this->level)
. $this->left . '=' .
_case(_between($this->left, $rightIdP, $leftId-1), $this->left . '+' . ($rightId-$leftId+1),
_case(_between($this->left, $leftId, $rightId), $this->left . '-' . ($leftId-$rightIdP) , $this->left))
. $this->right . '=' .
_case(_between($this->right, $rightIdP, $leftId), $this->right . '+' . ($rightId-$leftId+1),
_case(_between($this->right, $leftId, $rightId), $this->right . '-' . ($leftId-$rightIdP) , $this->right))
. 'WHERE ' . _between($this->left, $leftIdP, $rightId)
// !!! added this line (Maxim Matyukhin)
. ' OR ' . _between($this->right, $leftIdP, $rightId);
} else {
$this->sql = 'UPDATE ' . $this->table . ' SET '
. $this->level . '='
. _case(_between($this->left, $leftId, $rightId),
$this->level . sprintf('%+d', -($level-1) +$levelP) , $this->level)
. $this->left . '=' .
_case(_between($this->left, $rightId, $rightIdP), $this->left . '-' . ($rightId-$leftId+1),
_case(_between($this->left, $leftId, $rightId), $this->left . '+' . ($rightIdP-1-$rightId), $this->left))
. $this->right . '=' .
_case(_between($this->right, $rightId+1, $rightIdP-1), $this->right . '-' . ($rightId-$leftId+1),
_case(_between($this->right, $leftId, $rightId), $this->right . '+' . ($rightIdP-1-$rightId) , $this->right))
. 'WHERE ' . _between($this->left, $leftId, $rightIdP)
// !!! added this line (Maxim Matyukhin)
. ' OR ' . _between($this->right, $leftId, $rightIdP);
}
return $this->db->query($this->sql) or trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
}
//************************************************************************
// Deletes a record wihtout deleting its children
// $ID : an ID of the element to be deleted
// Returns : true on success, or false on error
function delete($ID)
{
if (!(list($leftId, $rightId, $level) = $this->getNodeInfo($ID))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
// Deleting record
$this->sql = 'DELETE FROM ' . $this->table . ' WHERE ' . $this->id . '=\'' . $ID . '\'';
if (!$this->db->query($this->sql)) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
// Clearing blank spaces in a tree
$this->sql = 'UPDATE ' . $this->table . ' SET '
. $this->left . '=' . _case($this->left . ' BETWEEN ' . $leftId . ' AND ' . $rightId, $this->left . '-1', $this->left) . ", "
. $this->right . '=' . _case($this->right . ' BETWEEN ' . $leftId . ' AND ' . $rightId, $this->right . '-1', $this->right) . ", "
. $this->level . '=' . _case($this->left . ' BETWEEN ' . $leftId . ' AND ' . $rightId, $this->level . '-1', $this->level) . ", "
. $this->left . '=' . _case($this->left . '>' . $rightId, $this->left . '-2', $this->left) . ", "
. $this->right . '=' . _case($this->right . '>' . $rightId, $this->right . '-2', $this->right)
. ' WHERE ' . $this->right . '>' . $leftId;
if (!$this->db->query($this->sql)) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
return true;
}
//************************************************************************
// Deletes a record with all its children
// $ID : an ID of the element to be deleted
// Returns : true on success, or false on error
function deleteAll($ID)
{
if (!(list($leftId, $rightId, $level) = $this->getNodeInfo($ID))) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
// Deleteing record(s)
$this->sql = 'DELETE FROM ' . $this->table . ' WHERE ' . $this->left . ' BETWEEN ' . $leftId . ' AND ' . $rightId;
if (!$this->db->query($this->sql)) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
// Clearing blank spaces in a tree
$deltaId = ($rightId-$leftId) +1;
$this->sql = 'UPDATE ' . $this->table . ' SET '
. $this->left . '=' . _case($this->left . '>' . $leftId, $this->left . '-' . $deltaId, $this->left) . ", "
. $this->right . '=' . _case($this->right . '>' . $leftId, $this->right . '-' . $deltaId, $this->right)
. ' WHERE ' . $this->right . '>' . $rightId;
if (!$this->db->query($this->sql)) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
return true;
}
//************************************************************************
// Enumerates children of an element
// $ID : an ID of an element which children to be enumerated
// $start_level : relative level from which start to enumerate children
// $end_level : the last relative level at which enumerate children
// 1. If $end_level isn't given, only children of
// $start_level levels are enumerated
// 2. Level values should always be greater than zero.
// Level 1 means direct children of the element
// Returns : a result id for using with other DB functions
function enumChildrenAll($ID)
{
return $this->enumChildren($ID, 1, 0);
}
function enumChildren($ID, $start_level = 1, $end_level = 1)
{
if ($start_level < 0) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
// We could use sprintf() here, but it'd be too slow
$whereSql1 = ' AND ' . $this->table . '.' . $this->level;
$whereSql2 = '_' . $this->table . '.' . $this->level . '+';
if (!$end_level) $whereSql = $whereSql1 . '>=' . $whereSql2 . (int)$start_level;
else {
$whereSql = ($end_level <= $start_level) ? $whereSql1 . '=' . $whereSql2 . (int)$start_level : ' AND ' . $this->table . '.' . $this->level . ' BETWEEN _' . $this->table . '.' . $this->level . '+' . (int)$start_level . ' AND _' . $this->table . '.' . $this->level . '+' . (int)$end_level;
}
$this->sql = $this->sqlComposeSelect(array(
'', // Params
'', // Fields
$this->table . ' _' . $this->table . ', ' . $this->table, // Tables
'_' . $this->table . '.' . $this->id . '=\'' . $ID . '\'' . ' AND ' . $this->table . '.' . $this->left . ' BETWEEN _' . $this->table . '.' . $this->left . ' AND _' . $this->table . '.' . $this->right . $whereSql
));
return $this->db->query($this->sql);
}
function enumChildrenArray($ID, $start_level = 1, $end_level = 1)
{
return $this->db->result2array($this->enumChildren($ID, $start_level, $end_level));
}
//************************************************************************
// Enumerates the PATH from an element to its top level parent
// $ID : an ID of an element
// $showRoot : whether to show root node in a path
// Returns : a result id for using with other DB functions
function enumPath($ID, $showRoot = false)
{
$this->sql = $this->sqlComposeSelect(array(
'', // Params
'', // Fields
$this->table . ' _' . $this->table . ', ' . $this->table, // Tables
'_' . $this->table . '.' . $this->id . '=\'' . $ID . '\'' . ' AND _' . $this->table . '.' . $this->left . ' BETWEEN ' . $this->table . '.' . $this->left . ' AND ' . $this->table . '.' . $this->right . (($showRoot) ? '' : ' AND ' . $this->table . '.' . $this->level . '>0') , // Where
'', // GroupBy
'', // Having
$this->table . '.' . $this->left // OrderBy
));
return $this->db->query($this->sql);
}
function enumPathArray($ID, $showRoot = false)
{
return $this->db->result2array($this->enumPath($ID, $showRoot));
}
//************************************************************************
// Returns query result to fetch data of the element's parent
// $ID : an ID of an element which parent to be retrieved
// $level : Relative level of parent
// Returns : a result id for using with other DB functions
function getParent($ID, $level = 1)
{
if ($level < 1) trigger_error("phpDbTree error: " . $this->db->error() , E_USER_ERROR);
$this->sql = $this->sqlComposeSelect(array(
'', // Params
'', // Fields
$this->table . ' _' . $this->table . ', ' . $this->table, // Tables
'_' . $this->table . '.' . $this->id . '=\'' . $ID . '\'' . ' AND _' . $this->table . '.' . $this->left . ' BETWEEN ' . $this->table . '.' . $this->left . ' AND ' . $this->table . '.' . $this->right . ' AND ' . $this->table . '.' . $this->level . '=_' . $this->table . '.' . $this->level . '-' . (int)$level // Where
));
$result = $this->db->result2array($this->db->query($this->sql));
return (int)$result[0][$this->id];
}
//************************************************************************
function sqlReset()
{
$this->qryParams = '';
$this->qryFields = '';
$this->qryTables = '';
$this->qryWhere = '';
$this->qryGroupBy = '';
$this->qryHaving = '';
$this->qryOrderBy = '';
$this->qryLimit = '';
return true;
}
//************************************************************************
function sqlSetReset($resetMode)
{
$this->sqlNeedReset = ($resetMode) ? true : false;
}
//************************************************************************
function sqlParams($param = '')
{
return (empty($param)) ? $this->qryParams : $this->qryParams = $param;
}
function sqlFields($param = '')
{
return (empty($param)) ? $this->qryFields : $this->qryFields = $param;
}
function sqlSelect($param = '')
{
return $this->sqlFields($param);
}
function sqlTables($param = '')
{
return (empty($param)) ? $this->qryTables : $this->qryTables = $param;
}
function sqlFrom($param = '')
{
return $this->sqlTables($param);
}
function sqlWhere($param = '')
{
return (empty($param)) ? $this->qryWhere : $this->qryWhere = $param;
}
function sqlGroupBy($param = '')
{
return (empty($param)) ? $this->qryGroupBy : $this->qryGroupBy = $param;
}
function sqlHaving($param = '')
{
return (empty($param)) ? $this->qryHaving : $this->qryHaving = $param;
}
function sqlOrderBy($param = '')
{
return (empty($param)) ? $this->qryOrderBy : $this->qryOrderBy = $param;
}
function sqlLimit($param = '')
{
return (empty($param)) ? $this->qryLimit : $this->qryLimit = $param;
}
//************************************************************************
function sqlComposeSelect($arSql)
{
$joinTypes = array(
'join' => 1,
'cross' => 1,
'inner' => 1,
'straight' => 1,
'left' => 1,
'natural' => 1,
'right' => 1
);
$this->sql = 'SELECT ' . $arSql[0] . ' ';
if (!empty($this->qryParams)) $this->sql.= $this->sqlParams . ' ';
if (empty($arSql[1]) && empty($this->qryFields)) $this->sql.= $this->table . '.' . $this->id;
else {
if (!empty($arSql[1])) $this->sql.= $arSql[1];
if (!empty($this->qryFields)) $this->sql.= ((empty($arSql[1])) ? '' : ',') . $this->qryFields;
}
$this->sql.= ' FROM ';
// $tblAr = array(0 => 'join');
$isJoin = ($tblAr = explode(' ', trim($this->qryTables)))
&& /*($joinTypes[strtolower($tblAr[0]) ])*/ 1;
if (empty($arSql[2]) && empty($this->qryTables)) $this->sql.= $this->table;
else {
if (!empty($arSql[2])) $this->sql.= $arSql[2];
if (!empty($this->qryTables)) {
if (!empty($arSql[2])) $this->sql.= (($isJoin) ? ' ' : ',');
elseif ($isJoin) $this->sql.= $this->table . ' ';
$this->sql.= $this->qryTables;
}
}
if ((!empty($arSql[3])) || (!empty($this->qryWhere))) {
$this->sql.= ' WHERE ' . $arSql[3] . ' ';
if (!empty($this->qryWhere)) $this->sql.= (empty($arSql[3])) ? $this->qryWhere : 'AND(' . $this->qryWhere . ')';
}
if ((!empty($arSql[4])) || (!empty($this->qryGroupBy))) {
$this->sql.= ' GROUP BY ' . $arSql[4] . ' ';
if (!empty($this->qryGroupBy)) $this->sql.= (empty($arSql[4])) ? $this->qryGroupBy : ',' . $this->qryGroupBy;
}
if ((!empty($arSql[5])) || (!empty($this->qryHaving))) {
$this->sql.= ' HAVING ' . $arSql[5] . ' ';
if (!empty($this->qryHaving)) $this->sql.= (empty($arSql[5])) ? $this->qryHaving : 'AND(' . $this->qryHaving . ')';
}
if ((!empty($arSql[6])) || (!empty($this->qryOrderBy))) {
$this->sql.= ' ORDER BY ' . (isset($arSql[6]) ? $arSql[6] : '') . ' ';
if (!empty($this->qryOrderBy)) $this->sql.= (empty($arSql[6])) ? $this->qryOrderBy : ',' . $this->qryOrderBy;
}
if (!empty($arSql[7])) $this->sql.= ' LIMIT ' . $arSql[7];
elseif (!empty($this->qryLimit)) $this->sql.= ' LIMIT ' . $this->qryLimit;
if ($this->sqlNeedReset) $this->sqlReset();
return $this->sql;
}
//************************************************************************
}

79
src/tree/sort.php Normal file
View file

@ -0,0 +1,79 @@
<?php
/**
* Сортировка дерева в представлении Nested Set
* Для дерева которое хранится в базе данных используя представление Nested Set нет возможности отсортировать элементы дерева по
* произвольному полю. Поэтому после извлечения дерева из базы данных оно преобразуется в обычное представление сортируется и обратно
*
* Пример:
* $sort = new NestedSetSort();
* $data = $sort->sortBy($data, 'name');
*/
class NestedSetSort {
private $data = array();
private $result = array();
private $sortBy = '';
public function __construct() {
}
// Преобразуем Nested Set в дерево и сортируем
private function listTree(array $tree, $offset, $level) {
$result = array();
for ($i = $offset; $i < sizeof($tree); $i++) {
$leaf = $tree[$i];
$clevel = $leaf['cat_level'];
if ($clevel == $level) {
$result [] = array($i);
} else if ($clevel > $level) {
list($subtree, $i) = $this->listTree($tree, $i, $clevel);
$i--;
$result[sizeof($result) - 1][1] = $subtree;
} else {
$this->sortList($result, $tree);
return array($result, $i);
}
}
$this->sortList($result, $tree);
return array($result, $i);
}
// Сравнение двух элементов
private function compare($a, $b) {
$a1 = $this->data[$a[0]][$this->sortBy];
$b1 = $this->data[$b[0]][$this->sortBy];
return strcmp($a1, $b1);
}
// Сортировка списка
private function sortList(array &$list, $data) {
usort($list, array($this, 'compare'));
}
// Создает дерево в виде списка
private function reorder(array $tree) {
foreach($tree as $node) {
$this->result[] = $this->data[$node[0]];
if (isset($node[1])) {
$this->reorder($node[1]);
}
}
}
public function sortBy0(array $data, $sortBy)
{
$this->data = $data;
$this->sortBy = $sortBy;
$order = $this->listTree($data, 0, 0);
return $order[0];
}
// Сортировка по заданному полю
public function sortBy(array $data, $sortBy) {
$this->data = $data;
$this->sortBy = $sortBy;
$order = $this->listTree($data, 0, 0);
$this->reorder($order[0]);
return $this->result;
}
}

View file

@ -0,0 +1,45 @@
<?php
abstract class Rule_Abstract
{
public $field;
protected $errorMsg;
protected $ctx;
public function __construct($field, $errorMsg = false)
{
$this->field = $field;
$this->errorMsg = $errorMsg;
}
public function setName($field)
{
$this->field = $field;
return $this;
}
public function setErrorMsg($errorMsg)
{
$this->errorMsg = $errorMsg;
return $this;
}
public function getErrorMsg()
{
return $this->errorMsg;
}
public function isValid(Collection $container, $status = null)
{
return true;
}
function skipEmpty() {
return true;
}
public function setContext($ctx)
{
$this->ctx = $ctx;
}
}

View file

@ -0,0 +1,2 @@
<?php
foreach (glob(dirname(__FILE__) . "/*.php") as $file) { require_once $file; }

View file

@ -0,0 +1,20 @@
<?php
require_once 'abstract.php';
/**
* Проверка на число
*/
class Rule_Alpha extends Rule_Abstract
{
public function getErrorMsg()
{
return "Поле должно содержать только буквы";
}
public function isValid(Collection $container, $status = null)
{
return ctype_alpha($container->get($this->field));
}
}

View file

@ -0,0 +1,59 @@
<?php
require_once 'abstract.php';
/**
* Проверка формата электронной почты
*/
class Rule_Code extends Rule_Abstract
{
public function getErrorMsg()
{
return "Неправильно указан персональный код";
}
function checkCode($code) {
foreach($code as $c) {
if (empty($c)) {
return false;
}
}
return true;
}
public function isValid(Collection $container, $status = null)
{
if ($status == 'update') return true;
$name = $this->field;
if (is_array($_POST[$name . '_code_genre'])) {
for($n = 0; $n < count($_POST[$name . '_code_genre']); $n++) {
$code = array(
$_POST[$name . '_code_genre'][$n],
$_POST[$name . '_code_f'][$n],
$_POST[$name . '_code_i'][$n],
$_POST[$name . '_code_o'][$n],
$_POST[$name . '_code_year'][$n],
$_POST[$name . '_code_month'][$n],
$_POST[$name . '_code_day'][$n]
);
if (!$this->checkCode($code)) {
return false;
}
}
return true;
} else {
$code = array(
$_POST[$name . '_code_genre'],
$_POST[$name . '_code_f'],
$_POST[$name . '_code_i'],
$_POST[$name . '_code_o'],
$_POST[$name . '_code_year'],
$_POST[$name . '_code_month'],
$_POST[$name . '_code_day']
);
return $this->checkCode($code);
}
}
}

View file

@ -0,0 +1,34 @@
<?php
require_once 'abstract.php';
/**
* Проверка формата даты
*/
class Rule_Count extends Rule_Abstract
{
public $size = 1;
public $max = false;
public function getErrorMsg()
{
return "Количество записей должно быть не менне {$this->size} и не более {$this->max}";
}
function not_empty($s) {
return $s != "";
}
public function isValid(Collection $container, $status = null)
{
if (!$this->max) {
$this->max = $this->size;
}
$count = count(array_filter(array_map('trim',
explode(";", $container->get($this->field))), array($this, 'not_empty')));
return $count >= $this->size && $count <= $this->max;
}
}

View file

@ -0,0 +1,24 @@
<?php
require_once 'abstract.php';
/**
* Проверка формата даты
*/
class Rule_Date extends Rule_Abstract
{
private $split = "\\/";
public function getErrorMsg()
{
return "Неверный формат даты";
}
public function isValid(Collection $container, $status = null)
{
$pattern = "/^([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})$/";
return (preg_match($pattern, $container->get($this->field), $matches)
&& checkdate($matches[2], $matches[1], $matches[3]));
}
}

View file

@ -0,0 +1,30 @@
<?php
require_once 'abstract.php';
/**
* Проверка формата электронной почты
*/
class Rule_Email extends Rule_Abstract
{
public function getErrorMsg()
{
return "Неверный формат электронной почты";
}
public function isValid(Collection $container, $status = null)
{
$user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\|\{\}~\']+';
$doIsValid = '(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]\.?)+';
$ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}';
$ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}';
$emails = explode(",", $container->get($this->field));
foreach ($emails as $email) {
// if (! preg_match("/^$user@($doIsValid|(\[($ipv4|$ipv6)\]))$/", $email)) return false;
if (! filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
}
return true;
}
}

View file

@ -0,0 +1,28 @@
<?php
require_once 'abstract.php';
/**
* Проверка формата электронной почты
*/
class Rule_EmailList extends Rule_Abstract
{
public function getErrorMsg()
{
return "Неверный формат электронной почты";
}
public function isValid(Collection $container, $status = null) {
$user = '[a-zA-Z0-9_\-\.\+\^!#\$%&*+\/\=\?\|\{\}~\']+';
$doIsValid = '(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]\.?)+';
$ipv4 = '[0-9]{1,3}(\.[0-9]{1,3}){3}';
$ipv6 = '[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7}';
$emails = $container->get($this->field);
foreach ($emails as $email) {
// if (! preg_match("/^$user@($doIsValid|(\[($ipv4|$ipv6)\]))$/", $email)) return false;
if (! filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
}
return true;
}
}

View file

@ -0,0 +1,51 @@
<?php
require_once 'abstract.php';
/**
* Проверка формата времени
*/
class Rule_IsFile extends Rule_Abstract
{
private $type = array();
private $maxsize = 1024;
function skipEmpty() {
return false;
}
function setSize($size) {
$this->maxsize = $size;
}
function setType(array $type) {
$this->type = $type;
}
public function isValid(Collection $container, $status = null)
{
if (!isset($_FILES[$this->field]) || $_FILES[$this->field]['error'] == UPLOAD_ERR_NO_FILE) {
$this->setErrorMsg('Файл не загружен');
return false;
}
if ($_FILES[$this->field]['error'] == UPLOAD_ERR_INI_SIZE) {
$this->setErrorMsg('Превышен размер файла');
return false;
}
$tmp = $_FILES[$this->field];
if (!in_array($tmp['type'], $this->type)) {
$this->setErrorMsg('Неверный формат файла');
return false;
}
if ($tmp['size'] > $this->maxsize*1024) {
$this->setErrorMsg('Неверный размер файла');
return false;
}
return true;
}
}

View file

@ -0,0 +1,28 @@
<?php
require_once 'abstract.php';
/**
* Проверка на равентство двух полей
*/
class Rule_Match extends Rule_Abstract
{
public $same;
public function getErrorMsg()
{
return "Поля не совпадают";
}
/* public function __construct($field, $refField, $errorMsg)
{
$this->field = $field;
$this->refField = $refField;
$this->errorMsg = $errorMsg;
}
*/
public function isValid(Collection $container, $status = null) {
return (strcmp($container->get($this->field), $container->get($this->same)) == 0);
}
}

View file

@ -0,0 +1,29 @@
<?php
require_once 'abstract.php';
class Rule_Notnull extends Rule_Abstract
{
function skipEmpty() {
return false;
}
public function getErrorMsg()
{
return "Поле не должно быть пустым";
}
public function isValid(Collection $container, $status = null)
{
$data = $container->get($this->field);
if (is_array($data)) {
foreach($data as $c) {
if (trim($c) != '') return true;
}
return false;
} else {
$value = trim($data);
return $value != '';
}
}
}

View file

@ -0,0 +1,19 @@
<?php
require_once 'abstract.php';
/**
* Проверка на число
*/
class Rule_Numeric extends Rule_Abstract
{
public function getErrorMsg()
{
return "Значение поля должно быть числом";
}
public function isValid(Collection $container, $status = null)
{
return (is_numeric($container->get($this->field)));
}
}

View file

@ -0,0 +1,35 @@
<?php
require_once 'abstract.php';
/**
* Проверка формата времени
*/
class Rule_Time extends Rule_Abstract
{
private $split = ":";
public function getErrorMsg()
{
return "Неверный формат времени";
}
static function checktime($hour, $minute)
{
if ($hour > -1 && $hour < 24 && $minute > -1 && $minute < 60) {
return true;
}
}
public function isValid(Collection $container, $status = null)
{
$tmp = explode($this->split, $container->get($this->field), 2);
if ($tmp) {
if (self::checktime ($tmp[0], $tmp[1])) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,20 @@
<?php
require_once 'abstract.php';
/**
* Проверка формата времени
*/
class Rule_Unique extends Rule_Abstract
{
public function getErrorMsg()
{
return $this->ctx->getMessage();
}
public function isValid(Collection $container, $status = null)
{
return $this->ctx->isUnique($container->get($this->field), $status);
}
}

126
src/validator/validator.php Normal file
View file

@ -0,0 +1,126 @@
<?php
require_once "core/validator/rule/all.php";
/**
* Проверка коллекции
*/
class Validator
{
protected $chain = array(); // Массив правил
protected $errorMsg = array(); // Массив ошибок
function __construct($rules = array()) {
$this->addRuleList($rules);
}
/**
* Добавление списка правил в специальном формате
* array(array('name' => fieldname, 'validate' => ruletext), ...)
* fieldname - Имя переменой для проверки
* ruletext - Описание правила см. формат правила ниже
*/
public function addRuleList(array $input)
{
$type = array(
'date' => 'Rule_Date',
'email' => 'Rule_Email',
'emaillist'=> 'Rule_EmailList',
'match' => 'Rule_Match',
'time' => 'Rule_Time',
'alpha' => 'Rule_Alpha',
'require' => 'Rule_Notnull',
'numeric' => 'Rule_Numeric',
'unique' => 'Rule_Unique',
'count' => 'Rule_Count',
'isfile' => 'Rule_IsFile',
'code' => 'Rule_Code'
);
// Разбор правила проверки
// Формат правила 'rule1|rule2,param1=value1|rule3,param1=value1,param2=value2'
foreach ($input as $value) {
// Список правил
if (!isset($value['validate']) || $value['validate'] == '') continue;
$rules = explode("|", $value['validate']);
foreach ($rules as $rule) {
// Список параметров правила
$rule_param = explode(",", $rule);
$name = array_shift($rule_param);
if (isset($type[$name])) {
$constructor = $type[$name]; // "Rule_" . ucfirst($name)
$rule = new $constructor($value['name'], false); // Нужны шаблонные сообщения для правил
if (isset($value['context'])) {
$rule->setContext($value['context']);
}
foreach ($rule_param as $param) {
// Имя и значение параметра
list($name, $value) = explode("=", $param);
$rule->$name = $value;
}
$this->addRule($rule);
} else {
throw new Exception('Unknown validation rule "' . $rule . "'");
}
}
}
}
public function addRule(&$rule)
{
if (is_array($rule)) {
$this->chain = array_merge($this->chain, $rule);
} else {
$this->chain[] = $rule;
}
}
public function skip($rule, $container) // -> Rule_Abstract
{
if ($rule->skipEmpty()) {
$data = $container->get($rule->field);
if (!is_array($data)) {
$value = trim($data);
return $value == '';
}
}
return false;
}
public function validate(Collection $container, $rule = null, $status = null)
{
if ($rule) {
$this->chain = $rule;
}
$this->errorMsg = array();
foreach ($this->chain as $key => $rule) {
if (!$this->skip($rule, $container) && !$rule->isValid($container, $status)) {
$name = $rule->field;
$this->errorMsg[$name] = $rule->getErrorMsg();
}
}
return $this->isValid();
}
public function addError($name, $message)
{
$this->errorMsg[$name] = $message;
}
public function isError()
{
return !empty($this->errorMsg);
}
public function isValid()
{
return empty($this->errorMsg);
}
public function getErrorMsg()
{
return $this->errorMsg;
}
}

298
src/view/compositeview.php Normal file
View file

@ -0,0 +1,298 @@
<?php
class _View_Composite // AbstractCompositeView
{
protected $_section = array(); // Вложенные шаблоны
// Блоки
protected $_stylesheet = array(); // Массив стилей текущего шаблона
protected $_script = array(); // Массив скриптов текущего шаблона
protected $_scriptstring = array();
protected $_startup = array();
protected $_title = null; // Заголовок текущего шаблона
public $alias = array();
function __construct()
{
}
/**
* Связывет переменную с вложенным шаблоном
*
* @param string $section переменная шаблона
* @param CompositeView $view вложенный шаблон
*/
public function setView($section, /*CompositeView*/ $view)
{
$this->_section [$section] = $view;
}
public function jGrowl($message, $args)
{
$this->addScriptRaw('$.jGrowl("' . $message . '", ' . json::encode($args) . ");\n", true);
}
public function setGlobal($name, $args)
{
$this->addScriptRaw("var " . $name . " = " . json::encode($args) . ";\n", false);
}
/**
* Добавляет скипт к текущему шаблону
*
* @param string $name путь к скрипту
*/
public function addScript($name)
{
$this->_script [] = $name;
}
/**
* Добавляет код скипта к текущему шаблону
*
* @param string $name строка javascript кода
*/
public function addScriptRaw($name, $startup = false)
{
if ($startup) {
$this->_startup [] = $name;
} else {
$this->_scriptstring [] = $name;
}
}
/**
* Добавляет стили к текущему шаблону
*
* @param string $name путь к стилю
*/
public function addStyleSheet($name)
{
$this->_stylesheet [] = $name;
}
/**
* Рекурсивно извлекает из значение свойства обьекта
*
* @param string $list Имя свойства
* @param boolean $flatten
*/
private function doTree($list, $flatten = true) {
$result = ($flatten == true) ? $this->$list : array($this->$list);
foreach ($this->_section as $key => $value) {
if (is_object($value)) $result = array_merge($value->doTree($list, $flatten), $result);
}
return $result;
}
/**
* Массив имен файлов скриптов
*
* return array
*/
public function getScripts()
{
return $this->doTree('_script');
}
function resolveAlias($alias, $list)
{
$result = array();
foreach($list as $item) {
$result [] = strtr($item, $alias);
}
return $result;
}
/**
* Строка со скриптом
*
* @return string
*/
public function getScriptRaw()
{
return implode("\n", $this->doTree('_scriptstring'));
}
public function getScriptStartup()
{
return implode("\n", $this->doTree('_startup'));
}
/*abstract*/ public function set($key, $value)
{
}
/**
* Массив имен файлов стилей
*
* return array
*/
public function getStyleSheet()
{
return $this->doTree('_stylesheet');
}
/**
* Обработка всех вложенных шаблонов
*
* @return string
*/
public function execute()
{
foreach ($this->_section as $key => $value) {
$this->set($key, (is_object($value)) ? $value->execute() : $value); // ?
}
}
/**
* Установка заголовка шаблона
*
* @param string $title
*/
public function setTitle($title)
{
$this->_title = $title;
}
private function isNotNull($title)
{
return $title !== null;
}
/**
* Общая строка заголовка
*/
public function getTitle()
{
return implode(" - ", array_filter($this->doTree('_title', false), array($this, 'isNotNull')));
}
private function findGroup($groups, $file)
{
foreach($groups as $key => $group) {
if(in_array($file, $group)) {
return $key;
}
}
return false;
}
private function groupFiles(array $list, $debug)
{
$debug = ($debug) ? 'debug=1' : '';
$path = parse_url(WWW_PATH, PHP_URL_PATH);
$list = array_reverse($list);
// Группы нужно передвавать как параметр !!!
$groups = array(
'table' => array($path . '/js/table.js', $path . '/js/listtable.js',
$path . '/js/page.js', $path . '/js/pagemenu.js'),
'base' => array($path . '/js/admin.js', $path . '/js/cookie.js'),
);
$use = array();
$result = array();
foreach ($list as $file) {
$name = $this->findGroup($groups, $file);
if($name) {
$use [$name] = 1;
} else {
$result [] = $file;
}
}
$list = array();
foreach ($use as $name => $value) {
$list [] = WWW_PATH . "/min/?$debug&f=" . implode(",", $groups[$name]);
}
return array_merge($list, $result);
}
/**
* Обработка шаблона
*
* @return string
*/
public function render()
{
$alias = $this->doTree('alias');
// require_once 'minify.php';
// Скрипты и стили
$this->set('scripts', array_unique($this->groupFiles($this->resolveAlias($alias, $this->getScripts()), false)));
$this->set('stylesheet', array_unique($this->groupFiles($this->resolveAlias($alias, $this->getStyleSheet()), false)));
$this->set('scriptstring', $this->getScriptRaw());
$this->set('startup', $this->getScriptStartup());
$this->set('title', $this->getTitle());
//
return $this->execute(); // execute+phptal ??
}
function setAlias($alias)
{
$this->alias = $alias;
}
function addAlias($name, $path)
{
$this->alias['${' . $name . '}'] = $path;
$this->set($name, $path);
}
function loadImports($importFile)
{
// Подключение стилей и скриптов
if (file_exists($importFile)) {
$import = file_get_contents($importFile);
$files = explode("\n", $import);
foreach ($files as $file) {
if (strpos($file, ".js") !== false) {
$this->addScript(strtr(trim($file), $this->alias));
} else if(strpos($file, ".css") !== false) {
$this->addStyleSheet(strtr(trim($file), $this->alias));
}
}
}
}
}
// CompositeView+PHPTAL
class View_Composite extends _View_Composite
{
private $tal;
function __construct($file)
{
parent::__construct($file);
$this->tal = new PHPTAL($file);
$this->tal->setEncoding('WINDOWS-1251'); // PHP_TAL_DEFAULT_ENCODING !!
$this->tal->stripComments(true);
}
function set($key, $val)
{
if ($key == 'title') {
$this->setTitle($val);
}
$this->tal->set($key, $val);
}
function __set($key, $val)
{
$this->tal->set($key, $val);
}
function setTranslator($tr)
{
$this->tal->setTranslator($tr);
}
function execute()
{
parent::execute();
// postProcess
return $this->tal->execute();
}
}

40
src/view/view.php Normal file
View file

@ -0,0 +1,40 @@
<?php
// Класс отображения
// CompositeView !! Composite pattern
class View
{
protected $document;
protected $values;
public function __construct ($document)
{
$this->document = $document;
}
public function set($key, $value)
{
$this->values[$key] = $value;
}
public function __set($key, $value)
{
$this->set($key, $value);
}
public function execute()
{
$result = $this->values;
return self::getTemplateContent ($this->document, $result);
}
static function getTemplateContent($document, $result)
{
ob_start ();
include ($document);
$result = ob_get_contents ();
ob_clean ();
return $result;
}
}

34
src/zipfile.php Normal file
View file

@ -0,0 +1,34 @@
<?php
/**
* Расширение класса ZipArchive с возможность архивирования директории
*/
class ZipFile extends ZipArchive
{
private function addDirDo($location, $name)
{
assert(is_string($location) && is_string($name));
$name .= '/';
$location .= '/';
// Read all Files in Dir
$dir = opendir($location);
while (($file = readdir($dir)) !== false)
{
if ($file === '.' || $file === '..') continue;
// Rekursiv, If dir: FlxZipArchive::addDir(), else ::File();
$call = (is_dir($file)) ? 'addDir' : 'addFile';
call_user_func(array($this, $call), $location . $file, $name . $file);
}
}
public function addDir($location, $name)
{
assert(is_string($location) && is_string($name));
$this->addEmptyDir($name);
$this->addDirDo($location, $name);
}
}